java SameTime与不同的TImeZone

noj0wjuj  于 5个月前  发布在  Java
关注(0)|答案(2)|浏览(50)

为什么程序会用不同的TimeZone处理相同的输出/时间?

Calendar cal = GregorianCalendar.getInstance();
    TimeZone timeZone_1 = TimeZone.getTimeZone("Asia/Rangoon");
    cal.setTimeZone(timeZone_1);
    System.out.println(cal.getTime());

    TimeZone timeZone_2 = TimeZone.getTimeZone("Asia/Tokyo");
    cal.setTimeZone(timeZone_2);
    System.out.println(cal.getTime());

字符串
输出示例:

Thu Nov 22 09:00:33 MMT 2012
Thu Nov 22 09:00:33 MMT 2012


我的预期输出是:

Thu Nov 22 09:00:33 MMT 2012
Thu Nov 22 11:30:33 MMT 2012

pkmbmrz7

pkmbmrz71#

你的代码很好,只是调试输出有问题(误导)。cal.getTime()返回Date对象,它与时区无关。但是Date.toString()总是用系统的时区打印这个日期。Date.toString()是如此违反直觉(它用系统时区显示日历时间,而它几乎不存储毫秒数),它应该被禁止/弃用。
要获得准确的日志记录,请使用SimpleDateFormat或调用各种Calendar.get*()方法:

System.out.println(cal.get(Calendar.HOUR_OF_DAY));
System.out.println(cal.get(Calendar.MINUTE));

字符串
和往常一样,对于Java日期/时间处理的任何问题,请考虑jodatime.

mkh04yzy

mkh04yzy2#

tl;dr

Instant                                           // Represent a moment as seen in UTC, that is, with an offset from UTC of zero hours-minutes-seconds.
.now()                                            // Capture the current moment in UTC.
.atZone( ZoneId.of( "Asia/Rangoon" ) )            // Adjust from UTC to a particular time zone. Same moment, different wall-clock/calendar.
.atZoneSameInstant( ZoneId.of( "Asia/Tokyo" ) )   // Adjust from one time zone to another. Same moment, different wall-clock/calendar. 
.toString()                                       // Generate text in ISO 8601 format wisely extended to append the name of time zone in square brackets.

字符串

java.util.Date#toString说谎

The Answer by Tomasz Nurkiewicz是正确的。您对cal.getTime()的调用将返回一个java.util.Date对象,该对象的toString方法属于您。该Date#toString方法具有动态将当前默认时区应用于其值的反功能,该值实际上存储为UTC中的时刻(从UTC偏移0小时-分钟-秒)。
这个反特性是避免使用DateCalendar类的原因之一。

避免使用旧的日期时间类

您正在使用有严重缺陷的日期-时间类,这些类在几年前就被JSR 310中定义的现代 java.time 类所取代。

ZonedDateTime

要表示当前时刻,请使用Instant
你可以调整那个时刻,让它在一个特定的时区中被看到。应用一个ZoneId来生成一个[ZonedDateTime][2]]对象。

ZoneId zRangoon = ZoneId.of( "Asia/Rangoon" );
ZoneId zTokyo = ZoneId.of( "Asia/Tokyo" ) ;

Instant instant = Instant.now() ;  // Always in UTC (offset of zero). 

// Adjust that moment into particular time zones.
ZonedDateTime zdtRangoon = instant.atZone( zRangoon ) ;
ZonedDateTime zdtTokyo = instant.atZone( zTokyo ) ;


由变量instantzdtRangoonzdtTokyo引用的对象都表示非常相同的时刻,时间轴上的相同点-三个视图,一个时刻。
调用ZonedDateTime#toString生成标准ISO 8601的文本,并明智地将时区名称附加在方括号中。

String outputRangoon = zdtRangoon.toString() ;
String outputTokyo = zdtTokyo.toString() ;


请看这段代码run at Ideone.com。注意日期和时间的不同。

instant.toString() = 2023-12-31T20:36:15.449185Z
outputRangoon = 2024-01-01T03:06:15.449185+06:30[Asia/Rangoon]
outputTokyo = 2024-01-01T05:36:15.449185+09:00[Asia/Tokyo]


要获得本地化的输出,请使用DateTimeFormatter.ofLocalizedDateTime。搜索堆栈溢出以查找更多信息。

相关问题