Java 中可以使用什么日期时间格式来处理夏令时

What Date time format can be used to handle DST in Java

我们的客户在文本文件中以以下格式的字符串向我们发送开始和结束日期时间

2019-10-0711:07上午

所有日期时间都在一个时区。我们计算开始和结束日期时间之间的差异来计算工作时间。当夏令时转换发生时,工作时间计算出错。他们发送的信息不足,我们无法正确计算。

我将建议他们向我们发送更多信息,以便我们解决此问题。这里有什么好的解决方案?我们应该向他们推荐哪种日期时间格式,以帮助我们应对 DST 更改并正确计算工作时间。

我们使用Java.

这是一个简单的任务。 DateTimeFormatter class gives you all the info you need. 2019-10-07 11:07 AM Your format would be 'yyyy-MM-dd hh:mm a' and you should use LocalDateTime class. But since you need to take into account daylight savings time then you might want to use classes ZonedDateTime or OffsetDateTime and provide your timezone. It might be an overkill, but I once worked on the project where I needed to parse Strings to Dates without knowing the format in advance. So, here is the article I wrote on how to do that: Java 8 java.time package: parsing any string to date

做对并不明显

他们告诉你他们的当地时间,你可以推断出时区(因为“所有日期都在一个时区”)。

基本计算如下:

ZoneId pacific = ZoneId.of("America/Los_Angeles");
DateTimeFormatter local = DateTimeFormatter.ofPattern("uuuu-MM-dd hh:mm a").withZone(pacific);
ZonedDateTime start = ZonedDateTime.parse("2022-11-06 01:30 AM", local);
ZonedDateTime until = ZonedDateTime.parse("2022-11-07 01:30 AM", local);
long hours = start.until(until, ChronoUnit.HOURS);
System.out.printf("%d hours elapsed%n", hours);

这会打印“25 小时 过去了。”在太平洋时区,2022 年 11 月 6 日是 25 小时,因为秋季夏令时结束时,时钟会拨慢一小时。如果有人告诉你现在是 1:00 上午,你不知道午夜是一小时前还是两小时前。

默认偏移启发式

您真正需要的是偏移量,为此您必须依靠一些启发式方法。默认情况下,ZonedDateTime 从多个模糊局部 date-times by selecting the earliest offset(“夏季”偏移量)中选择一个瞬间。

指定偏移量

如果这不是您想要的,您可以显式覆盖偏移量。例如,也许你处理这些接近real-time的时间戳,你可以根据当前时间猜出偏移量应该是多少。或者您可能知道这些本地时间戳总是按时间顺序处理;通过跟踪您看到的最晚时间,并注意后面是否有更早的时间戳,您可以检测到时钟拨回并更改偏移量。

ZonedDateTime.ofLocal() and ZonedDateTime.ofStrict() 函数可用于显式控制偏移量。

偏移日期时间

或者,您可以要求他们在时间戳字符串中包含偏移量。通常这会用带符号的小时数和分钟数表示:“-07:00”或“-0800”。这将在 DST 转换期间提供明确的时间解释。


这里是一个使用 OffsetDateTime 的例子。首先,如果偏移量使用冒号,如“2019-10-07T11:07:00+01:00”,它是标准格式,可以这样解析:

OffsetDateTime start = OffsetDateTime.parse("2019-10-07T11:07:00+01:00");

如果缺少冒号,您需要格式化程序来处理 non-standard 输入:

DateTimeFormatter odt = new DateTimeFormatterBuilder()
    .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
    .appendOffsetId()
    .toFormatter();
OffsetDateTime when = OffsetDateTime.parse("2019-10-07T11:07:00+01:00", odt);

从那里开始,计算与 ZonedDateTime 相同:

OffsetDateTime start = OffsetDateTime.parse("2022-11-06T01:00:00-07:00", odt);
OffsetDateTime until = OffsetDateTime.parse("2022-11-07T01:54:00-08:00", odt);
long hours = start.until(until, ChronoUnit.HOURS);
System.out.printf("%d complete hours elapsed.%n", hours);
Duration duration = Duration.between(start, until);
System.out.println("Full duration: " + duration);