【问题标题】:SimpleDateFormat ignoring TimeZone when also present in String当 String 中也存在 SimpleDateFormat 时忽略 TimeZone
【发布时间】:2019-06-10 13:23:32
【问题描述】:

Java 的SimpleDateFormat 允许您在将字符串解析为Date 时指定要使用的TimeZone。

当字符串不包含时区时,这可以正常工作,但是当存在时区时,它似乎什么都不做。

文档似乎也没有真正解释 TimeZone 的使用方式。

示例代码:

public class DateFormatTest {
    public static void main(final String[] args) throws ParseException {
         testBoth("HH:mm", "13:40");
         testBoth("HH:mm z", "13:40 UTC");
    }

    private static void testBoth(final String dateFormatString, final String dateString) throws ParseException {
        // First, work with the "raw" date format
        DateFormat dateFormat = new SimpleDateFormat(dateFormatString);
        parse(dateFormat, dateString);

        // Now, set the timezone to something else and try again
        dateFormat = new SimpleDateFormat(dateFormatString);
        dateFormat.setTimeZone(TimeZone.getTimeZone("PST"));
        parse(dateFormat, dateString);
    }

    private static void parse(final DateFormat dateFormat, final String dateString) throws ParseException {
        System.out.println(MessageFormat.format("Parsed \"{0}\" with timezone {1} to {2}", dateString,
        dateFormat.getTimeZone().getDisplayName(), dateFormat.parse(dateString)));
    }
}

示例输出:

Parsed "13:40" with timezone Greenwich Mean Time to 01/01/70 13:40
Parsed "13:40" with timezone Pacific Standard Time to 01/01/70 22:40
Parsed "13:40 UTC" with timezone Greenwich Mean Time to 01/01/70 14:40
Parsed "13:40 UTC" with timezone Pacific Standard Time to 01/01/70 14:40

请注意第一个示例中的 Date 如何更改 - 但对于第二个示例,它没有。

【问题讨论】:

标签: java simpledateformat date-format


【解决方案1】:

类型错误

您使用了错误的数据类型,试图将时间值适合包含时间和日期以及与 UTC 的偏移量(为零)的类型。 Square peg, round hole.

另外,java.util.Date 类的设计和实现都非常糟糕。几年前,随着 JSR 310 的采用,现代 java.time 类取代了它。

时间:LocalTime

“13:40”

只需解析为 LocalTime 对象。

LocalTime lt = LocalTime.parse( "13:40" ) ;

如果您想结合日期和时区来确定时刻,请应用LocalDate 和ZoneId 来生成ZonedDateTime 对象。

ZoneId z = ZoneId.of( "America/Los_Angeles" ) ;
LocalDate today = LocalDate.now( z ) ;
ZonedDateTime zdt = ZonedDateTime.of( today , lt , z ) ;

要查看 UTC 中的同一时刻,请提取 Instant。

Instant instant = zdt.toInstant() ; 

与 UTC 偏移的时间:OffsetTime

“世界标准时间 13:40”

带有time zone 或offset-from-UTC 的时间实际上没有意义。如果没有日期,就没有有意义的方式来考虑与特定时区相关的时间。没有人能够向我解释一个如何在逻辑上有意义的例子。我听到的每一次尝试都涉及到一个隐含的日期。

尽管如此,SQL 标准委员会决定定义TIME WITH TIME ZONE 数据类型。因此,为了支持这一点,java.time 类包含一个匹配类OffsetTime。

很遗憾,我在您的输入末尾找不到可以解析 SPACE 和 UTC 的格式模式。因此,作为一种解决方法,我建议用单个 Z 字符替换这些字符。所以"13:40 UTC" 变成了"13:40Z"。 Z 表示 UTC,发音为“Zulu”。此格式默认处理,因此无需指定格式模式。

String input = "13:40 UTC".replace( " UTC" , "Z" ) ;  // "13:40 UTC" becomes "13:40Z".
OffsetTime ot = OffsetTime.parse( input ) ;

【讨论】:

  • 我知道 Java 中的“现代” Date 类,但这无助于解释我遇到的行为。
  • @Jakg 我的意思是你的问题没有实际意义。没有理由与那些可怕的日期时间课程搏斗。 Sun、Oracle 和 JCP 社区在采用 JSR 310 时都放弃了这些课程。您也应该这样做。当然,与尝试理解不再使用的过时类的缺陷设计相比,您还有更多富有成效的事情要做。
【解决方案2】:

在 2019 年,没有人应该关心为什么 SimpleDateFormat 和 TimeZone 类的行为方式会如此,因为我们应该在多年前就放弃使用这些类。 Basil Bourque 为您提供了您应该想要的答案。这个答案会尽量满足你的好奇心,但请不要用它来让SimpleDateFormat 表现出来。 不使用那个类会好很多。

我假设您的 JVM 时区是欧洲/伦敦(我们将看到这很重要)。当我以这种方式设置时区并将语言环境设置为英国时,我可以准确地重现您的结果。

解析“13:40”时区格林威治标准时间为 01/01/1970, 13:40

在您的默认时区解析 13:40 会得到默认时区的 13:40,这不足为奇。由于英国在 1970 年冬天处于 UTC 偏移 +01:00,因此该时间与 12:40 UTC 相同(如果没有给出日期,SimpleDateFormat 使用默认值 1970 年 1 月 1 日)。当输出显示Greenwich Mean Time 时,这是一个错误。

解析“13:40”时区太平洋标准时间为 01/01/1970, 22:40

1970 年,美国西海岸比 UTC 晚 8 小时,因此比英国晚 9 小时。因此,当您告诉SimpleDateFormat 假设 13:40 在 America/Los_Angeles 时区时,它会解析为 13:40 PST 的时间,与 21:40 UTC 或 22:40 英国时间相同(America/Los_Angeles 是TimeZone 如何解释 PST,但它已被弃用,不要依赖它。)MessageFormat 使用您的默认时区来打印时间,因此会打印 22:40。

解析“13:40 UTC”时区格林威治标准时间为 01/01/1970, 14:40

由于(如前所述)伦敦此时的偏移量为 +01:00,并且由于 MessageFormat 使用您的默认时区,因此 14:40 是正确且预期的输出。 dateFormat.parse(dateString) 解析为 Date,这是另一个设计不佳且早已过时的类。 Date 是一个时间点,不能保存 UTC 偏移量(与 Basil Bourque 正确使用的 OffsetTime 类相反)。而且您的观察是正确的:SimpleDateFormat 使用字符串中的UTC 将 13:40 解释为时间点(而不是其时区设置)。 MessageFormat 无法知道时间早先是从 13:40 UTC 开始解析的。

解析“13:40 UTC”时区太平洋标准时间为 01/01/1970, 14:40

由于SimpleDateFormat 使用字符串中的UTC 将13:40 解释为时间点,因此我们得到的时间与上述相同。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-09
    • 2013-06-25
    相关资源
    最近更新 更多