DateTimeFormatter与SimpleDateFormat不兼容

时间:2016-04-19 15:27:56

标签: java java-8 java-time

import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;


public class Test001 {

    public static void main(String[] args) {
        Date dt = new Date();
        LocalDateTime localDateTime = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS zzz").withZone(ZoneId.of("America/New_York"));
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS zzz");

        System.out.println(formatter.format(localDateTime));
        System.out.println(sdf.format(dt));
    }

}

输出

2016-04-19 11:25:34.917 ET
2016-04-19 11:25:34.637 EDT

我的笔记本电脑的时区是" America / New York" (即美国东部时间/加拿大)。

我想知道如何获得" EDT"而不是" ET"使用DateTimeFormatter
我也想知道:为什么DateTimeFormatter在解析/格式模式方面与SimpleDataFormat不兼容(因为这个例子显示它们不兼容)。

3 个答案:

答案 0 :(得分:9)

您看到的格式“ET”在Unicode Locale Data Markup Language (LDML)中称为“通用非位置”格式。您想要的格式“EDT”被称为“特定的非位置”格式。

DateTimeFormatterBuilder中的相关源代码检查日期时间对象是否可以提供ChronoField.INSTANT_SECONDS。如果可以,则使用“特定非位置”格式,否则使用“通用非位置”格式。

由于您要格式化不支持ChronoField.INSTANT_SECONDS访问权限的LocalDateTime,因此您将获得“通用非位置”格式。要获得所需的输出,请改用ZonedDateTimeOffsetDateTime。 (需要立即确定是夏季还是冬季。)

请注意SimpleDateFormatDateTimeFormatter确实不同,假设模式相同是不正确的。决定将DateTimeFormatter与LDML规范重新同步,这将在未来带来好处。

这个答案提供了解决方案和解释,但是我必须注意JDK代码在这里过于苛刻。由于您同时提供LocalDateTimeZoneId,因此代码应该能够做得更好,并在运行中确定ChronoField.INSTANT_SECONDS,从而使用“特定的非位置”格式。因此,我认为这里有一个边缘情况JDK issue

答案 1 :(得分:5)

你不应该在LocalDateTime上操作。 (几乎)等同于DateInstant。您希望格式化Instant,就像它在您的时区中一样。所以

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS zzz").withZone(ZoneId.of("America/New_York"));
Instant now = Instant.now();
System.out.println(formatter.format(now));

打印

2016-04-19 12:07:57.684 EDT

或者,您可以使用ZonedDateTime而无需为ZoneId

设置DateTimeFormatter
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS zzz");

ZonedDateTime now = ZonedDateTime.now(ZoneId.of("America/New_York")))
System.out.println(formatter.format(now));

也打印

2016-04-19 12:11:01.667 EDT

我没有关于为什么这一切都有效的文档,但要点是这些时间(你正在尝试格式化的日期对象)有偏移,它们有区域规则,可以是日光是否节省时间。格式化程序在其打印中使用它。

LocalDateTime没有偏移量或时区。您的格式化程序会覆盖它,但偏移量不是夏令时。

答案 2 :(得分:-2)

最直接的方法(也可能导致夏季/冬季时间的一些问题如下:

    public static void main(String[] args) {
        Date dt = new Date();
        LocalDateTime localDateTime = LocalDateTime.now();

        String dateFormat;
        if (Calendar.getInstance().get(Calendar.DST_OFFSET) != 0) {
            dateFormat = "yyyy-MM-dd HH:mm:ss.SSS 'EDT'";
        } else {
            dateFormat = "yyyy-MM-dd HH:mm:ss.SSS 'EST'";
        }
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(dateFormat).withZone(ZoneId.of("America/New_York"));
        SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);

        System.out.println(formatter.format(localDateTime));
        System.out.println(sdf.format(dt));
    }