提问人:Dima Garbar 提问时间:5/18/2023 最后编辑:AnonymousDima Garbar 更新时间:5/23/2023 访问量:203
Java ZonedDateTime MST 到 UTC 的转换使用夏令时
Java ZonedDateTime MST to UTC conversion uses daylight savings
问:
我需要将时间戳转换为UTC。 时间戳具有 MST 时区,该时区在时间戳中指定
观察到的转化示例:
- 2023-03-20 08:53:19 MST -> 2023-03-20T14:53:19 (-6)
- 2023-01-20 08:53:19 MST -> 2023-01-20T15:53:19 (-7)
static DateTimeFormatter EVENT_TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss zzz");
static LocalDateTime timestampConverter(String timestamp) {
return ZonedDateTime.parse(timestamp, EVENT_TIMESTAMP_FORMATTER)
.withZoneSameInstant(ZoneOffset.UTC)
.toLocalDateTime();
}
MDT 与 UTC 的偏移量为 -6:00 小时
MST 与 UTC 的偏移量为 -7:00 小时
问题: Java 转换将 MST 视为遵守夏令时的 MDT
例如,JS 库没有这个问题:
console.log(new Date("2023-03-20 08:53:19 MST").toUTCString()); // Mon, 20 Mar 2023 15:53:19 GMT
问题: 为什么逻辑与标准行为不同?
答:
0赞
Stephen Olujare
5/18/2023
#1
首先,让我们在提供的代码片段中定义错误。
我意识到上面的代码片段将 MST 时区视为 MDT,并且不考虑夏令时。我希望你明白这一点。
现在,让我们解决这个问题,您可以使用 ZoneId 类来指定夏令时。使用此更新版本的 timestampConverter 方法,该方法应适用于 MST 和 MDT:
static LocalDateTime timestampConverter(String timestamp) {
ZonedDateTime zonedDateTime = ZonedDateTime.parse(timestamp,EVENT_TIMESTAMP_FORMATTER);
if(zonedDateTime.getZone().getRules().isDaylightSavings(zonedDateTime.toInstant())){
zonedDateTime.toInstant().atZone(ZoneOffset.UTC).toLocalDateTime();
}else{
return zonedDateTime.withZoneSameInstant(ZoneOffset.UTC).toLocalDateTime();
}
}
这应该可以解决您目前正在经历的挑战!如果您需要更多说明...不要犹豫。祝您编码愉快!
评论
0赞
VGR
5/18/2023
问题不在于如何补偿这种行为。问题是询问如何正确解析其时区 ID 明确声明它不在夏令时的字符串。(旁注:您应该修复答案的格式。
0赞
Anonymous
5/23/2023
将 MST 时区视为 MDT,并且不考虑夏令时从问题中可能并不清晰,但问题恰恰相反:即使在 MST 声明不应考虑的情况下,代码也会考虑夏令时。因此,您的代码无法修复任何内容。你的部分和你的部分是等价的,所以你的/是无用的。if
else
if
else
2赞
VGR
5/19/2023
#2
发生这种情况是因为 ZonedDateTime.parse 使用 ZonedDateTime.from,其文档指出:
转换将首先从临时对象获取 ZoneId,并在必要时回退到 ZoneOffset。
因此,将转换为始终具有有效 DST 规则的 ZoneId。MST
我曾希望接受首选区域覆盖的 DateTimeFormatterBuilder.appendZoneText 版本可能会有所帮助,但事实并非如此。首选区域仅针对被视为不明确的区域 ID,并且显然 MST 不被视为不明确。
我找不到任何方法让单个 DateTimeFormatter 做你想要的,但你至少可以在显式查找区域时提供区域覆盖,使用双参数 ZoneId.of 方法:
static DateTimeFormatter EVENT_TIMESTAMP_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private static final Map<String, String> nonDSTOverrides = Map.of(
"PST", "UTC-08:00",
"MST", "UTC-07:00",
"CST", "UTC-06:00",
"EST", "UTC-05:00");
static LocalDateTime timestampConverter(String timestamp) {
int lastSpace = timestamp.lastIndexOf(' ');
if (lastSpace < 0) {
throw new DateTimeParseException(
"Text must contain a space before the timezone.", timestamp, 0);
}
String localPart = timestamp.substring(0, lastSpace);
String zonePart = timestamp.substring(lastSpace + 1);
return ZonedDateTime.of(
LocalDateTime.parse(localPart, EVENT_TIMESTAMP_FORMATTER),
ZoneId.of(zonePart, nonDSTOverrides))
.withZoneSameInstant(ZoneOffset.UTC)
.toLocalDateTime();
}
评论
0赞
Anonymous
5/23/2023
很好的解释。超载对我有用。我想知道为什么它不适合你。这是我的在线演示。DateTimeFormatterBuilder.appendZoneText](TextStyle, Set<ZoneId>)
0赞
VGR
5/23/2023
@OleV.V.我没有看过源代码,但我确实在文档中看到时区的分辨率是基于语言环境的。也许 MST 对我来说被认为是明确的,因为我使用的是美国语言环境,但在其他地方被认为是模棱两可的。
0赞
Anonymous
5/23/2023
感谢您的思考。好吧,我明确指定了对我有用的格式化程序。那么,Java 版本呢?我相信我在早期的 Java 版本中也有过这种工作。而且很多区域都打印为 MST(超过 5 个),所以“不模棱两可”对我来说没有多大意义。我会一直想知道。:-)(是的,存在特定于区域设置的时区名称和缩写,我们显然也应该能够解析。Locale.US;
0赞
Anonymous
5/23/2023
即使您将美国/亚利桑那州指定为首选,您是否只是获得了美国/丹佛?(如果您尝试解析成 a 并打印它)。ZonedDateTime
0赞
VGR
5/23/2023
@OleV.V.是的,我得到美国/丹佛,但我没有指定美国/凤凰城。我正在使用 ZoneId.of(“MST”, Map.of(“MST”, “UTC-07:00”)),我现在看到它返回一个 ZoneId,它根本不与 ID“MST”相关联。使用美国/凤凰城绝对可以解决这个特殊问题。我想我希望有一个通用的解决方案可以应用于任何希望抑制使用 DST 的时区。
0赞
Dima Garbar
5/19/2023
#3
我一直使用此代码来实现 -07:00 转换。
LocalDateTime timestampConverter(String timestamp) {
return ZonedDateTime.parse(timestamp, EVENT_TIMESTAMP_FORMATTER)
// Java conversion treats MST as MDT which observe daylight saving
.withZoneSameLocal(ZoneId.of(ZONE_OFFSET))
.withZoneSameInstant(ZoneOffset.UTC)
.toLocalDateTime();
}
评论
0赞
Anonymous
5/20/2023
Thamks 的贡献。恕我直言,这是一个非常丑陋的解决方案。如果你想要的只是一个,这比需要的要复杂得多。LocalDateTime
0赞
Dima Garbar
5/21/2023
@OleV.V.如果您能提供比这更好的工作解决方案,我将很高兴。或者至少定义为什么这是丑陋的
0赞
Anonymous
5/23/2023
感谢您的请求。这是我推荐的方式。我不喜欢您的代码的是,您假设偏移量为 -07:00,而没有验证它在字符串中是否说。如果字符串在某些日子带有不同的时区,您的转换可能会很遥远,并且没有人可能会注意到,或者当他们注意到时,他们将不知所措,不知道如何以及为什么。我的代码也没有验证,但它也可以从其他时区正确转换。MST
评论
ZoneInfo
aliases.put("MST", "America/Denver");
DateTimeFormatterBuilder.appendZoneText(TextStyle, Set
) 给美国/凤凰城或其他不使用夏令时 (DST) 的时区来解决这个问题吗?我相信是这样,并会推荐它。new ZoneTextPrinterParser
preferredZone=null