【问题标题】:Change timezone of Date object without changing date更改 Date 对象的时区而不更改日期
【发布时间】:2016-12-06 20:57:22
【问题描述】:

我可以在我的 java 代码中更改 Date 对象的时区而不更改日期吗? 当我写 Date date = new Date(longValue); 我在我当地的时区得到这个日期。我想得到这个日期,但我的时区应该是我的用户 ID(例如“美国/芝加哥”)存在于数据库(不是本地)中的一个。

提前感谢您的帮助。

【问题讨论】:

  • 始终使用UTC时间将日期保存在数据库中,并且您可以在需要时获取任何时区日期。

标签: java-8 jodatime


【解决方案1】:

tl;博士

ZonedDateTime zdtMontreal = 
    ZonedDateTime.now( ZoneId.of( "America/Montreal" ) );

ZonedDateTime zdtAuckland = 
    zdtMontreal.withZoneSameLocal( ZoneId.of( "Pacific/Auckland" ) );  

不是同一时刻。zdtAuckland时刻发生在几个小时之前

确定这确实是您的意图。我有一种下沉的感觉,你在做错事,对日期时间处理的工作方式感到困惑。日期时间工作令人困惑。请务必搜索和研究 Stack Overflow。

java.time

Joda-Time 项目现在位于 maintenance mode,建议迁移到 java.time。

Local… 类故意没有时区或offset-from-UTC 信息。

LocalDate ld = LocalDate.of( 2016 , Month.January , 23 ); // 2016-01-23
LocalTime lt = LocalTime.of( 12 , 34 , 56 );  // 12:34:56

这些值代表时间线上的一个点。那会是新西兰奥克兰、法国巴黎或加利福尼亚蒙特利尔的中午时间吗?我们必须应用时区来确定实际时刻。我们应用ZoneId 来获得ZonedDateTime

continent/region 的格式指定proper time zone name,例如America/MontrealAfrica/CasablancaPacific/Auckland。切勿使用 3-4 个字母的缩写,例如 ESTISTCST,因为它们不是真正的时区,没有标准化,甚至不是唯一的 (!)。

ZoneId zMontreal = ZoneId.of( "America/Montreal" );
ZonedDateTime zdtMontreal = ZonedDateTime.of( ld , lt , zMontreal );

要查看以标准 ISO 8601 格式表示此值的字符串,请调用 toString。实际上,ZonedDateTime 类通过在方括号中巧妙地附加时区名称来扩展标准格式。

String outputMontreal = zdtMontreal.toString();

2016-01-23T12:34:56-05:00[美国/蒙特利尔]

要在另一个时区获取相同的日期和时间,请重复该过程。

ZoneId zParis = ZoneId.of( "Europe/Paris" );
ZonedDateTime zdtParis = ZonedDateTime.of( ld , lt , zParis );

ZoneId zAuckland = ZoneId.of( "Pacific/Auckland" );
ZonedDateTime zdtAuckland = ZonedDateTime.of( ld , lt , zAuckland );

但要知道,您正在经历一个不同的时刻。奥克兰的中午发生在巴黎中午前几个小时,蒙特利尔的中午甚至更晚,因为三个不同的时间点碰巧共享相同的wall-clock time

2016-01-23T12:34:56-05:00[美国/蒙特利尔]

2016-01-23T12:34:56+01:00[欧洲/巴黎]

2016-01-23T12:34:56+13:00[太平洋/奥克兰]

当前时刻

要获取当前时刻,请致电Instant.nowInstant 类代表UTC 中时间线上的时刻,分辨率为nanoseconds(最多九 (9) 位小数)。

Instant instance = Instance.now();

应用ZoneId 调整到时区。

ZoneId z = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = instance.atZone( z );

对其他所需时区重复该过程,类似于我们在上面所做的。

作为快捷方式,您可以直接获取ZonedDateTime

ZonedDateTime zdtMontreal = ZonedDateTime.now( ZoneId.of( "America/Montreal" ) );

作为另一个快捷方式,您可以应用不同的时区,同时保留相同的日期和时间(相同的挂钟时间)。致电ZonedDateTime::withZoneSameLocal

ZonedDateTime zdtAuckland = zdtMontreal.withZoneSameLocal( ZoneId.of( "Pacific/Auckland" ) );

我再说一遍:这个结果是时间线上的不同点。 zdtAuckland 发生在 zdtMontreal 发生前几个小时。

要保持相同的时刻,请致电ZonedDateTime::withZoneSameInstant

数据库

通常最好将您的日期时间值存储在UTC 的数据库中。已经在 Stack Overflow 上进行了广泛讨论。搜索更多信息。


关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

Joda-Time 项目现在位于 maintenance mode,建议迁移到 java.time。

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

从哪里获得 java.time 类?

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

【讨论】:

    【解决方案2】:

    您可能会发现ZonedDateTime 很有帮助:

    Java 8 Convert given time and time zone to UTC time

    或者,您可以创建另一个 Date 对象并手动对其应用一些偏移量。

    【讨论】:

    • 我已经尝试过了,但它并没有解决我的问题。我只想更改我的时区而不是日期。例如,我希望将此日期 2016 年 11 月 23 日下午 4:16 EST 更改为 2016 年 11 月 23 日下午 4:16 CST。这可能吗?
    【解决方案3】:

    一个使用Java8 ZonedDateTime类的例子:

    Date date = new Date(longValue);
    ZoneId = ZoneId.of("America/Chicago");
    ZonedDateTime dt = ZonedDateTime.ofInstant(date.toInstant(), zoneId);
    

    你已经用 Joda 标记了这个问题,但实际上使用 Java8 你不再需要它了。

    【讨论】:

    • 我已经尝试过了,但它并没有解决我的问题。我只想更改我的时区而不是日期。例如,我希望将此日期 2016 年 11 月 23 日下午 4:16 EST 更改为 2016 年 11 月 23 日下午 4:16 CST。您的示例也更改了日期。
    猜你喜欢
    • 1970-01-01
    • 2014-02-16
    • 2015-01-29
    • 2014-11-14
    • 1970-01-01
    • 2019-11-18
    • 1970-01-01
    • 1970-01-01
    • 2013-03-12
    相关资源
    最近更新 更多