android - 如何在不加载区域的情况下将 LocalDateTime 与 UTC 相互转换?

标签 android timezone utc threetenbp

背景

我正在使用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/

相关文章:

android - 单击抽屉菜单后,重新创建关闭抽屉的 Activity

java - 将上下文传递给 ListView 的自定义适配器

ruby-on-rails - 在单个模型上设置default_timezone

Javascript setUTCMonth 不适用于 11 月

java - 如何间隔选项卡布局文本

android - 使用 Eclipse "Play"按钮调试游戏服务

java - 使用 Java 转换 ColdFusion 中的时区但无法获取新时间

java - new Date() 和日历日期之间的区别

ruby-on-rails - rails 4 : wrong time in edit view with string field and TimeZone per request

sql-server - SQL 服务器 : Convert Between UTC and Local Time Precisely