如何在java中转换1413225446.92000
纪元ZonedDateTime
?
给出的代码需要长值,因此这将抛出NumberFormatException
上面给出的值。
ZonedDateTime.ofInstant(Instant.ofEpochMilli(Long.parseLong(dateInMillis)), ZoneId.of(TIME_ZONE_PST));
如何在java中转换1413225446.92000
纪元ZonedDateTime
?
给出的代码需要长值,因此这将抛出NumberFormatException
上面给出的值。
ZonedDateTime.ofInstant(Instant.ofEpochMilli(Long.parseLong(dateInMillis)), ZoneId.of(TIME_ZONE_PST));
编辑:如果您的毫秒值始终为非负数,则DateTimeFormatter
可以解析它。
private static final String TIME_ZONE_PST = "America/Los_Angeles";
private static final DateTimeFormatter epochFormatter = new DateTimeFormatterBuilder()
.appendValue(ChronoField.INSTANT_SECONDS, 1, 19, SignStyle.NEVER)
.optionalStart()
.appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true)
.optionalEnd()
.toFormatter()
.withZone(ZoneId.of(TIME_ZONE_PST));
现在解析成 aZonedDateTime
只是一个方法调用:
ZonedDateTime zdt = ZonedDateTime.parse(dateInMillis, epochFormatter);
System.out.println(zdt);
输出是:
2014-10-13T11:37:26.920-07:00[美国/洛杉矶]
它不能在负值下正常工作:分数仍会被解析为正数,我假设这是不正确的。为了确保在出现负值的情况下得到通知,我在格式化程序中指定了该数字无法签名。
如果您需要更通用的解决方案,例如包括负数,我认为最好让BigDecinmal
解析数字并进行数学运算。
BigDecimal bd = new BigDecimal(dateInMillis);
BigDecimal[] wholeAndFractional = bd.divideAndRemainder(BigDecimal.ONE);
long seconds = wholeAndFractional[0].longValueExact();
int nanos = wholeAndFractional[1].movePointRight(9).intValue();
ZonedDateTime zdt = Instant.ofEpochSecond(seconds, nanos)
.atZone(ZoneId.of(TIME_ZONE_PST));
输出和以前一样。只是现在我们还可以按预期处理负数:
String dateInMillis = "-1.5";
1969-12-31T15:59:58.500-08:00[美国/洛杉矶]
甚至科学记数法也被接受:
String dateInMillis = "1.41322544692E9";
2014-10-13T11:37:26.920-07:00[美国/洛杉矶]
如果字符串中可能有比纳秒更精细的精度,请考虑您希望如何截断或舍入,并BigDecimal
相应地指示,有许多选项。
Basil Bourque 的回答很好。从小数部分取出纳秒到纳秒的整数可能会带来一两个陷阱。我建议:
String dateInMillis = "1413225446.92000";
String[] secondsAndFraction = dateInMillis.split("\\.");
int nanos = 0;
if (secondsAndFraction.length > 1) { // there’s a fractional part
// extend fractional part to 9 digits to obtain nanoseconds
String nanosecondsString
= (secondsAndFraction[1] + "000000000").substring(0, 9);
nanos = Integer.parseInt(nanosecondsString);
// if the double number was negative, the nanos must be too
if (dateInMillis.startsWith("-")) {
nanos = -nanos;
}
}
ZonedDateTime zdt = Instant
.ofEpochSecond(Long.parseLong(secondsAndFraction[0]), nanos)
.atZone(ZoneId.of("Asia/Manila"));
System.out.println(zdt);
这打印
2014-10-14T02:37:26.920+08:00[Asia/Manila]
我们不需要 64 位纳秒,所以我只是使用int
.
假设:我假设您的字符串包含一个浮点数并且它可能是有符号的,例如意味着在纪元前-1.50
一秒半。如果有一天您的纪元时间以科学计数法出现(1.41322544692E9),上述方法将不起作用。
如果不是亚洲/马尼拉,请以地区/城市格式替换您想要的时区,例如美国/温哥华、美国/洛杉矶或太平洋/皮特凯恩。避免使用 PST 之类的三个字母缩写,它们是模棱两可的,而且通常不是真实的时区。
将数字拆分为一对 64 位长整数:
将这些数字传递给工厂方法Instant.ofEpochSecond(long epochSecond, long nanoAdjustment)
拿着aInstant
在手,继续分配一个时区来获得一个ZonedDateTime
.
ZoneId z = ZoneId.of( "America/Los_Angeles" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;
在这里扩展 Basil 和 Ole 的答案,对于负时间戳的特殊情况,即在纪元之前。这甚至可能吗?这是Jon Skeet在“All about java.util.Date”中所写的:
Date 类使用“自 Unix 纪元以来的毫秒数”——这是 getTime() 返回的值,由 Date(long) 构造函数或 setTime() 方法设置。由于月球漫步发生在 Unix 纪元之前,该值为负数:实际上是 -14159020000。
Ole 的答案(除了一些额外的断言)之间唯一真正的区别是,在这里,如果日期字符串以负号开头,我们不会反转 nanos 上的符号。这样做的原因是,当将 nanos 传递给Instant 构造函数时,这是一个调整,所以如果我们将 nanos 作为负数发送,它实际上会调整秒数,因此整个 ZonedDateTime 值被 nanos 关闭。
这是来自 JavaDoc,请注意有趣的行为:
此方法允许传入任意数量的纳秒。工厂将更改秒和纳秒的值,以确保存储的纳秒在 0 到 999,999,999 的范围内。例如,以下将导致完全相同的瞬间:
Instant.ofEpochSecond(3, 1);
Instant.ofEpochSecond(4,-999_999_999);
Instant.ofEpochSecond(2, 1000_000_001);
所以第二个参数 nanos,我们没有设置值,它是一个调整。因此,与正时间戳(在 epoch 之后)相同,我们希望发送实际的 nanos。
以 Ole 的代码为基础并添加上述更改:
String strDateZoned = "Jul 20 1969 21:56:20.599 CDT"; // yes, should use America/Chicago here as Ole points out
DateTimeFormatter dtfFormatter = DateTimeFormatter.ofPattern("MMM dd yyyy HH:mm:ss.SSS zzz");
ZonedDateTime originalZoned = ZonedDateTime.parse(strDateZoned, dtfFormatter);
long epochSeconds = originalZoned.toInstant().getEpochSecond();
int nanoSeconds = originalZoned.toInstant().getNano();
String dateInMillis = epochSeconds + "." + nanoSeconds;
String[] secondsAndFraction = dateInMillis.split("\\.");
int nanos = 0;
if (secondsAndFraction.length > 1) { // there’s a fractional part
// extend fractional part to 9 digits to obtain nanoseconds
String nanosecondsString
= (secondsAndFraction[1] + "000000000").substring(0, 9);
nanos = Integer.parseInt(nanosecondsString);
}
ZonedDateTime zdt = Instant
.ofEpochSecond(Long.parseLong(secondsAndFraction[0]), nanos)
.atZone(ZoneId.of("America/Chicago"));
String formattedZdt = dtfFormatter.format(zdt);
System.out.println("zoneDateTime expected = " + strDateZoned);
System.out.println("zoneDateTime from millis = " + formattedZdt);
assertEquals("date in millis is wrong", "-14159020.599000000", dateInMillis);
assertEquals("date doesn't match expected",strDateZoned, dtfFormatter.format(zdt));
代码输出:
zoneDateTime expected = Jul 20 1969 21:56:20.599 CDT
zoneDateTime from millis = Jul 20 1969 21:56:20.599 CDT
如果我们在秒部分为负的情况下反转 nanos 上的符号,我们可以看到格式化的 ZonedDateTime 的差异:
org.junit.ComparisonFailure: date doesn't match expected
Expected :Jul 20 1969 21:56:20.599 CDT
Actual :Jul 20 1969 21:56:19.401 CDT
PS 'All About Dates' 帖子中关于 Jon Skeet 所说的“宽大处理”以及我在其他地方看到的称为“规范化”的更多想法,这可能是由于 POSIX 的影响:
没有明显的理由是宽松的:“在所有情况下,为这些目的提供给方法的参数不必在指定的范围内;例如,日期可以指定为 1 月 32 日,并被解释为 2 月 1 日。” 多久有用一次?