背景
我正在使用threetenbp backport对于 Android ( here ),处理各种与时间相关的数据操作。
其中之一是将时间转换为不同的时区(当前为 UTC 并返回)。
我知道如果你使用类似的东西这是可能的:
LocalDateTime now = LocalDateTime.now();
LocalDateTime nowInUtc = now.atZone(ZoneId.systemDefault()).withZoneSameInstant(ZoneId.of("UTC")).toLocalDateTime();
这工作得很好,而且也很容易做相反的事情。
问题
我试图避免初始化库,因为它会向其中加载相当大的区域文件。我已经弄清楚如何在没有这个的情况下处理各种日期/时间相关的操作,除了转换为 UTC 并返回的情况。
我得到的结果与正确的转换相差整整 1 小时的错误。
我尝试过的
这是我发现并尝试过的:
// getting the current time, using current time zone:
Calendar cal = Calendar.getInstance();
LocalDateTime now = LocalDateTime.of(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH) + 1, cal.get(Calendar.DAY_OF_MONTH), cal.get(Calendar.HOUR_OF_DAY),
cal.get(Calendar.MINUTE), cal.get(Calendar.SECOND), cal.get(Calendar.MILLISECOND) * 1000000);
//the conversion itself, which is wrong by 1 hour in my tests:
LocalDateTime alternativeNowInUtc = now.atZone(ZoneOffset.ofTotalSeconds(TimeZone.getDefault().getRawOffset() / 1000)).withZoneSameInstant(ZoneId.ofOffset("UTC", ZoneOffset.ofHours(0))).toLocalDateTime();
问题
我写的到底有什么问题?如何在不初始化库的情况下获得转换时间的替代代码?
给定 LocalDateTime 实例作为输入,如何将其从当前时区转换为 UTC,以及从 UTC 转换为当前时区?
最佳答案
发生这种情况的原因可能是您的 JVM 的默认时区采用夏令时 (DST)。
要获得正确的偏移量,您应该检查时区是否处于 DST 并将其添加到偏移量中:
Calendar cal = Calendar.getInstance();
TimeZone zone = TimeZone.getDefault();
// if in DST, add the offset, otherwise add zero
int dst = zone.inDaylightTime(cal.getTime()) ? zone.getDSTSavings() : 0;
int offset = (zone.getRawOffset() + dst) / 1000;
LocalDateTime alternativeNowInUtc = now.atZone(ZoneOffset.ofTotalSeconds(offset))
.withZoneSameInstant(ZoneId.ofOffset("UTC", ZoneOffset.ofHours(0)))
.toLocalDateTime();
将 nowInUtc
创建为 LocalDateTime
的另一种方法是从 Calendar
创建一个 Instant
:
LocalDateTime nowInUtc = Instant.ofEpochMilli(cal.getTimeInMillis())
.atOffset(ZoneOffset.ofHours(0)).toLocalDateTime();
实际上,您根本不需要Calendar
,只需使用Instant.now()
即可获取当前时刻:
LocalDateTime nowInUtc = Instant.now().atOffset(ZoneOffset.ofHours(0)).toLocalDateTime();
或者更短,直接使用 OffsetDateTime
:
LocalDateTime nowInUtc = OffsetDateTime.now(ZoneOffset.ofHours(0)).toLocalDateTime();
不确定其中是否有任何加载时区数据,这取决于您测试。
而且我认为可以使用常量ZoneOffset.UTC
代替ZoneOffset.ofHours(0)
,因为它不会也加载 tz 数据(但我还没有测试过)。
最终解决方案
假设默认时区是以色列(TimeZone.getDefault()
是 Asia/Jerusalem
):
// April 11th 2018, 3 PM (current date/time in Israel)
LocalDateTime now = LocalDateTime.of(2018, 4, 11, 15, 0, 0);
TimeZone zone = TimeZone.getDefault();
// translate DayOfWeek values to Calendar's
int dayOfWeek;
switch (now.getDayOfWeek().getValue()) {
case 7:
dayOfWeek = 1;
break;
default:
dayOfWeek = now.getDayOfWeek().getValue() + 1;
}
// get the offset used in the timezone, at the specified date
int offset = zone.getOffset(1, now.getYear(), now.getMonthValue() - 1,
now.getDayOfMonth(), dayOfWeek, now.getNano() / 1000000);
ZoneOffset tzOffset = ZoneOffset.ofTotalSeconds(offset / 1000);
// convert to UTC
LocalDateTime nowInUtc = now
// conver to timezone's offset
.atOffset(tzOffset)
// convert to UTC
.withOffsetSameInstant(ZoneOffset.UTC)
// get LocalDateTime
.toLocalDateTime();
// convert back to timezone
LocalDateTime localTime = nowInUtc
// first convert to UTC
.atOffset(ZoneOffset.UTC)
// then convert to your timezone's offset
.withOffsetSameInstant(tzOffset)
// then convert to LocalDateTime
.toLocalDateTime();
关于android - 如何在不加载区域的情况下将 LocalDateTime 与 UTC 相互转换?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49770822/