我从我的 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
导入日期和时间类。
链接
- > Oracle tutorial: Date Time解释如何使用
java.time
。 - > Java Specification Request (JSR) 310 ,其中首次描述了
java.time
。 - > ThreeTen Backport project ,
java.time
到 Java 6 和 7(JSR-310 的 ThreeTen)的反向移植。 - > ThreeTenABP , ThreeTen Backport 安卓版
- > Question: How to use ThreeTenABP in Android Project ,并提供非常详尽的解释。
关于java - 日历给错误的一天,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53725617/