跟踪日期时间的错误方法
首先我必须说一下 count-from- 的使用epoch https://en.wikipedia.org/wiki/Epoch_(reference_date)各种日期时间值的整数时区 https://en.wikipedia.org/wiki/Time_zone而不是使用 UTC 是一个非常非常糟糕的主意。我见过一些处理日期时间的糟糕方法,包括我自己发明的一两种糟糕的方法。但这是最糟糕的。想出这个主意的人应该被判一年每天阅读 StackOverflow 上标有“java”、“date”和“Jon Skeet”的答案。
在应用程序代码中使用 count-from-epoch 处理日期时间就像使用位数组处理文本一样。我们有 CharSequence、String、StringBuilder、Printer、Reader 等类/接口来处理文本、字符、字符编码、排序规则等的具体复杂细节,使我们可以更轻松地编写应用程序。想象一下尝试调试、排除故障并将文本数据记录为位数组。疯了,对吧?尝试调试、排除故障并将日期时间数据记录为长整数也是疯狂的。
日期时间也是如此,我们有乔达时间 http://www.joda.org/joda-time/现在有了它的继任者java.time http://docs.oracle.com/javase/8/docs/api/java/time/package-summary.html (Tutorial http://docs.oracle.com/javase/tutorial/datetime/TOC.html)内置于 Java 8 及更高版本中。
其次,隐式地将纪元计数调整为时区,然后丢失该事实会使不良做法变得更糟。
Fix
解决此问题的方法是将某个任意时区中的纪元计数转换为本地日期和本地时间,其中local意味着挂钟时间 https://en.wikipedia.org/wiki/Wall-clock_time正如比时区的人们所看到的那样。
有了本地日期时间,我们创建一个具有指定时区的日期时间对象,ZonedDateTime http://docs.oracle.com/javase/8/docs/api/java/time/ZonedDateTime.html. A 分区日期时间基本上是一个Instant http://docs.oracle.com/javase/8/docs/api/java/time/Instant.html(UTC 时间线上的一个点)加上ZoneId http://docs.oracle.com/javase/8/docs/api/java/time/ZoneId.html(时区)。
由于问题的作者未能提供任何示例数据,让我们以这种奇怪的方式创建一个值。获取芝加哥时区的当前时刻。获取合法的纪元计数,调整自纳秒 https://en.wikipedia.org/wiki/Nanosecond决议毫秒 https://en.wikipedia.org/wiki/Millisecond。然后任意添加/减去该时区与 UTC 的偏移量。
在此示例中,我们使用时区America/Chicago https://en.wikipedia.org/wiki/List_of_tz_database_time_zones。我们的样本在夏令时的偏移量是-05:00
。以毫秒为单位,5 * 60 * 60 * 1,000 = 18,000,000。
// First, create sample data, a count-from-epoch but not in UTC, instead adjusted for the time zone’s offset.
ZoneId zoneId = ZoneId.of( "America/Chicago" );
// 2015-09-19T12:34:56.000-05:00[America/Chicago]
ZonedDateTime zdtTemp = ZonedDateTime.of( 2015 , 9 , 19 , 12 , 34 , 56 , 0 , zoneId );
long millisecondsFromEpoch = zdtTemp.toInstant().toEpochMilli(); // Loosing data, goin from nanosecond
long offsetInMillisecondsForChicagoInDaylightSavingTime = 18_000_000L; // Offset of `-05:00` is in milliseconds, 5 * 60 * 60 * 1,000 = 18,000,000.
long input = ( millisecondsFromEpoch - offsetInMillisecondsForChicagoInDaylightSavingTime );
转储到控制台。
System.out.println( "zoneId : " + zoneId );
System.out.println( "zdtTemp : " + zdtTemp );
System.out.println( "millisecondsFromEpoch : " + millisecondsFromEpoch );
System.out.println( "offsetInMillisecondsForChicagoInDaylightSavingTime : " + offsetInMillisecondsForChicagoInDaylightSavingTime );
System.out.println( "input : " + input );
现在,做工作吧。采取这个奇怪的输入数字,假装它是 UTC,即使我们知道它不是,以产生一个 Instant。从 Instant 中获取 LocalDateTime。现在将 LocalDateTime 推入时区以获得我们最终想要的,ZonedDateTime
.
// With example data in hand, proceed to convert to a valid date-time object.
Instant instantPretendingToBeInUtcButNotReally = Instant.ofEpochMilli( input );
LocalDateTime localDateTimeOfPretendInstant = LocalDateTime.ofInstant( instantPretendingToBeInUtcButNotReally , ZoneOffset.UTC );
ZonedDateTime zdt = localDateTimeOfPretendInstant.atZone( zoneId );
转储到控制台。
System.out.println( "instantPretendingToBeInUtcButNotReally : " + instantPretendingToBeInUtcButNotReally );
System.out.println( "localDateTimeOfPretendInstant : " + localDateTimeOfPretendInstant );
System.out.println( "zdt : " + zdt );
运行时。
zoneId : America/Chicago
zdtTemp : 2015-09-19T12:34:56-05:00[America/Chicago]
millisecondsFromEpoch : 1442684096000
offsetInMillisecondsForChicagoInDaylightSavingTime : 18000000
input : 1442666096000
instantPretendingToBeInUtcButNotReally : 2015-09-19T12:34:56Z
localDateTimeOfPretendInstant : 2015-09-19T12:34:56
zdt : 2015-09-19T12:34:56-05:00[America/Chicago]
CAVEAT我匆忙地做了这件事。请评论或修复任何错误。