当 SimpleDateFormat 也出现在 String 中时忽略 TimeZone
SimpleDateFormat ignoring TimeZone when also present in String
Java 的 SimpleDateFormat allows you to specify a TimeZone to be used when parsing a String to a Date.
当字符串不包含时区时,这会像您预期的那样工作,但当存在时区时,它似乎什么都不做。
该文档似乎也没有真正解释如何使用 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
请注意第一个示例的日期如何更改 - 但对于第二个示例,日期没有更改。
类型错误
您使用了错误的数据类型,试图将 time-of-day 值放入包含 time-of-day 和日期以及 offset-from-UTC(为零)的类型中。 Square peg, round hole.
此外,java.util.Date
class 的设计和实现非常糟糕。它在多年前被现代的 java.time classes 取代,并采用了 JSR 310。
Time-of-day: 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() ;
Time-of-day 与 offset-from-UTC: OffsetTime
"13:40 UTC"
time-of-day 和 time zone or offset-from-UTC 实际上没有意义。没有日期,就没有有意义的方式来思考与特定时区相关的时间。没有人能够向我解释一个例子,说明这在逻辑上是如何有意义的。我听到的每一次争论实际上都涉及一个隐含的日期。
尽管如此,SQL 标准委员会明智地决定定义一个 TIME WITH TIME ZONE
数据类型。因此,为了支持这一点,java.time classes 包含匹配的 class、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 ) ;
在 2019 年,没有人应该关心 SimpleDateFormat
和 TimeZone
classes 的行为方式,因为我们应该放弃使用那些 classes 很多几年前。 Basil Bourque 已经给了你应该想要的答案。这个答案会尽量满足你的好奇心,但请不要用它来让 SimpleDateFormat
表现出来。使用 class.
而不是 会更好
我假设您的 JVM 时区是 Europe/London(我们会看到这很重要)。当我以这种方式设置我的时区并将我的区域设置为英国时,我可以准确地重现您的结果。
Parsed "13:40" with timezone Greenwich Mean Time to 01/01/1970, 13:40
在您的默认时区中解析 13:40 会在您的默认时区中得到 13:40,这不足为奇。由于英国在 1970 年冬天的 UTC 偏移量为 +01:00,因此此时间与 12:40 UTC 相同(未指定日期时,SimpleDateFormat
使用默认值 1970 年 1 月 1 日) .当输出显示 Greenwich Mean Time
时,这是一个错误。
Parsed "13:40" with timezone Pacific Standard Time to 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 被打印出来。
Parsed "13:40 UTC" with timezone Greenwich Mean Time to 01/01/1970, 14:40
由于(如前所述)伦敦此时的偏移量为 +01:00,并且由于 MessageFormat
使用您的默认时区,因此 14:40 是正确且预期的输出。 dateFormat.parse(dateString)
解析为 Date
,另一个设计糟糕且过时的 class。 Date
是一个时间点,不能保存 UTC 偏移量(与 Basil Bourque 正确使用的 OffsetTime
class 相反)。您的观察是正确的:SimpleDateFormat
使用字符串中的 UTC
将 13:40 解释为一个时间点(不是其时区设置)。 MessageFormat
无法知道时间早先是从 13:40 UTC 解析的。
Parsed "13:40 UTC" with timezone Pacific Standard Time to 01/01/1970, 14:40
由于SimpleDateFormat
使用字符串中的UTC
将13:40解释为一个时间点,所以我们得到与上面相同的时间。
Java 的 SimpleDateFormat allows you to specify a TimeZone to be used when parsing a String to a Date.
当字符串不包含时区时,这会像您预期的那样工作,但当存在时区时,它似乎什么都不做。
该文档似乎也没有真正解释如何使用 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
请注意第一个示例的日期如何更改 - 但对于第二个示例,日期没有更改。
类型错误
您使用了错误的数据类型,试图将 time-of-day 值放入包含 time-of-day 和日期以及 offset-from-UTC(为零)的类型中。 Square peg, round hole.
此外,java.util.Date
class 的设计和实现非常糟糕。它在多年前被现代的 java.time classes 取代,并采用了 JSR 310。
Time-of-day: 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() ;
Time-of-day 与 offset-from-UTC: OffsetTime
"13:40 UTC"
time-of-day 和 time zone or offset-from-UTC 实际上没有意义。没有日期,就没有有意义的方式来思考与特定时区相关的时间。没有人能够向我解释一个例子,说明这在逻辑上是如何有意义的。我听到的每一次争论实际上都涉及一个隐含的日期。
尽管如此,SQL 标准委员会明智地决定定义一个 TIME WITH TIME ZONE
数据类型。因此,为了支持这一点,java.time classes 包含匹配的 class、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 ) ;
在 2019 年,没有人应该关心 SimpleDateFormat
和 TimeZone
classes 的行为方式,因为我们应该放弃使用那些 classes 很多几年前。 Basil Bourque 已经给了你应该想要的答案。这个答案会尽量满足你的好奇心,但请不要用它来让 SimpleDateFormat
表现出来。使用 class.
我假设您的 JVM 时区是 Europe/London(我们会看到这很重要)。当我以这种方式设置我的时区并将我的区域设置为英国时,我可以准确地重现您的结果。
Parsed "13:40" with timezone Greenwich Mean Time to 01/01/1970, 13:40
在您的默认时区中解析 13:40 会在您的默认时区中得到 13:40,这不足为奇。由于英国在 1970 年冬天的 UTC 偏移量为 +01:00,因此此时间与 12:40 UTC 相同(未指定日期时,SimpleDateFormat
使用默认值 1970 年 1 月 1 日) .当输出显示 Greenwich Mean Time
时,这是一个错误。
Parsed "13:40" with timezone Pacific Standard Time to 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 被打印出来。
Parsed "13:40 UTC" with timezone Greenwich Mean Time to 01/01/1970, 14:40
由于(如前所述)伦敦此时的偏移量为 +01:00,并且由于 MessageFormat
使用您的默认时区,因此 14:40 是正确且预期的输出。 dateFormat.parse(dateString)
解析为 Date
,另一个设计糟糕且过时的 class。 Date
是一个时间点,不能保存 UTC 偏移量(与 Basil Bourque 正确使用的 OffsetTime
class 相反)。您的观察是正确的:SimpleDateFormat
使用字符串中的 UTC
将 13:40 解释为一个时间点(不是其时区设置)。 MessageFormat
无法知道时间早先是从 13:40 UTC 解析的。
Parsed "13:40 UTC" with timezone Pacific Standard Time to 01/01/1970, 14:40
由于SimpleDateFormat
使用字符串中的UTC
将13:40解释为一个时间点,所以我们得到与上面相同的时间。