DateTimeFormatter与SimpleDateFormat不兼容

pet*_*rov 10 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));
    }

}
Run Code Online (Sandbox Code Playgroud)

产量

2016-04-19 11:25:34.917 ET
2016-04-19 11:25:34.637 EDT
Run Code Online (Sandbox Code Playgroud)

我的笔记本电脑的时区是"美国/纽约"(即东部时间美国/加拿大).

我想知道如何使用"EDT"代替"ET" DateTimeFormatter.
我也想知道:为什么在解析/格式模式方面DateTimeFormatter不兼容SimpleDataFormat(因为这个例子表明它们不兼容).

Jod*_*hen 10

您看到的格式"ET"在Unicode区域设置数据标记语言(LDML)中称为"通用非位置"格式.您想要的格式"EDT"称为"特定非位置"格式.

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

由于您正在格式化a LocalDateTime,它不支持访问ChronoField.INSTANT_SECONDS,因此您将获得"通用非位置"格式.要获得所需的输出,请使用ZonedDateTimeOffsetDateTime替代.(需要立即确定是夏季还是冬季.)

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

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


Sav*_*ior 6

你不应该操作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));
Run Code Online (Sandbox Code Playgroud)

打印

2016-04-19 12:07:57.684 EDT
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用a 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));
Run Code Online (Sandbox Code Playgroud)

还打印

2016-04-19 12:11:01.667 EDT
Run Code Online (Sandbox Code Playgroud)

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

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


uni*_*now -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));
    }
Run Code Online (Sandbox Code Playgroud)