将 GMT 日期时间转换为本地时区日期时间

Posted

技术标签:

【中文标题】将 GMT 日期时间转换为本地时区日期时间【英文标题】:Convert a GMT datetime to local timezone datetime 【发布时间】:2021-05-02 03:13:57 【问题描述】:

在 Java 8 中,我需要一种从 ISO 8601 格式的 GMT 日期时间获取本地日期时间 (GMT+1) 的方法。

一个简单的例子: 客户在这个日期时间向我(服务器)发送"2020-01-11T23:00:00.000Z" 当用户从日期选择器中选择 2020 年 1 月 12 日时,客户会向我发送此消息。 GMT+1 是 1 月 12 日,而 GMT 是前一天。

由于上述原因,我知道对我而言,这个日期时间不是 2020 年 1 月 11 日,而是 2020 年 1 月 12 日(GMT+1)。

所以我需要这个值"2020-01-12T00:00:00.000"

确切地说,我不需要使用 simpleDateFormat 打印它,只需在 java.util.Date 类字段中将 "2020-01-11T23:00:00.000Z" 转换为 "2020-01-12T00:00:00.000"

谢谢。

【问题讨论】:

我建议你不要使用SimpleDateFormatjava.util.Date。这些类设计不良且过时,尤其是前者,尤其是出了名的麻烦。如果您只对日期(而不是一天中的时间)感兴趣,请使用 java.time, the modern Java date and time API 中的 LocalDate 正如您所说的那样,2020-01-11T23:00:00.000Z 和 2020-01-12T00:00:00.000+01:00 是同一时间点。 Date 只是一个时间点,因此代表这两个时间的两个 Date 对象将是相同的。它们之间没有转换。 您知道客户端时区或UTC偏移量吗?否则你会不知所措,至少对于某些输入值。 【参考方案1】:

问题是源系统采用纯日期值,但在午夜添加时间,然后将其转换为 UTC,但您希望纯日期值在 java.util.Date 中,默认情况下在您的本地时区打印,即JVM的默认时区。

因此,您必须解析字符串,将值恢复为源系统的时区,将该本地时间视为您自己的 JVM 的默认时区中的时间。

你可以这样做,显示所有中间类型:

String sourceStr = "2020-01-11T23:00:00.000Z";
ZoneId sourceTimeZone = ZoneOffset.ofHours(1); // Use real zone of source, e.g. ZoneId.of("Europe/Paris");

// Parse Zulu date string as zoned date/time in source time zone
Instant sourceInstant = Instant.parse(sourceStr);
ZonedDateTime sourceZoned = sourceInstant.atZone(sourceTimeZone);

// Convert to util.Date in local time zone
ZonedDateTime localZoned = sourceZoned.withZoneSameLocal(ZoneId.systemDefault());
Instant localInstant = localZoned.toInstant();
Date localDate = Date.from(localInstant); // <== This is your desired result

// Print value in ISO 8601 format
String localStr = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS").format(localDate);
System.out.println(localStr);

输出

2020-01-12T00:00:00.000

代码当然可以合并在一起:

String input = "2020-01-11T23:00:00.000Z";

Date date = Date.from(Instant.parse(input).atZone(ZoneOffset.ofHours(1))
        .withZoneSameLocal(ZoneId.systemDefault()).toInstant());

System.out.println(date);

输出

Sun Jan 12 00:00:00 EST 2020

如您所见,日期值是正确的,即使我在美国东部时区。

【讨论】:

我不确定我是否理解 withZoneSameLocal 的用途。我认为Instant.parse("2020-01-11T23:00:00.000Z").atZone(ZoneId.of("GMT+1")).format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) 已经可以解决问题了。为什么我们需要两个ZonedDateTimes;只是为了让OP更容易理解吗? @akuzminykh java.util.Date 以 UTC 存储值,但在 JVM 的默认时区(也称为“本地”时区)中解析和打印值,这是第一个探针的原始时区地方。在源服务器 (GMT+1) 上,“本地”日期 2020-01-12 又名 2020-01-12T00:00+01:00,存储在 Date 中作为 2020-01-11T23:00Z。在我的机器(US Eastern)上,“本地”日期2020-01-12 又名2020-01-12T00:00-05:00 存储为2020-01-12T05:00Z。我们使用withZoneSameLocal2020-01-12T00:00+01:00 转换为2020-01-12T00:00-05:00 @Andreas 感谢您的回答。在这种情况下如何处理 DST。这是一个需要处理的问题吗? @Yuri 是的,这就是为什么你应该使用源系统的实时时区(例如Europe/Paris),而不是代码中注释的硬编码GMT+1

以上是关于将 GMT 日期时间转换为本地时区日期时间的主要内容,如果未能解决你的问题,请参考以下文章

将日期从 GMT 时区转换为本地时区——使用 ISO_OFFSET_DATE_TIME

用户日期时间设置为 GMT,如何将日期转换为其本地化设置?

将本地时间转换为用户首选时区,将用户首选时区转换为 GMT

使用时区偏移量而不是时区标识符将 GMT 时间转换为本地时间

如何使用 MQ 的 esql 将日期的特定时区转换为 GMT 时区?

如何使用js将UTC日期转换为本地时间? [复制]