Java 8 LocalDateTime与Date的比较

3

我有一个字符串 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?这将在世界上许多地方遇到问题。 - Robert
2
"我得到了不同的结果。" 展示给我们看,不要期望我们运行你的代码来查看它。编辑问题并展示你的结果。 - Andreas
1
当你可以使用java.time,现代Java日期和时间API,其中包括LocalDateTimeZonedDateTime时,为什么还要担心过时的类,如SimpleDateFormatDate呢?我的建议是忘记它们。 - Ole V.V.
1
你所说的CST是指中国标准时间还是古巴标准时间?这些三四个字母的时区缩写真是太模糊了。 - Ole V.V.
2个回答

5

您不应该自己解析时区偏移量。只需使用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的做法。只是实现相同结果的不同方式。

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日处于夏令时期间,您得到了一个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月31日03:01

如果您觉得美国/芝加哥的时区不太适合您的情况,您可以考虑例如美国/巴伊亚班德拉斯、美国/印第安纳/诺克斯、美国/印第安纳/特尔城、美国/马塔莫罗斯、美国/梅诺米尼或美国/温尼伯等时区。不要依赖三个或四个字母的时区缩写。CST不是真正的时区,因为它只在一些时候使用,并且它是含糊的,它可能指澳大利亚中部标准时间,北美和中美洲中部标准时间,中国标准时间或古巴标准时间。而且不能保证Java会给你哪一个。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接