【问题标题】:Can't convert from local date to London date object无法从本地日期转换为伦敦日期对象
【发布时间】:2020-10-05 22:14:13
【问题描述】:

Android Studio 4.0、Java 6。 我的 GMT 是 GMT + 03:00

 defaultConfig {
        minSdkVersion 17
        targetSdkVersion 28


 const val LONDON_TIME_ZONE_ID = "Europe/London"
const val DEFAULT_DATE_JSON_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.000'Z'"

    fun fromDateToLondonDate(localDate : Date) : Date? {
        val dateLondonAsString = fromDate2LondonDateAsString(localDate)
        val timeZoneLondon : TimeZone = TimeZone.getTimeZone(LONDON_TIME_ZONE_ID)
        val dateLondon = getDateFromString(dateLondonAsString, timeZoneLondon)
        if (BuildConfig.DEBUG)
            Log.d("", "fromDateToLondonDate:" +
                    "\nlocalDate = $localDate"+
                    "\ndateLondonAsString = $dateLondonAsString"+
                    "\ndateLondon = $dateLondon")
        return dateLondon
    }

    fun fromDate2LondonDateAsString(date : Date) : String? {
        val timeZoneLondon : TimeZone = TimeZone.getTimeZone(LONDON_TIME_ZONE_ID)
        val formatter: DateFormat = SimpleDateFormat(DEFAULT_DATE_JSON_FORMAT)
        formatter.setTimeZone(timeZoneLondon)
        val dateAsString : String = formatter.format(date)
        return dateAsString
    }

    fun getDateFromString(str: String?, tz: TimeZone?): Date? {
        return try {
            val sdf = SimpleDateFormat(DEFAULT_DATE_JSON_FORMAT);
            sdf.setTimeZone(tz)
            val date = sdf.parse(str)
            return date
        } catch (e: ParseException) {
            //e.printStackTrace();
            null
        }
    }

这里的结果:

fromDateToLondonDate:
 localDate = Tue Jun 16 13:14:59 GMT+03:00 2020
 dateLondonAsString = 2020-06-16T11:14:59.000Z
 dateLondon = Tue Jun 16 13:14:59 GMT+03:00 2020

如您所见,当地时间是 Tue Jun 16 13:14:59 GMT+03:00 2020 并成功转换为伦敦日期作为字符串 -> dateLondonAsString = 2020-06-16T11:14:59.000Z

很好。 但我需要将字符串转换为伦敦日期。我使用 getDateFromString 方法,结果不正确:

dateLondon = 2020 年 6 月 16 日星期二 13:14:59 GMT+03:00

正确的伦敦日期必须是:Tue Jun 16 11:14:59 GMT+03:00 2020

为什么getDateFromString 不能正确从字符串转换为日期?

【问题讨论】:

  • 我建议不要使用java.util.Date,它已经过时了。其中之一是对偏移量和区域的麻烦处理。可以用java.time吗?
  • @deHaar 这是旧的 Android 项目。而且我不能使用java.time。我只能使用 java.util.Date
  • 好的,但是您可以导入 backport 以获得此功能...也请参阅 this question (How to use ThreeTenABP in Android)
  • 这是 Kotlin,不是 Java。

标签: android kotlin timezone java.util.date datetime-conversion


【解决方案1】:

java.time 和 ThreeTenABP

要获取具有特定时区的日期时间对象,请使用现代 Java 日期和时间 API java.time。我正在写 Java 代码,这是我能做的,我相信你会翻译:

    ZoneId london = ZoneId.of("Europe/London");

    Date date = getOldfashionedDateFromSomewhere();
    System.out.println("Original Date: " + date);

    ZonedDateTime dateTimeLondon = date.toInstant().atZone(london);
    System.out.println("Date-time in London: " + dateTimeLondon);

示例输出:

Original Date: Tue Jun 16 13:14:59 GMT+03:00 2020
Date-time in London: 2020-06-16T11:14:59+01:00[Europe/London]

Date 没有,因为不能有时区。是的,我知道,当您打印它时,它会隐式调用其toString 方法,它会打印一个时区。发生的情况是它会获取 JVM 的默认时区并将其用于呈现字符串。因此,只要您的 JVM 的时区设置为 GMT+03:00,您的所有 Date 对象将始终打印该时区。

所以如果(在另一个项目中)我需要带时区的日期 更好的方法是使用 java.time.ZonedDateTime (java 8) ?

这至少是我推荐的方式。正如我所说,使用Date 不是一种方法。一些方法是:

  1. 穷人和老掉牙的方式:使用GregorianCalendar
  2. 更好的方法:使用来自 Joda-Time 的DateTime。不过,如果接受外部依赖,我更喜欢 ThreeTenABP。
  3. 好方法:ZonedDateTime 来自 java.time。
  4. 进阶方式:使用Time4J。我没有经验,所以我宁愿不推荐也不反对。如果您的项目有超出 java.time 提供的要求,我当然会研究这个选项。

你的代码出了什么问题

首先,您可能不应该将时区设置为 GMT+03:00。虽然您的时区现在使用这个 GMT 偏移量,但这并不意味着它总是有,也不意味着它总是会。要获得历史和未来日期的正确结果,请使用实时区域 ID,例如非洲/内罗毕或欧洲/莫斯科,即 区域/城市 格式。就像您在英国时间使用Europe/London 一样(幸运的是不是GMT+00:00)。

我已经提到了第二点:您不能将 Date 用于带有时区的日期和时间,因为 Date 没有时区。 Date 是一个时间点,不多也不少。

接下来,2020-06-16T11:14:59.000Z 在伦敦的时间是错误的。 11:14:59 对于一天中的时间是正确的。 Z 表示 UTC 或与 UTC 的偏移量 0,这是错误的,因为英国使用夏令时 (DST),因此在 6 月的偏移量为 +01:00(正如我上面代码的输出所示)。偏移量Z 的时间应该是 10:14:59。换句话说,你的时间是 1 小时。 Z 的定义是 ISO 8601 标准的一部分。您的 JSON 格式是 ISO 8601 格式。我在底部包含一个链接。由于Z 是一个偏移量,因此您应该始终对其进行格式化和解析,并且从不将其硬编码为格式模式字符串中的文字。

您从字符串转换回Date 会出现相同的错误,因此它与转换为String 的错误相平衡,并且您成功地获得了等效的Date 对象。

这是旧的 Android 项目。而且我不能使用java.time。我只能使用 java.util.Date

您当然也可以在旧的 Android 项目中为旧的 Android 版本使用 java.time。

  • 在 Java 8 及更高版本以及更新的 Android 设备(从 API 级别 26 起)中,现代 API 是内置的。
  • 在非 Android 的 Java 6 和 7 中,获取 ThreeTen Backport,这是现代类的后向端口(对于 JSR 310,ThreeTen;请参阅底部的链接)。
  • 在(较旧的)Android 上使用 ThreeTen Backport 的 Android 版本。它被称为 ThreeTenABP。并确保从 org.threeten.bp 导入日期和时间类以及子包。

链接

【讨论】:

  • 所以如果(在另一个项目中)我需要带时区的日期,更好的方法是使用 java.time.ZonedDateTime (java 8)?
猜你喜欢
  • 2018-07-07
  • 2015-12-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-14
  • 1970-01-01
相关资源
最近更新 更多