Java SimpleDataFormat忽略时区,但同时也存在于字符串中

Java SimpleDataFormat忽略时区,但同时也存在于字符串中,java,simpledateformat,date-format,Java,Simpledateformat,Date Format,Java允许您在将字符串解析为时指定要使用的 当字符串不包含时区时,这与您所期望的一样有效,但当存在时区时,它似乎不起任何作用 文档似乎也没有真正解释时区是如何使用的 示例代码: public class DateFormatTest { public static void main(final String[] args) throws ParseException { testBoth("HH:mm", "13:40"); testBoth("H

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

当字符串不包含时区时,这与您所期望的一样有效,但当存在时区时,它似乎不起任何作用

文档似乎也没有真正解释时区是如何使用的

示例代码:

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
请注意,第一个示例中的日期是如何更改的,但第二个示例中的日期不会更改。

错误的类型 您使用了错误的数据类型,试图将一天中的某个时间值适配到包含一天中某个时间、某个日期以及与UTC的偏移量(为零)的类型中

另外,这个
java.util.Date
类的设计和实现非常糟糕。几年前,随着JSR310的采用,它被现代的java.time类所取代

一天中的时间:
LocalTime
“13:40”

只需将其解析为
LocalTime
对象

LocalTime lt = LocalTime.parse( "13:40" ) ;
ZoneId z = ZoneId.of( "America/Los_Angeles" ) ;
LocalDate today = LocalDate.now( z ) ;
ZonedDateTime zdt = ZonedDateTime.of( today , lt , z ) ;
如果要结合日期和时区来确定时刻,请应用
LocalDate
ZoneId
来生成
ZonedDateTime
对象

LocalTime lt = LocalTime.parse( "13:40" ) ;
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
“UTC时间13:40”

一天中有or的时间实际上没有意义。如果没有日期,就没有任何有意义的方式来考虑与特定时区相关的时间。没有人能向我解释一个例子,说明这在逻辑上是如何具有意义的。我听到的每一次尝试实际上都包含一个隐含的日期

然而,SQL标准委员会明智地决定用时区定义一个
时间
数据类型。因此,为了支持这一点,java.time类包括一个匹配的类

不幸的是,我找不到一种格式模式来解析空间和输入末尾的
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 ) ;

在2019年,没有人应该关心为什么
SimpleDateFormat
TimeZone
类的行为方式,因为我们多年前就应该放弃使用这些类了。巴兹尔·波克已经给了你你应该想要的答案。这个答案将试图满足您的好奇心,但请不要用它来让
SimpleDateFormat
表现。你最好不要上那门课

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

将“13:40”与时区格林威治平均时间解析为1970年1月1日13:40

解析默认时区中的13:40会得到默认时区中的13:40,这并不奇怪。由于1970年冬季英国的UTC偏移量为+01:00,因此该时间与UTC 12:40相同(如果没有给出日期,
SimpleDataFormat
使用默认值1970年1月1日)。当输出显示格林威治标准时间时,这是一个错误

将“13:40”与时区太平洋标准时间解析为1970年1月1日22:40

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

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

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

将“13:40 UTC”与时区太平洋标准时间解析为1970年1月1日14:40


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

提示:
PST
不是一个实时时区。实时时区名称的格式为
大陆/地区
,例如
美国/洛杉矶
。我建议您不要使用
SimpleDataFormat
日期格式
。这些类是出了名的麻烦和过时。改用
DateTimeFormatter
LocalTime
OffsetTime
,所有这些都来自。我知道Java中的“现代”日期类,但这无助于解释我所经历的行为。@jagg我的观点是你的问题没有意义。没有理由要与那些糟糕的约会时间课程搏斗。Sun、Oracle和JCP社区在采用JSR 310时都放弃了这些类。你也应该这样。当然,比起试图理解不再使用的过时类的有缺陷的设计,您还有更多的工作要做。