繁体   English   中英

解析本地日期/时间的便携式方法(带有隐式时区)

[英]Portable way to parse local date/time (with implicit time zone)

我需要解析包含本地日期和时间的字符串。 时区未明确指定,但时间戳是特定地点的本地时间。

挑战是:

  • 我需要与 Java 7 兼容,这排除了 Java 8 引入的一些漂亮功能。
  • Java 7 兼容性主要用于较早的 Android 版本,但代码将在 JRE 和 Android 上运行。
  • 时间要么是标准时间,要么是 DST,具体取决于一年中的时间。 因此,简单的 UTC 偏移量将不起作用,因为它每年来回跳动一个小时。

(这也意味着 DST 到标准时间切换期间的时间戳是模棱两可的,但对于那个特定的极端情况,我可以忍受一个小时的不准确。)

这是我得到的结果:

/*
 * Let timestampString be the date/time string, examples:
 * "2023-01-28 20:36"
 * "2022-07-07 13:37"
 */
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm zzzz");
Date date = dateFormat.parse(timestampString + " CET");
System.out.println("Timestamp: " + dateFormat.format(date));

问题:这将所有内容都解释为中欧标准时间,全年无休 - 在夏季,时间偏差一小时。

使用诸如Europe/Brussels之类的时区名称而不是CET (它与 Java 8 上的DateTimeFormatterZonedDateTime一起使用)不起作用,因为无法识别该字符串。

Java 7 是否提供类似的东西? (最好不必求助于外部库。)

两个通用选项:

选项 1:使用 Java 8+ 类,API 在 Android 上脱糖

优点:面向未来; 不会遭受 Java 的Date和相关类中的错误; 给你 一些 Java 8 个 API

缺点:将在 API 26 (Android 8) 之前的 Android 上运行,但不能在 JRE7 上运行; 可能需要工具链升级(无论如何你最终都需要这样做)

这是首选方式,至少如果您不必支持旧桌面 Java 版本。 Java 8 对 Android 的支持(至少部分支持)是通过称为脱糖的过程完成的。

首先,确保您至少使用 Gradle 6.1.1 和 Android Gradle 插件的 4.0.0 版。

您的build.gradle不应指定buildToolsVersion除非您需要一个特定的版本(然后需要为 29.0.2 或更高版本)。

然后将以下内容添加到您的build.gradle

android {
    defaultConfig {
        multiDexEnabled true
    }

    compileOptions {
        coreLibraryDesugaringEnabled true
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.8'
}

版本 1.1.8 等同于 1.2.2 (谷歌在 2023 年 1 月推荐的版本),但适用于旧版本的 R8/D8 编译器(我在 1.2.2 上遇到错误,但 1.1.8 有效)。

到目前为止的所有更改仅适用于 Android 版本。 对于您可能在其上运行代码的其他平台,没有任何变化。

这允许您运行以下 Java 代码:

DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
    .withZone(ZoneId.of("Europe/Brussels"));
try {
    ZonedDateTime zonedDate = ZonedDateTime.parse(rawDate, formatter);
    System.out.println("Timestamp: " + zonedDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm zzzz")))
} catch (DateTimeParseException e) {
    // do whatever is necessary here
}

如果您需要将其传递给需要Date的对象,您可以使用Date.from(zonedDate.toInstant())进行转换。

选项 2:使用 Java 7 兼容类

优点:适用于任何 Java 7 API,无论是台式机还是 Android

缺点:不是面向未来的(因为您使用的是已弃用的类); 仍然存在 Java 的Date和相关类中的所有错误; 将解决此特定的 API 不兼容问题,但不会解决其他问题

DateFormat有一个setTimeZone()方法,它接受一个TimeZone参数(反过来,可以从Europe/Brussels等字符串生成)。 设置它将导致日期/时间字符串被解析为该时区的本地时间,并自动调整 DST。 不需要 append 任何被解析的字符串。

SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");
dateFormat.setTimeZone(TimeZone.getTimeZone("Europe/Brussels"));
Date date = dateFormat.parse(timestampString);
/*
 * dateFormat no longer contains a time zone, so use a separate format
 * for output (with the time zone)
 */
SimpleDateFormat dateFormatOut = new SimpleDateFormat("yyyy-MM-dd HH:mm zzzz");
dateFormatOut.setTimeZone(TimeZone.getTimeZone("Europe/Brussels"));
System.out.println("Timestamp: " + dateFormatOut.format(date));

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM