将UTC ISO 8601字符串转换为毫秒可提供+12小时的时间

时间:2018-03-07 05:46:05

标签: java android datetime utc iso8601

我正在以UTC ISO8601格式从我的服务器接收时间,如下所示:

2018-01-25T05:14:03.4973436

我正在使用我当前的string将此milliseconds转换为generating,并且我time已经过了一段时间值:

public static CharSequence getRelativeTimeSpan(String timeStamp) {

    DateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
    format.setTimeZone(TimeZone.getTimeZone("UTC"));

    Date date = format.parse(timeStamp);

    long past = date.getTime();

    int offset = TimeZone.getDefault().getRawOffset() + TimeZone.getDefault().getDSTSavings();
    long now = System.currentTimeMillis() + offset;

    return DateUtils.getRelativeTimeSpanString(
            past,
            now,
            DateUtils.MINUTE_IN_MILLIS);
}

但是,出于某种原因,代码会在 过去 现在 <之间产生12 hours的直接差异/ strong>即可。我不确定为什么存在这样的差异但是例如UTC字符串( 2018-01-25T05:14:03.4973436 )会生成以下milliseconds( 1516857243000 ),但它并不真正对应到time中的当前milliseconds

所有这一切的有趣之处在于,这种自动12 hour存在差异。我确保使用"HH"代替"hh"来解释AM/PM差异,因此我不知道造成这种差异的原因。

使用JAVA8时间更新 - 仍然是相同的行为

我已使用以下代码段更新了代码,但我得到了完全相同的行为(12小时即时差异)

public static CharSequence getRelativeTimeSpan(String timeStamp) {
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSSSSS");
    LocalDateTime fromCustomPattern = LocalDateTime.parse(timeStamp, formatter);
    return DateUtils.getRelativeTimeSpanString(
            fromCustomPattern.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(),
            Instant.now().toEpochMilli(),
            DateUtils.MINUTE_IN_MILLIS);
}

2 个答案:

答案 0 :(得分:3)

两个问题:

    来自longDate
  1. System.currentTimeInMillis()毫秒来自UTC。在比较这些值时,不需要添加时区偏移。你不应该像那样调整时区。

  2. DateSimpleDateFormat等仅支持毫秒分辨率,但源字符串的分辨率为100纳秒。有些实现将4973436中的2018-01-25T05:14:03.4973436作为毫秒,导致436毫秒,并将4973000溢出到其他字段。这种情况发生在一些较旧的API级别上,较新的API会将毫秒级别截断/填充为3位数。

    要解决此问题,您可以自行将输入截断为3个小数位,或使用较新的java.time API(API 26+,可在ThreeTenABP库中使用后端)。

答案 1 :(得分:0)

当然可以使用java.time,即现代Java日期和时间API,也称为JSR-310:

public static CharSequence getRelativeTimeSpan(String timeStamp) {
    LocalDateTime fromIso8601 = LocalDateTime.parse(timeStamp);
    return DateUtils.getRelativeTimeSpanString(
            fromIso8601.atOffset(ZoneOffset.UTC).toInstant().toEpochMilli(),
            Instant.now().toEpochMilli(),
            DateUtils.MINUTE_IN_MILLIS);
}

您不需要显式格式化程序。现代日期和时间类将ISO 8601解析为默认值。由于您的日期时间字符串是UTC格式,请使用atOffset(ZoneOffset.UTC)(不是您的默认时区进行转换),这肯定是导致错误结果的原因。

要在ThreeTenABP上使用上述代码,请务必从org.threeten.bp导入:

import org.threeten.bp.Instant;
import org.threeten.bp.LocalDateTime;
import org.threeten.bp.ZoneOffset;

链接