【问题标题】:Java 8 localdatetime vs DateJava 8 本地日期时间与日期
【发布时间】:2018-10-28 01:55:24
【问题描述】:

我有一个字符串 2017-07-31T01:01:00-07:00,我正在尝试将其解析为日期和 CST 时区。当我使用 Date 和 Java 8 ZonedDateTime 解析这个字符串时,我得到了不同的结果。我不明白为什么会发生这种情况以及我做错了什么。

    String dateStr = "2017-07-31T01:01:00-07:00";
    LocalDateTime time = null;
    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss-hh");

    String[] dateArray = dateStr.split("-");
    String[] timeZones = TimeZone
            .getAvailableIDs(TimeZone.getTimeZone("GMT-" + dateArray[dateArray.length - 1]).getRawOffset());
    format.setTimeZone(TimeZone.getTimeZone(timeZones[0]));
    Date dateObj = null;
    try {
        dateObj = format.parse(dateStr);
    } catch (ParseException e) {
        e.printStackTrace();
    }
    time = dateObj.toInstant().atZone(TimeZone.getTimeZone("CST").toZoneId()).toLocalDateTime();
    ZonedDateTime time2 = ZonedDateTime.parse(dateStr).toInstant().atZone(TimeZone.getTimeZone("CST").toZoneId());
    System.out.println(time);
    System.out.println(time2.toLocalDateTime());

【问题讨论】:

  • 为什么要手动提取时区-限制为0..9。这将在世界上很多地方遇到问题。
  • “我得到了不同的结果” 向我们展示。不要指望我们运行您的代码来查看它。编辑问题并显示您的结果。
  • 当你可以使用java.timeLocalDateTimeZonedDateTime所属的现代Java日期和时间API时,你为什么要担心像SimpleDateFormat和@这样过时的类987654327@ 呢?我的建议是忘记它们。
  • CST 是指中国标准时间还是古巴标准时间?那些三个和四个字母的时区缩写是如此的模棱两可。

标签: java datetime java-8


【解决方案1】:

我不确定TimeZone.getAvailableIDs 的确切行为(它的文档记录不完整)。当我在我的 Java 9.0.4 上运行您的代码时,它返回的第一个 ID 是 America/Boise。美国爱达荷州博伊西的冬季采用山区标准时间 (UTC-07:00),现在采用山区夏令时间 (UTC-06:00)。您将 format 设置为使用它进行解析。您的格式将 01 解析为一天中的小时(大写 HH,00 到 23) 07 解析为上午或下午中的小时(小写 hh,01 到 12)。所以这里有冲突。显然是前者赢了,我不知道为什么(不要总是坚持理解SimpleDateFormat)。由于 7 月 31 日是一年中的夏令时 (DST),因此您获得的时间为 01:01-06:00,等于北美中部夏令时间 02:01 (-05:00)。这是不正确的。

ZonedDateTime.parse 正确解析您的字符串。 TimeZone.getTimeZone("CST").toZoneId() 将 CST 解释为 America/Chicago(严格来说这是不正确的,因为芝加哥仅在一年中的一小部分时间使用 CST)。所以在time2 你得到2017-07-31T03:01-05:00[America/Chicago],这是正确的。

对于我认为你想要获得的东西,我建议:

    ZonedDateTime dateTime = OffsetDateTime.parse(dateStr)
            .atZoneSameInstant(ZoneId.of("America/Chicago"));
    System.out.println(dateTime.toLocalDateTime());

输出与您的第二个输出相同:

2017-07-31T03:01

如果您发现它更适合您的情况,您可以考虑使用 America/Bahia_Banderas、America/Indiana/Knox、America/Indiana/Tell_City、America/Matamoros、America/Menominee 或 America/Winnipeg 代替 America/Chicago .不要依赖三个或四个字母的时区缩写。 CST 不是真正的时区,因为它只用于一年中的某些时间,而且它是模棱两可的,它可能指的是澳大利亚中部标准时间、北美和中美洲中部标准时间、中国标准时间或古巴标准时间。并且无法保证给您提供哪一种 Java。

【讨论】:

    【解决方案2】:

    您不应该自己解析时区偏移量。只需使用X 模式:

    new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX")
    

    您不应使用时区CST,因为它不明确(中部标准时间、中国标准时间、古巴标准时间)。使用America/Chicago(我想这就是你的意思)。

    因此,使用新旧 API 解析日期字符串:

    String dateStr = "2017-07-31T01:01:00-07:00";
    
    // Using Date
    SimpleDateFormat parseFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
    Date date = parseFormat.parse(dateStr);
    
    SimpleDateFormat printFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm");
    printFormat.setTimeZone(TimeZone.getTimeZone("America/Chicago"));
    System.out.println(printFormat.format(date));
    
    // Using ZonedDateTime
    ZonedDateTime zonedDateTime = ZonedDateTime.parse(dateStr);
    zonedDateTime = zonedDateTime.withZoneSameInstant(ZoneId.of("America/Chicago"));
    System.out.println(zonedDateTime.toLocalDateTime());
    

    输出

    2017-07-31T03:01
    2017-07-31T03:01
    

    如果你想查看时区,你可以这样做:

    String dateStr = "2017-07-31T01:01:00-07:00";
    
    // Using Date
    SimpleDateFormat parseFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
    Date date = parseFormat.parse(dateStr);
    
    SimpleDateFormat printFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm XXX z");
    printFormat.setTimeZone(TimeZone.getTimeZone("America/Chicago"));
    System.out.println(printFormat.format(date));
    
    // Using ZonedDateTime
    ZonedDateTime zonedDateTime = ZonedDateTime.parse(dateStr);
    
    DateTimeFormatter printFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm XXX z")
                                                        .withZone(ZoneId.of("America/Chicago"));
    System.out.println(zonedDateTime.format(printFormatter));
    

    输出

    2017-07-31T03:01 -05:00 CDT
    2017-07-31T03:01 -05:00 CDT
    

    注意第一个示例如何更改 ZonedDateTime 时区,将其转换为 LocalDateTime,然后在没有格式化程序的情况下打印,而在第二个示例中,DateTimeFormatter 设置为在特定时区格式化值,类似于SimpleDateFormat 的做法。只是实现相同结果的不同方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-03-09
      • 1970-01-01
      • 1970-01-01
      • 2022-06-16
      • 2014-05-31
      • 2021-01-02
      • 2010-10-22
      • 1970-01-01
      相关资源
      最近更新 更多