tl;博士
使用现代 java.time 类。
ZonedDateTime
.of( 2014 , 1 , 14 , 11 , 12 , 0 , 0 , ZoneId.of( "Australia/Sydney" ) )
.toInstant()
.toEpochMilli()
1389658320000
往另一个方向发展。
Instant
.ofEpochMilli( 1_389_658_320_000L ) // .toString(): 2014-01-14T00:12:00Z
.atZone(
ZoneId.of( "Australia/Sydney" )
) // .toString(): 2014-01-14T11:12+11:00[Australia/Sydney]
.format(
DateTimeFormatter
.ofPattern (
"dd/MM/uuuu HH:mm:ss z" ,
new Locale( "en" , "AU" )
)
)
2014 年 14 月 1 日 11:12:00 澳大利亚东部夏令时间
java.time
您正在使用糟糕的日期时间类,这些类在几年前因采用定义现代 java.time 类的 JSR 310 而过时。
我对 Java 中的时区很好奇。
仅供参考,与 UTC 的偏移量只是几个小时-分钟-秒。当我们说“UTC”或在字符串末尾添加Z 时,我们指的是 UTC 本身的零时分秒偏移量。
时区要多得多。时区是特定地区的人们使用的偏移量的过去、现在和未来变化的历史。世界各地的政客对改变他们的管辖范围有一种奇怪的偏好。
我想从设备获取 UTC 时间(以毫秒为单位)并发送到服务器。
目前,请使用Instant。 Instant 内部是自 1970 UTC 第一时刻的纪元参考以来的整秒数,加上以纳秒为单位的小数秒。
Instant now = Instant.now() ; // Capture current moment in UTC.
long millisecondsSinceEpoch = now.toEpochMilli() ;
往另一个方向发展。
Instant instant = Instant.ofEpochMilli( millisecondsSinceEpoch ) ;
服务器会将其转换为本地时区……
指定用户期望/期望的时区。
如果没有指定时区,JVM 会隐式应用其当前的默认时区。该默认值可能在运行时(!)期间change at any moment,因此您的结果可能会有所不同。最好将您想要/预期的时区明确指定为参数。如果关键,请与您的用户确认该区域。
以Continent/Region 的格式指定proper time zone name,例如America/Montreal、Africa/Casablanca 或Pacific/Auckland。切勿使用 2-4 个字母的缩写,例如 EST 或 IST,因为它们不是真正的时区,没有标准化,甚至不是唯一的 (!)。
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
…当它向用户显示时间时
根据用户的语言和文化自动本地化。
要本地化,请指定:
例子:
Locale l = Locale.CANADA_FRENCH ; // Or Locale.US, Locale.JAPAN, etc.
DateTimeFormatter f =
DateTimeFormatter
.ofLocalizedDateTime( FormatStyle.FULL )
.withLocale( l )
;
String output = zdt.format( f );
我系统中的时区是澳大利亚/悉尼(UTC + 11:00)
您的服务器当前的默认时区应该与您的程序无关。始终指定所需/预期的时区。坦率地说,将各种日期时间方法的时区(和Locale)参数设为可选是 java.time 框架中为数不多的设计缺陷之一。
提示:通常最好将您的服务器设置为 UTC 作为其当前的默认时区。
顺便说一句,要明确时区和语言环境与没有任何关系。您可能需要日语来显示在Africa/Tunis 时区中看到的时刻。
ZoneID zAuSydney = ZoneId.of( "Australia/Sydney" ) ;
ZonedDateTime zdt = instant.atZone( zAuSydney ) ;
String output = zdt.format(
DateTimeFormatter
.localizedDateTime( FormatStyle.LONG )
.withLocale( new Locale( "en" , "AU" ) ;
) ;
int 年 = 2014; …
请注意,java.time 使用合理的编号,这与遗留类不同。 1 月至 12 月的月份为 1-12,周一至周日的工作日为 1-7。
LocalDate ld = LocalDate.of( 2014 , 1 , 14 ) ;
LocalTime lt = LocalTime.of( 11 , 12 ) ;
ZoneId z = ZoneId.of( "Australia/Sydney" ) ;
ZonedDateTime zdt = ZonedDateTime.of( ld , lt , z ) ;
zdt.toString() = 2014-01-14T11:12+11:00[澳大利亚/悉尼]
通常最好自动本地化显示,如上所示。但如果您坚持,您可以硬编码格式模式。
Locale locale = new Locale ( "en" , "AU" );
ZoneId z = ZoneId.of ( "Australia/Sydney" );
ZonedDateTime zdt = ZonedDateTime.of ( 2014 , 1 , 14 , 11 , 12 , 0 , 0 , z );
zdt.toString(): 2014-01-14T11:12+11:00[澳大利亚/悉尼]
指定你的格式化模式。
DateTimeFormatter f = DateTimeFormatter.ofPattern ( "dd/MM/uuuu HH:mm:ss z" , locale );
String output = zdt.format ( f );
输出 = 2014 年 1 月 14 日 11:12:00 AEDT
您的问题对自 1970-01-01T00:00:00Z 纪元以来的毫秒数感兴趣。所以从澳大利亚时区调整为UTC。同一时刻,时间轴上的同一点,不同的挂钟时间。
Instant instant = zdt.toInstant() ; // Adjust from time zone to UTC.
instant.toString(): 2014-01-14T00:12:00Z
注意instant 和zdt 之间的小时差异。
我认为我可以为 c2 设置 13/01/2014 00:12:00,因为 UTC 时间比我的晚 11 小时。
➥ 如您所愿,悉尼地区上午 11 点后 12 分钟与 UTC 午夜后 12 分钟是同一时刻,因为该日期的Australia/Sydney 比 UTC 早 11 小时。
计算自纪元以来的毫秒数。
long millisecondsSinceEpoch = instant.toEpochMilli() ;