当字符串中也存在时,SimpleDateFormat忽略TimeZone

时间:2019-06-10 13:23:32

标签: java simpledateformat date-format

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

当String不包含时区时,此方法可以按预期工作,但是当存在时区时,它似乎无作用。

文档似乎也没有真正解释如何使用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

请注意,第一个示例的日期如何更改-但第二个示例的日期没有更改。

2 个答案:

答案 0 :(得分:1)

类型错误

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

此外,该java.util.Date类的设计和实现也非常糟糕。几年前,现代的 java.time 类通过采用JSR 310取代了它。

时间:LocalTime

  

“ 13:40”

简单地解析为LocalTime对象。

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

如果要结合日期和时区来确定时刻,请应用LocalDateZoneId来生成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 UTC”

在一天中使用time zoneoffset-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 ) ;

Table of date-time types in Java (both modern and legacy) and in standard SQL.

答案 1 :(得分:0)

在2019年,没有人应该关心SimpleDateFormatTimeZone类的行为方式,因为我们早在多年前就已经放弃使用这些类。罗勒·布尔克(Basil Bourque)给了您您想要的答案。此答案将尽力满足您的好奇心,但请不要使用它来使SimpleDateFormat表现良好。使用该类,会更好。

我假设您的JVM时区是Europe / London(我们会发现这很重要)。当我以这种方式设置时区并将地区设置为英国时,我可以准确地重现您的结果。

  

使用格林威治标准时间到1970年1月1日13:40的时区解析为“ 13:40”

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

  

使用太平洋标准时间到1970年1月1日22:40解析为“ 13:40”

1970年,美国西海岸比UTC落后8个小时,因此比英国落后9个小时。因此,当您告诉SimpleDateFormat假定13:40在美国/洛杉矶时区时,它将解析为太平洋标准时间13:40,与世界标准时间21:40或英国时间22:40相同(美国/ Los_Angeles是TimeZone解释PST的方式,但已过时,请不要依赖它。)MessageFormat使用默认时区来打印时间,因此会打印22:40。

  

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

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

  

使用“太平洋标准时间”到1970年1月1日14:40的时区,解析为“ 13:40 UTC”

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