我正在尝试为我的应用创建一个正确的转换方法,它将获得PST输入,并可以将其转换为CST或EST,并且还支持夏令时。
这是问题所在。检查以下代码和输出。我只是将我的PST日期转换为CST和EST并打印它。但输出CST和EST中的相同。需要有1小时的差异,但它没有反映出来。
System.out.println("CURRENT in PST : " + new Date());
SimpleDateFormat utcDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
utcDateFormat.setTimeZone(TimeZone.getTimeZone("PST"));
System.out.println("convert in PST : " + utcDateFormat.format( new Date()));
utcDateFormat.setTimeZone(TimeZone.getTimeZone("CST"));
System.out.println("convert in CST : " + utcDateFormat.format(new Date()));
utcDateFormat.setTimeZone(TimeZone.getTimeZone("EST"));
System.out.println("convert in EST : " + utcDateFormat.format(new Date()));
OutPut:
CURRENT in PST : Wed Jun 13 15:14:15 PDT 2018
convert in PST : 2018-06-13T15:14:15Z
convert in CST : 2018-06-13T17:14:15Z
convert in EST : 2018-06-13T17:14:15Z
任何人都可以告诉我为什么?并且如何我可以完美地为美国的所有时区进行此转换。
我使用 EST5EDT 并且它有效,但不知道它会在夏令时开始或结束时支持。 我可以使用JAVA 8.
答案 0 :(得分:3)
我如何能够完美地为美国的所有时区进行此转换。
Instant now = Instant.now() ; // Capture current moment in UTC.
ZonedDateTime zdtLosAngeles = now.atZone( ZoneId.of( "America/Los_Angeles" ) ) ;
ZonedDateTime zdtChicago = now.atZone( ZoneId.of( "America/Chicago" ) ) ;
ZonedDateTime zdtNewYork = now.atZone( ZoneId.of( "America/New_York" ) ) ;
ZonedDateTime zdtGuam = now.atZone( ZoneId.of( "America/Guam" ) ) ;
ZonedDateTime zdtHonolulu = now.atZone( ZoneId.of( "America/Los_Angeles" ) ) ;
ZonedDateTime zdtAnchorage = now.atZone( ZoneId.of( "America/Anchorage" ) ) ;
ZonedDateTime zdtIndianapolis = now.atZone( ZoneId.of( "America/Indiana/Indianapolis" ) ) ;
ZonedDateTime zdtPortOfSpain = now.atZone( ZoneId.of( "America/Port_of_Spain" ) ) ;
ZonedDateTime zdtPhoenix = now.atZone( ZoneId.of( "America/Phoenix" ) ) ;
......等等通过美国的list of the many time zones。
Date
是UTC " PST中的当前:" +新日期()
这是不正确的;这段代码的表现并不像你显然期望的那样。您可能获取Wed Jun 13 15:58:37 PDT 2018
之类的字符串,或者您可能不会。
根据定义,java.util.Date
始终为UTC。定义为自UTC 1970年第一时刻的纪元参考以来的毫秒数。您生成的输出字符串可能位于西海岸时间,但这只是偶然的。
令人困惑的部分是,遗憾的是,Date::toString
方法被设计为动态地注入JVM的当前默认时区,同时生成一个String来表示此Date
对象的值。 如果您的JVM恰好具有某个区域的当前默认时区,例如America/Los_Angeles
,您将获得一个美国西海岸时间字符串。但是如果设置了默认时区,那么你的结果会在运行时变化,而你的" CURRENT在PST:"标签不正确。请记住,JVM当前的默认时区可以在运行期间随时通过JVM中任何应用程序的任何线程中的任何代码进行更改。
传统的日期时间类充满了如此糟糕的设计选择。避免使用这些类。
现代方法使用 java.time 类而不是那些麻烦的旧遗留日期时间类。
Instant
取代java.util.Date
。 Instant
类代表UTC中时间轴上的一个时刻,分辨率为nanoseconds(小数部分最多九(9)位)。
Instant instant = Instant.now() ; // Capture the current moment in UTC.
应用time zone(ZoneId
)获取ZonedDateTime
个对象。同一时刻,时间轴上的同一点,但通过某个地区人民使用的挂钟时间来查看。
以continent/region
的格式指定proper time zone name,例如America/Montreal
,Africa/Casablanca
或Pacific/Auckland
。切勿使用诸如EST
或IST
之类的3-4个字母伪区域,因为它们不是真正的时区,不是标准化的,甚至不是唯一的(!)。
ZoneId z = ZoneId.of( "America/Los_Angeles" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
作为一种快捷方式,您可以通过调用Instant
并传递ZonedDateTime.now
来跳过ZoneId
对象。
ZonedDateTime zdt = ZonedDateTime.now( z ) ;
我如何才能完美地完成美国所有时区的转换。
首先,从不使用伪区“PST”,“CST”,“EST”如上所述。使用实时区域。
美国有三个以上的区域,例如America/Chicago
,America/New_York
,America/Fort_Wayne
,Pacific/Honolulu
,America/Puerto_Rico
等等。为什么这么多?因为当前和过去的做法各不相同。例如,美国的某些地方选择退出Daylight Saving Time (DST)的愚蠢。各个地方都有各种各样的历史,其中offset-from-UTC在该区域被人们在其历史中的不同点改变。
其次,保持您的时区定义是最新的。大多数软件系统使用IANA发布的tzdata(以前称为 Olson数据库)的副本。您的主机操作系统,JVM实施和数据库服务器可能都有tzdata的副本,如果您关心的任何区域的规则发生变化,则必须保持最新状态。
" YYYY-MM-DD' T' HH:MM:SS' Z'"
您的格式化模式在Z
周围放置单引号时做出了可怕的选择。 Z
表示UTC,发音为Zulu。您的单引号告诉格式化程序忽略该特定字符串,就好像它没有意义一样。但它 not 毫无意义,它是关于您选择忽略和丢弃的输入数据的重要信息。
另一件事......该特定格式由ISO 8601标准定义。 java.time 类在解析/生成字符串时默认使用这些标准格式。
Instant.parse( "2018-01-23T12:34:56Z" ) // Parse standard ISO 8601 string into a `Instant` object.
instant.toString() // Yields "2018-01-23T12:34:56Z".
java.time框架内置于Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.Date
,Calendar
和& SimpleDateFormat
现在位于Joda-Time的maintenance mode项目建议迁移到java.time类。
要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。规范是JSR 310。
您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*
类。
从哪里获取java.time类?
ThreeTen-Extra项目使用其他类扩展java.time。该项目是未来可能添加到java.time的试验场。您可以在此处找到一些有用的课程,例如Interval
,YearWeek
,YearQuarter
和more。