正确的时区名称
诸如“太平洋夏令时间”之类的名称不是真正的时区。它们的 3-4 个字母代码既不标准化也不唯一。帮自己一个忙,忘掉他们。切勿在日期时间工作中使用“PDT”、“EST”等。
对于日期时间工作,学习使用proper time zone names。这些格式为大陆、斜线和城市或地区。这些都不是任意的或方便的,每个都代表了该时区的调整或定义的变化或异常的历史。
所以地理上的接近是无关紧要的。例如,西雅图离洛杉矶比凤凰城远得多。但是西雅图的官方时区是America/Los_Angeles,而亚利桑那州使用America/Phoenix。
虽然美国西海岸的大部分地区都使用America/Los_Angeles,但并非全部都使用。正如我们所说,亚利桑那州没有,Metlakatla Alaska 也没有。但请注意两者是如何分别与 America/Los_Angeles 在 DST 和标准时移中使用的 -07:00 和 -08:00 偏移量重叠的。 (见下文)
因此,您必须做好功课,了解任何特定位置的官方时区。如果您不了解业务逻辑,则必须询问用户。
使用框架,卢克
您不必担心夏令时的开始和结束时间。为日期时间工作使用一个体面的框架。让框架处理有关 DST 的细节。如果您感兴趣的区域有不断变化的规则,您唯一需要担心的应该是在您的框架中保持 tz 数据库的最新状态。
java.time
对于 Java 8 及更高版本,选择的框架是新的内置 java.time 框架。 (Tutorial) 受Joda-Time 启发,由JSR 310 定义,由ThreeTen-Extra 项目扩展。
ZoneId zoneId_Montréal = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt_Now_Montréal = ZonedDateTime.now( zoneId_Montréal );
String outputNowMontréal = zdt_Now_Montréal.toString( );
outputNowMontréal:2015-09-04T01:39:46.962-04:00[美国/蒙特利尔]
调整夏令时
Daylight Saving Time (DST) 是日期时间工作中最常见的异常。当提前一小时或一小时后跳时,这意味着某些时间值根本不存在或一天出现两次。正如ZonedDateTime.of 类文档所说:
时区规则,例如夏令时,意味着并非每个本地日期时间都对指定区域有效,因此可以调整本地日期时间。
该方法解释了 java.time 在进行必要调整时使用的规则。请务必研究它们以了解其行为。
文档还说,我们通常应该使用 LocalDateTime 类从数字创建日期时间,而不是使用 ZonedDateTime.of 方法。然后通过使用不同的ZonedDateTime.of 静态方法应用ZoneId 以获取ZonedDateTime。
LocalDateTime localDateTime = LocalDateTime.of( 2015, 9, 29, 10, 0, 0 );
ZoneId zoneId_LosAngeles = ZoneId.of("America/Los_Angeles");
ZonedDateTime zdt_LosAngeles = ZonedDateTime.of( localDateTime, zoneId_LosAngeles ) ;
zdt_LosAngeles : 2015-09-29T10:00-07:00[America/Los_Angeles]
我们可以代表时间轴中的某个时刻,就像在其他两个时区看到的那样。 America/Los_Angeles 的标准和 DST 偏移量分别为 -08:00 和 -07:00,America/Phoenix(亚利桑那州)只有 -07:00,America/Metlakatla(阿拉斯加)只有 -08:00。
ZonedDateTime zdt_Phoenix = zdt_LosAngeles.withZoneSameInstant( ZoneId.of( "America/Phoenix" ) );
ZonedDateTime zdt_Metlakatla = zdt_LosAngeles.withZoneSameInstant( ZoneId.of("America/Metlakatla") );
zdt_Phoenix : 2015-09-29T10:00-07:00[美国/凤凰城]
zdt_Metlakatla : 2015-09-29T09:00-08:00[美国/Metlakatla]
课堂文档中解释了在“Spring Ahead”和“Fall Back”日子调整 DST 的规则。
本地日期时间被解析为时间线上的一个瞬间。这是通过查找区域 ID 规则定义的本地日期时间与 UTC/格林威治的有效偏移量来实现的。
在大多数情况下,本地日期时间只有一个有效偏移量。在重叠的情况下,当时钟被调回时,有两个有效的偏移量。此方法使用通常对应于“summer”的较早偏移量。
在间隙的情况下,当时钟向前跳时,没有有效的偏移量。相反,本地日期时间会根据间隙的长度进行调整。对于典型的一小时夏令时更改,本地日期时间将在一小时后移动到通常对应于“夏季”的偏移量中。
乔达时间
如果 Java 8 技术不可用,请使用第三方 Joda-Time 框架(它启发了 java.time)。顺便说一下,Joda-Time 在 Android 中工作,其他人提供的一些特殊版本有助于克服 Dalvik 类加载器的一些问题。
避免使用旧的 j.u.Date/Calendar
避免使用旧的 java.util.Date/.Calendar 和 java.text.SimpleDateFormat 类。这些类是业界首次尝试以复杂的方式处理日期时间。但最终他们失败了,被证明是令人困惑、麻烦和有缺陷的。
ISO 8601
java.time 和 Joda-Time 都提供格式化程序,如果您必须将它们用于输出,它们将使用这些 3-4 个字母代码生成日期时间值的字符串表示形式。但不要将它们用于数据存储、数据交换或解析。
为此,了解ISO 8601 标准字符串格式。 java.time 和 Joda-Time 在解析和生成字符串表示时默认使用 ISO 8601。