转换后的 unix 时间戳加上时区(以秒为单位)在 Android 模拟器上给出了真实的本地日期时间,但在真实设备上却没有?

Converted unix timestamp with a timezone addition in seconds gives a true local date time on Android emulator but not in real device?

我从 UNIX 时间戳中的 Web 服务获取了一个日期。我将它乘以 1000L 然后我以秒为单位添加时区(也由网络服务提供)乘以 1000 以根据应用程序 运行 而不是 UTC 日期的国家/地区获取日期。 在模拟器中,提供的日期时间是正确的,但当我在真实设备上测试时,它为我提供了 1 小时以上的时间,这与当地时间不符。问题出在哪里?

long numberOfsecondsRise = json.getJSONObject("city").getInt("timezone");

long res=(json.getJSONObject("city").getLong("sunrise")*1000L +numberOfsecondsRise*1000) ;
 Date rise=new java.util.Date(res);
 DateFormat dfa = DateFormat.getTimeInstance();
 sunFiled.setText(getResources().getString(R.string.sunrise)+": " + dfa.format(rise));

Date (long date) 构造函数 文档 说:

Allocates a Date object and initializes it to represent the specified number of milliseconds since the standard base time known as "the epoch", namely January 1, 1970, 00:00:00 GMT.

这意味着值应该是 UTC。 格式化显示日期时,必须应用以秒为单位的时间偏移量。

long numberOfsecondsRise = json.getJSONObject("city").getInt("timezone");
Date rise = new java.util.Date(json.getJSONObject("city").getLong("sunrise") * 1000L);

int offsetMinutes = numberOfsecondsRise / 60;
String sign = (offsetMinutes < 0 ? "-" : "+");
offsetMinutes = Math.abs(offsetMinutes);
String timeZoneID = String.format("GMT%s%d:%02d", sign, offsetMinutes / 60, offsetMinutes % 60);

DateFormat dfa = DateFormat.getTimeInstance();
dfa.setTimeZone(TimeZone.getTimeZone(timeZoneID));
sunFiled.setText(getResources().getString(R.string.sunrise) + ": " + dfa.format(rise));

java.time 和 ThreeTenABP

考虑使用 java.time,现代 Java 日期和时间 API,作为您的时间工作。如果 minSDK 低于 API 级别 26,那么通过向后移植,我会回到那个。先上代码:

    DateTimeFormatter timeFormatter
            = DateTimeFormatter.ofLocalizedTime(FormatStyle.MEDIUM);

    long sunriseUnixTs = 1_589_581_234;
    ZonedDateTime sunriseApplicationTz = Instant.ofEpochSecond(sunriseUnixTs)
            .atZone(ZoneId.systemDefault());

    System.out.println("Sunrise: " + sunriseApplicationTz.format(timeFormatter));

此示例片段在我的时区和语言环境中的输出:

Sunrise: 03.50.34

我发现 java.time 很棒的一件事是代码明确表示我们在应用程序 运行 所在的 JVM 的默认时区中获取时间。

你的代码出了什么问题?

添加您所查询城市的时区偏移是错误的。 Unix 时间戳与时区无关。因此,如果您乘以 1000 并提供给 new Date(long),您将得到一个包含正确时间点的 Date。如果添加非零偏移量,则会得到错误的时间点。你的模拟器给了你预期的结果,那为什么呢?这可能是因为 JSON 的偏移量为 0(零),或者因为模拟器使用与您预期不同的默认时区来平衡错误。

问题:java.time 不需要 Android API 26 级吗?

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

  • 在 Java 8 和更新的 Android 设备上(从 API 级别 26)内置了现代 API。
  • 非Android Java 6 和 7 获得 ThreeTen Backport,现代 类 的 backport(ThreeTen 用于 JSR 310;请参阅底部的链接)。
  • 在(较旧的)Android 使用 ThreeTen Backport 的 Android 版本。它叫做 ThreeTenABP。并确保使用子包从 org.threeten.bp 导入日期和时间 类。

链接