java - 日历给错误的一天

标签 java android date calendar

我从我的 GPS 跟踪器得到一个 UTC 格式的时间字符串,如下所示:hhmmss.ssss

我想通过使用calendar 将UTC 时间转换为用户的本地时间。因此,我通过 substring(int start, int end) 从时间字符串中提取小时、分钟和秒,并通过 Calendar.set(int field, int value) 进行设置> 功能。在此之后,我将 Calendar 转换为 Date,但我知道我有一个错误的日子。

例如timestamp = 091215.0000, 如果我登录 Calendar.getInstance(),我得到:Thu Dec 11 10:12:15 GMT+01:00 2018 但是当我用我的函数转换它时,我得到:Thu Dec 13 10:12:15 GMT+01:00 2018

我的函数

   public static Date utcToLocalTimeFromLock(String timestamp) {
    Calendar calendar = Calendar.getInstance();

    if (timestamp.charAt(0) == '0') {
        calendar.set(Calendar.HOUR_OF_DAY, timestamp.charAt(1) + 1);
    } else {
        calendar.set(Calendar.HOUR_OF_DAY, Integer.valueOf(timestamp.substring(0, 2) + 1));
    }


    calendar.set(Calendar.MINUTE, Integer.valueOf(timestamp.substring(2, 4)));
    calendar.set(Calendar.SECOND, Integer.valueOf(timestamp.substring(4, 6)));

    Date date = calendar.getTime();
    Log.d(LOG_TAG, "utcToLocalTimeFromLock: " + date);
   return date;
}

最佳答案

你的代码出了什么问题?

你的错误在这一行:

        calendar.set(Calendar.HOUR_OF_DAY, timestamp.charAt(1) + 1);

字符在计算机中表示为数字。在 Java 中,字符和数字在许多情况下可以互换使用,这是一个令人困惑的事实。如果 timestamp.charAt(1) 是您示例中的字符 '9',则它表示为数字 57。当您加 1 时,您会得到 58。当您将一天中的小时数设置为 58 — 如果您预期具有默认设置的 Calendar 类会报告错误,那您就错了,这是该类的一个令人困惑的事情,并且只是导致错误的众多原因之一我们建议您避免使用它。它只是继续计算接下来几天的小时数,巧合的是在两天后的正确时间 10 点结束(两天是 48 小时,48 + 10 = 58)。

其他推荐:

  • 不要“手动解析”您的时间字符串。将解析留给库类。
  • 不要通过增加一个小时来转换为欧洲中部时间。它很容易出错。在夏令时 (DST) 期间,它会给出不正确的结果。它不能移植到其他时区。而是再次将转换留给库类。

如何修复?

Basil Bourque 已经展示了使用现代 Java 日期和时间 API java.time 解决问题的好方法。不过,我想向您展示,如果您愿意,您也可以使用 java.time 轻松包含秒的小数部分。

static DateTimeFormatter timeFormatter = new DateTimeFormatterBuilder()
        .appendPattern("HHmmss")
        .appendFraction(ChronoField.NANO_OF_SECOND, 3, 4, true)
        .toFormatter(Locale.ROOT);
static ZoneId zone = ZoneId.of("Europe/Paris");

public static ZonedDateTime utcToLocalTimeFromLock(String timestamp) {
    return LocalDate.now(ZoneOffset.UTC)
            .atTime(LocalTime.parse(timestamp, timeFormatter))
            .atOffset(ZoneOffset.UTC)
            .atZoneSameInstant(zone);
}

让我们试试:

    System.out.println(utcToLocalTimeFromLock(timestamp));

刚才运行时的输出:

2018-12-11T10:12:15+01:00[Europe/Paris]

appendFraction 方法采用小数点后的最小和最大小数位数,因此我分别指定了 3 和 4。根据您的需要,您可以指定最小为 0 和最大为 9 位数字。

当然,如果欧洲/巴黎没有发生这种情况,请替换您自己的时区。

如果您现在不想升级的遗留 API 不可避免地需要一个老式的 Date 对象:

public static Date utcToLocalTimeFromLock(String timestamp) {
    Instant inst= LocalDate.now(ZoneOffset.UTC)
            .atTime(LocalTime.parse(timestamp, timeFormatter))
            .atOffset(ZoneOffset.UTC)
            .toInstant();
    return Date.from(inst);
}

Tue Dec 11 10:12:15 CET 2018

如果使用反向端口(ThreeTen 反向端口和/或 ThreeTenABP,见下文)从 Instant 转换为 Date 而不是 Date.from(inst) 使用这个:

    return DateTimeUtils.toDate(inst);

因为 Date 没有时区,所以在这种情况下不需要时区转换。只是因为我碰巧也在欧洲中部时区,输出是否与您的预期一致——它将是 JVM 时区的时间。

问题:我可以在 Android 上使用 java.time 吗?

是的,java.time 在新旧 Android 设备上都能很好地工作。它只需要至少 Java 6

  • 在 Java 8 及更高版本以及更新的 Android 设备(从 API 级别 26 开始)中,现代 API 是内置的。
  • 在 Java 6 和 7 中获取 ThreeTen Backport,新类的反向端口(ThreeTen for JSR 310;查看底部的链接)。
  • 在(较旧的)Android 上使用 ThreeTen Backport 的 Android 版本。它叫做 ThreeTenABP。并确保使用子包从 org.threeten.bp 导入日期和时间类。

链接

关于java - 日历给错误的一天,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53725617/

相关文章:

android-intent - 在 Android 中的 Activity 、服务和应用程序之间传递变量

java - 在树莓派上使用OpenCV进行视觉追踪FRC

java - Android 与 PC 上的 Java : Different HttpResponse?

java - 无法在 android 设备和 node.js 服务器之间连接

MySQL 选择月的最后一天存在于表中

java - GWT 解析不同语言环境中的日期

Python PANDAS : New Column, 将唯一值应用于所有行

java - 使用单例或依赖注入(inject)之一相对于另一种的优点和缺点

java - 有没有办法设置/更改 CMIS 存储库的功能?

android - Cordova 火力地堡谷歌认证