简而言之
使用现代的java.time类。
ZonedDateTime
.of( 2014 , 1 , 14 , 11 , 12 , 0 , 0 , ZoneId.of( "Australia/Sydney" ) )
.toInstant()
.toEpochMilli()
1389658320000
Going the other direction.
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年1月14日11时12分(澳大利亚东部标准时间)。
java.time
您正在使用已经过时多年的糟糕日期时间类,这些类已被 JSR 310 所定义的现代 java.time 类所取代。
我对 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会隐式地应用其当前默认的时区。这个默认值可能在运行时
任何时刻改变(!),因此你的结果可能会有所不同。最好明确地指定你期望的时区作为参数。如果很重要,请与您的用户确认时区。
以
大陆/地区
的格式指定一个
正确的时区名称,例如
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 year = 2014; …
请注意,
java.time 使用合理的数字编码,不像旧版类。月份是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月01日T00: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分后的12分钟和UTC时间午夜12点12分后的12分钟相同,因为在该日期,澳大利亚/悉尼比UTC快11个小时。
计算自纪元以来的毫秒数。
long millisecondsSinceEpoch = instant.toEpochMilli() ;
new Date(c1.getTimeInMillis()))
是多余的。使用c1.getTime()
即可。 - Sotirios DelimanolisZ
以查看你所在的时区。 - Sotirios Delimanolisjava.util.Date
、java.util.Calendar
和java.text.SimpleDateFormat
这样的日期时间类现在已经成为遗留系统,被内置于Java 8及更高版本中的java.time类所取代。请参阅Oracle的教程。 - Basil Bourque