【问题标题】:Standard Time and Daylight Time标准时间和夏令时
【发布时间】:2015-11-29 18:03:52
【问题描述】:

这不是一个真正的技术编程问题,但它与在 Java 中处理和显示时区有关。

在美国,有 Eastern TimePacific Time 等时区,在夏令时期间,它们是 Eastern Daylight TimePacific Daylight Time,而在夏令时无效时,它们是 Eastern Standard TimePacific Standard Time

那么,既然夏令时从 3 月的第二个星期日开始,到 11 月的第一个星期日结束,那么说 3 月和 11 月之间的日期在 Eastern Standard TimePacific Standard Time 是无效的吗?例如,以下日期在技术上不存在吗?

太平洋标准时间 2015 年 9 月 29 日星期二上午 10 点

不应该是Pacific Daylight Time (PDT),还是只是PT,以避免必须指定DaylightStandard

【问题讨论】:

  • 我认为这完全取决于应用程序。用户需要为您的应用程序看到什么?只为他们显示时区就足够了,还是他们需要更多信息?
  • 我不了解太平洋,但亚利桑那州的大部分地区全年都遵守山区标准时间(没有 DST),所以是的,您指定的日期可以存在。
  • @VGR - 阿拉斯加梅特拉卡特拉全年都有太平洋标准时间。 :) 见timeanddate.com/time/zones/pst

标签: java time timezone


【解决方案1】:

在Java 中使用SimpleDateFormat,指定任何一个似乎都是有效的。打印后,您可以看到夏令时转换在解析时发生,即使在调用 setLenient(false) 时也是如此。

例子:

SimpleDateFormat sdf = new SimpleDateFormat("yyyy MMMM dd HH:mm:ss z");
sdf.setLenient(false);
Date d = sdf.parse("2015 September 29 10:00:00 PST");
System.out.println(d);

这个输出:

Tue Sep 29 11:00:00 PDT 2015

它需要 PST(太平洋标准时间)并在输出时将其转换为 PDT(太平洋夏令时间)。至少现在是这样。在撰写本文时,夏令时生效。

也接受 PDT:

Date d2 = sdf.parse("2015 September 29 10:00:00 PDT");
System.out.println(d2);

这个输出:

Tue Sep 29 10:00:00 PDT 2015

注意:这可能取决于是否拥有美国语言环境。由于这是我的默认语言环境,因此上述方法有效。如果这不是您的语言环境,请尝试在顶部的 SimpleDateFormat 构造函数调用中添加第二个参数 Locale.US

【讨论】:

  • 您描述的行为仅有效,因为有少数 ID 被识别为向后兼容。通常,您不能依赖时区缩写的解析。请参阅the JavaDoc 顶部附近标题为“三个字母的时区 ID”的部分。解析时,识别的缩写是来自 RFC 822 的缩写。其他的将不起作用。
【解决方案2】:

EDT 中的日期/时间说EST,例如7 月 4 日在纽约,无效,但我们人类经常不在乎并松散地使用EST。这是错误的,但我们还是这样做了。

在计算方面,我们力求准确,所以绝对无效

但 VGR 是对的。如果您所在的地区不遵守夏令时,即使 7 月 4 日也可能是“标准”时间。

【讨论】:

    【解决方案3】:

    ...会说 3 月到 11 月之间的日期是东部标准时间或太平洋标准时间无效吗?

    如果您谈论的时区在特定时间没有观察到该特定片段,那么是的 - 它是无效的。东部标准时间始终为 UTC-5,东部夏令时间始终为 UTC-4。如果您在指 UTC-4 时说 EST,那么您就用错了。 (而且人们确实经常错误地使用它。)

    但是,正如其他人所指出的那样,许多时区的某些位置不遵守夏令时,即使它们周围的地区这样做也是如此。如果你在夏天说 MST,它在凤凰城有效,但在洛杉矶无效。

    ...或者只是简单地使用 PT 来避免指定 Daylight 或 Standard?

    是的,您可以只说 PT 并省略 SD。在电视上经常以这种方式完成,但在计算机上却不那么常见。此外,这不适用于世界各地的时区(伦敦使用 GMT 和 BST - 那里没有通用的通用缩写)。

    【讨论】:

    • 因此,如果我只是说太平洋时间 9 月 29 日星期二上午 10 点,那么从观察夏令时的地区与不遵守夏令时的地区的某人的立场如何看待?
    • 他们会根据自己的理解来解读——这可能会导致两个不同的观众之间的时间不同。但总的来说,PT 的意思是“PST 或 PDT,以有效者为准”。还要考虑在回退过渡期间会有歧义,此时任何一个都可能在 1:00 到 1:59 生效。
    • 上下文就是一切。还要考虑一下,如果你对全世界的观众说“CT”或“CST”,中国人的理解与美国人的理解会大不相同。
    • 嗯,所以 9 月 29 日上午 10 点在阿拉斯加州梅特拉卡特拉市实际上指的是与太平洋时间 9 月 29 日上午 10 点在一个观察夏令时的地区不同的时间?有趣...
    • 只有当阅读它的人以这种方式解释它时。不要指望计算机会做出这样的推断。另请注意,Metlakatla 很小,因此那里的当地人很可能会理解大陆 PT 与他们的不同。然而,在亚利桑那州长大,我亲身知道那里的人只会说“山地时间”,而当他们在 MST 时,经常忽略 MT 的其余部分在 MDT 中。凤凰城和丹佛两个人的讨论,人们可能会说“你的时间”或“我的时间”,但在夏天他们对“山地时间”的理解确实不同.
    【解决方案4】:

    正确的时区名称

    诸如“太平洋夏令时间”之类的名称不是真正的时区。它们的 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。

    【讨论】:

    • 感谢您非常详细的回答。我正在使用带有ZonedDateTimes 和DateTimeFormatter 的java.time。我使用Instants 并使用Duration.ofXXX() 方法添加范围,然后使用.atZone(ZoneId.of("time zone name")) 转换为ZonedDateTimes。不过,在显示时间时必须使用两/三字母缩写,因为我们的大多数典型最终用户不会理解 ISO 8601 格式。
    • 正如我所说,java.time 提供了以字符串表示形式生成那些假时区的格式化程序。如果必须向用户展示输出,请使用它们。但是,作为程序员,请不要将它们放在脑海中。不要指望解析它们; java.time 和 Joda-Time 都因为模棱两可而拒绝。
    猜你喜欢
    • 2012-06-20
    • 2014-12-14
    • 2021-01-16
    • 2016-05-20
    • 2016-02-06
    • 2017-07-11
    • 2015-08-09
    • 2012-04-07
    • 2020-05-22
    相关资源
    最近更新 更多