【问题标题】:DateTimeFormatter not compatible with SimpleDateFormatDateTimeFormatter 与 SimpleDateFormat 不兼容
【发布时间】:2016-04-19 15:27:56
【问题描述】:
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

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

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

【问题讨论】:

标签: java java-8 java-time


【解决方案1】:

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

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

由于您正在格式化不支持访问ChronoField.INSTANT_SECONDSLocalDateTime,因此您将获得“通用非位置”格式。要获得所需的输出,请改用ZonedDateTimeOffsetDateTime。 (需要瞬间判断是夏季时间还是冬季时间。)

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

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

【讨论】:

  • 谢谢。我会根据您的回答仔细研究。
  • 如果您仔细研究LDML-specification,您会发现模式符号“z”旨在代表特定的非位置格式,而不是通用变体。 LDML 相反需要符号“v”来表示通用的非位置格式。显然,没有包括 Java-8 在内的库支持“v”。如果 Java-8 应该紧跟 LDML,那么“z”不应该支持泛型变体。因此,最好拒绝像 LocalDateTime 这样的局部类型与模式符号“z”的组合。
  • 当然,'z' 确实只是“特定”格式。但是 LDML 是基于仅使用 java.util.Date/Instant/ZonedDateTime 类型对象的概念编写的。在 JSR-310 中,我们有更多 LDML 没有考虑的类型,例如LocalDateTime。 JSR-310 也没有声称与 LDML 完全匹配 - 它被用作指南。因此,将规范概括为在数据不足时使用“通用”形式是完全合适的。
  • 我完全同意您对 LDML-Intention 的分析,以专注于类似即时的类型,但您的结论似乎不是唯一可能的方法。即使在本地类型的上下文中(如LocalDateTime),如果用户仅将“z”理解为“特定非位置格式”,恕我直言,用户会更清楚。为什么不支持符号“v”(= 与 LDML 重新同步)而不是混淆“z”的行为(概括也就是改变 LMDL 行为)?至少记录你的推理就可以了。顺便说一句,我还没有看到任何关于“通用非位置格式”的 API 文档。
【解决方案2】:

您不应该在LocalDateTime 上进行操作。 Date 的(几乎)等价物是 Instant。您需要格式化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 而无需为您的DateTimeFormatter 设置ZoneId

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 没有偏移量或时区。您的格式化程序会覆盖它,但偏移量不是夏令时。

【讨论】:

  • 你说得很好,java.util.Date 主要映射到java.time.Instant。因此,如果 OP 抱怨两种格式引擎之间的不兼容,他至少应该关心为java.util.Date 使用正确的等效类型。否则,这种不兼容声明是不公平的(在这种情况下 - 撇开其他细节存在不兼容的事实 - 例如不同的模式符号)。
【解决方案3】:

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

    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));
    }

【讨论】:

  • 那么当DST发生时,可怜的开发者会在半夜被召唤并更改苍蝇的代码吗?
  • 完善的答案,以便将 DST 考虑在内。
  • @Tunaki 否,Calendar.getInstance() 使用考虑 DST 的默认时区。所以这个答案无疑是非常尴尬和有限的(对我来说不是直截了当的),但也不是完全错误的(尽管只在纽约地区和英语地区工作)。比方说,这还不够。
猜你喜欢
  • 2022-01-20
  • 1970-01-01
  • 1970-01-01
  • 2021-11-15
  • 2018-07-04
  • 2019-09-30
  • 1970-01-01
  • 2020-10-27
  • 2013-03-14
相关资源
最近更新 更多