我从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;
}
答案 0 :(得分:2)
您使用的是可怕的旧日期时间类,而这些类早已被行业领先的 java.time 类取代了。
使用UTC将当前时刻捕获为OffsetDateTime
对象。
OffsetDateTime now = OffsetDateTime.now( ZoneOffset.UTC ) ;
将一天中的时间解析为LocalTime
。
String input = "091215.000" ;
DateTimeFormatter f = DateTimeFormatter.ofPattern( "HHmmss.SSS" ) ;
LocalTime lt = LocalTime.parse( input ) ;
应用我们的一天中的时间。
OffsetDateTime odt = now.with( lt ) ;
您可能会在午夜时分遇到问题。您可能需要添加一些代码,以查看捕获的当前时间是否在新的一天,但是您的时间是昨天的午夜之前。如果是这样,请从now
中减去一天。使用适合您情况的任何边界时间值。
if (
lt.isAfter( LocalTime.of( 23 , 55 ) )
&&
odt.toLocalTime().isBefore( LocalTime.of( 0 , 5 ) )
) {
now = now.minusDays( 1 ) ;
}
从UTC调整到所需的时区。
ZoneId z = ZoneId( "Africa/Tunis" ) ;
ZonedDateTime zdt = odt.atZoneSameInstant( z ) ;
对于Android <26,请参见 ThreeTenABP 项目。
java.time框架已内置在Java 8及更高版本中。这些类取代了麻烦的旧legacy日期时间类,例如java.util.Date
,Calendar
和SimpleDateFormat
。
目前位于Joda-Time的maintenance mode项目建议迁移到java.time类。
要了解更多信息,请参见Oracle Tutorial。并在Stack Overflow中搜索许多示例和说明。规格为JSR 310。
您可以直接与数据库交换 java.time 对象。使用符合JDBC driver或更高版本的JDBC 4.2。不需要字符串,不需要java.sql.*
类。
在哪里获取java.time类?
ThreeTen-Extra项目使用其他类扩展了java.time。该项目为将来可能在java.time中添加内容提供了一个试验场。您可能会在这里找到一些有用的类,例如Interval
,YearWeek
,YearQuarter
和more。
答案 1 :(得分:2)
您的错误在这一行:
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)。
其他建议:
Basil Bourque已经展示了使用java.time(现代Java日期和时间API)解决问题的好方法。我想向您展示,即使您愿意,也可以使用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 [欧洲/巴黎]
appendFraction
方法在该点之后采用最小和最大小数位数,因此我分别指定了3和4。根据您的需要,您可以指定最小0到最小9位的数字。
如果欧洲/巴黎没有发生这种情况,请当然替换您自己的时区。
如果您需要老式的Date
对象作为您不想立即升级的旧版API:
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);
}
2018年12月11日星期二10:12:15 CET
如果使用反向端口(ThreeTen反向端口和/或ThreeTenABP,请参见下文)从Instant
到Date
而非Date.from(inst)
转换,请使用以下命令:
return DateTimeUtils.toDate(inst);
由于Date
没有时区,因此在这种情况下无需进行时区转换。仅仅是因为我也恰好在中欧时区,所以输出与您的期望相符-这将是JVM时区中的时间。
是的,java.time在较新和较旧的Android设备上均可正常运行。它只需要至少 Java 6 。
org.threeten.bp
导入日期和时间类。java.time
。java.time
。java.time
向Java 6和7(JSR-310的ThreeTen)的反向端口。