我默认使用时区巴西,但是当抓到一个纽约的LocalDateTime并转换为 java.tim.Instant 时,即时会被正确填充。问题是,当我尝试使用 Date.from(instantValue)生成日期时,而不是生成纽约的日期,我最终从巴西获得当前日期。
ZoneId nyZone = ZoneId.of("America/New_York");
ZoneId brazilZone = ZoneId.of("America/Recife");
LocalDateTime ldtBrazil = LocalDateTime.now(brazilZone);
LocalDateTime ldtNY = LocalDateTime.now(nyZone);
Instant instantBrazil = ldtBrazil.toInstant(ZoneOffset.UTC);
Instant instantNY = ldtNY.toInstant(ZoneOffset.UTC);
System.out.println("-------LocalDateTime-------");
System.out.println("ldtBrazil : "+ldtBrazil);
System.out.println("ldtNY : "+ldtNY);
System.out.println("\n-------Instant-------");
System.out.println("instantBrazil: "+instantBrazil);
System.out.println("instantNY : "+instantNY);
long milliBrazil = instantBrazil.toEpochMilli();
long milliNY = instantNY.toEpochMilli();
System.out.println("\n----------Milli----------");
System.out.println("miliBrazil : "+milliBrazil);
System.out.println("miliNY : "+milliNY);
Date dateBrazil = Date.from(instantBrazil);
Date dateNY = Date.from(instantNY);
System.out.println("\n---------Date From Instant---------");
System.out.println("dateBrazil: "+dateBrazil);
System.out.println("dateNY : "+dateNY);
System.out.println("\n---------Date From Milli---------");
System.out.println("dateBrazil: "+new Date(milliBrazil));
System.out.println("dateNY : "+new Date(milliNY));
结果
-------LocalDateTime-------
ldtBrazil : 2016-09-21T22:11:52.118
ldtNY : 2016-09-21T21:11:52.118
-------Instant-------
instantBrazil: 2016-09-21T22:11:52.118Z
instantNY : 2016-09-21T21:11:52.118Z
----------Milli----------
miliBrazil : 1474495912118
miliNY : 1474492312118
---------Date From Instant---------
dateBrazil: Wed Sep 21 19:11:52 BRT 2016
dateNY : Wed Sep 21 18:11:52 BRT 2016 //this data must be related to NY LocalDateTime, but reiceved a same date of Brazil.
---------Date From Milli---------
dateBrazil: Wed Sep 21 19:11:52 BRT 2016
dateNY : Wed Sep 21 18:11:52 BRT 2016
答案 0 :(得分:19)
LocalDateTime
表示没有区域您似乎误解了LocalDateTime
的目的。
此课程没有时区,没有offset-from-UTC。 不时间轴上的一个点。相反,它代表了一个关于可能时刻的模糊概念。名称“Local ...”可能是违反直觉的,因为不代表任何特定的地点,而是任何地点。
例如,今年圣诞节是2016年12月25日午夜,或2016-12-25T00:00
。这是没有意义的,除非您申请时区在新西兰奥克兰或加尔各答IN或巴黎FR或蒙特利尔加利福尼亚州获得圣诞节,每个时间轴都是时间轴上的不同点,越晚越好,然后向西移动。
永远不要使用LocalDateTime
,因为您认为这样可以省去区域和偏移的麻烦。恰恰相反,你会把自己挖进一个含糊不清的日期时间值的洞里。
您的大多数业务逻辑,日志记录,数据存储和数据交换都应该在UTC。将UTC视为一个真实的时间;所有其他区域和抵消都伪装成打扮的UTC值。
在java.time中,这意味着Instant
类是您的首选类,即日期时间对象的基本构建块。 Instant
类代表UTC中时间轴上的一个时刻,分辨率为nanoseconds。
Instant now = Instant.now();
ZonedDateTime
仅在需要访问某些地区的wall-clock time时调整时区。应用ZoneId
获取ZonedDateTime
对象。
ZoneId zNewYork = ZoneId.of("America/New_York");
ZoneId zRecife = ZoneId.of("America/Recife");
ZonedDateTime zdtNewYork = now.atZone( zNewYork );
ZonedDateTime zdtRecife = now.atZone( zRecife );
所有这三个对象now
,zdtNewYork
和zdtRecife
都是所有时刻,即时间轴上的同一点。这三个人共享同一个时代。唯一的区别是我们看到他们的挂钟时间的镜头。
避免使用与最早版本的Java捆绑在一起的麻烦的旧日期时间类。因此,请避免java.util.Date
和java.util.Calendar
。他们真的那么糟糕。坚持使用java.time类。
如果必须与尚未针对java.time类型更新的旧代码进行交互,则可以转换为/来自java.time类型。寻找添加到旧类的新方法。 java.util.Date.from
方法需要Instant
。我们可以从Instant
(或ZoneDateTime
)中提取OffsetDateTime
。
java.util.Date utilDate = java.util.Date.from( zdtNewYork.toInstant() );
走向另一个方向。
Instant instant = utilDate.toInstant();
有关转换的详细信息,请参阅my Answer至问题Convert java.util.Date to what “java.time” type?
避免使用从历史算起的数字,例如自1970年以来的UTC开始的毫秒数。计数有多种粒度(毫秒,微秒,纳秒,整秒等)。除了1970年以外,各种计算机系统至少使用了十几个时代。这些数字在人类阅读时没有任何意义,因此可能无法检测到错误。
练习时,您可能会觉得它们很有用。在getEpochSecond
上致电getNano
和Instant
,或拨打值toEpochMilli
。
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。
答案 1 :(得分:6)
在我看来,您对LocalDateTime
和Instant
(或Date
之间的区别感到困惑,这与{{{{1}基本相同。 1}})。这些是完全不同的对象。
Instant
是特定的日历日期和特定的时钟时间。你可以把它想象成这张照片。
或者您可以将其视为年,月,日,小时,分钟和秒。但它没有时区。这正是日历所说的内容以及时钟所说的内容。
LocalDateTime
是一个时刻。例如,尼尔阿姆斯特朗第一次踏上月球的那一刻可以表示为Instant
。 JFK被拍摄的那一刻也是如此。
同样,没有时区。但它与Instant
有所不同。除非您知道要将其写下来的时区,否则您无法记下LocalDateTime
的时间。哦,Instant
和Date
是一回事。
因此,要在Instant
和LocalDateTime
之间进行转换,您需要引用特定的时区。所以要表达尼尔阿姆斯特朗作为一年,一个月,一个月,一小时,一分钟和一秒钟登上月球的那一刻;你需要知道使用什么时区。如果你使用UTC,那是1969年7月21日凌晨2点56分。如果你使用太平洋标准时间,那是1969年7月20日下午6点56分。
有了这些知识,让我们分析你的代码。您从一些Instant
个对象开始。
现在,您使用UTC将这些转换为LocalDateTime
个对象。
然后你打印出来。我们需要知道一个时区才能做到这一点,但那没关系。无论如何,Instant
都会以UTC格式打印。这就是Instant
的含义。
现在,您将Z
个对象转换为毫秒数。精细。这是自1970年1月1日午夜UTC以来的毫秒数。 milliNY明显比milliBrazil少360万,因为它相当于一小时前的Instant
。
然后将Instant
个对象转换为Instant
个对象。这并没有真正改变任何事情,因为Date
和Date
表示同样的事情,即使它们的打印方式不同。
您打印出转换后的Instant
个对象。他们在巴西时间打印,因为这是您的语言环境。恰好Date
比dateNY
提前一小时;但它们仍然在巴西时间打印,比UTC时间晚三个小时。所以你分别得到19:11:52和18:11:52。
最后,您从毫秒数开始生成几个dateBrazil
个对象。但是这些新的Date
对象与您已经拥有的Date
和dateBrazil
完全相同,因为您使用相同的毫秒数制作它们。再次,他们在巴西时间印刷。