【问题标题】:timezone clarification in java / androidjava / android中的时区说明
【发布时间】:2023-03-14 10:20:01
【问题描述】:

我有下面的代码

public Long getEpochTime(String dateToGetItsEpoch) throws ParseException
{
    TimeZone timeZone = TimeZone.getTimeZone("UTC");
    final String REQUEST_DATE_FORMAT = "dd/MM/yyyy h:m";

    DateFormat format = new SimpleDateFormat(REQUEST_DATE_FORMAT);
    Date localDate = format.parse(dateToGetItsEpoch);

    Calendar cal = Calendar.getInstance(timeZone);
    cal.setTime(localDate);

    format.setTimeZone(timeZone);
    final String utcTime = format.format(cal.getTime());

    Date d = cal.getTime();

    return d.getTime();
}

如果我将设备的区域设置更改为任何内容,我总是将 UTC 时间作为返回值。哪个是正确的,但是我想知道这是怎么回事?设备如何知道哪个时区是我给它的日期,以便它进行相应的计算?

【问题讨论】:

    标签: android date datetime timezone locale


    【解决方案1】:

    java.time

    Answer by Jon Skeet 是正确的。这里更新了一些代码以使用现代的 java.time 类,这些类取代了麻烦的旧日期时间类。

    格式化模式

    定义格式模式以匹配您的输入。

    顺便说一句,您的格式选择不佳。相反,我建议使用标准的 ISO 8601 格式,用于将日期时间值交换为文本。

    12 小时制与 24 小时制

    您的输入数据或格式模式存在缺陷。您使用了小写的h,这意味着在 12 小时制中一个小时的一个或两个数字(而不是 24 小时制,即大写的 HHH)。因此,除非您添加AMPM 的指示符,否则您的输入毫无意义。我会假设你错误地从你的问题代码中省略了这个。

    Locale locale = Locale.US ;
    DateTimeFormatter f = DateTimeFormatter.ofPattern( "dd/MM/uuuu h:m a" ).withLocale( locale ) ;
    

    LocalDateTime

    解析 LocalDateTime 对象之类的字符串,因为它们缺少预期时区的指示符或与 UTC 的偏移量。

    String input = "23/01/2020 4:5 PM" ;
    
    LocalDateTime ldt = LocalDateTime.parse( input , f ) ;
    

    ldt.toString(): 2020-01-23T16:05

    时刻

    我们上面得到的LocalDateTime 对象确实代表一个时刻,不是时间轴上的一个点。我们的时间是 23 日下午 4 点左右。但我们不知道这是否意味着东京、图卢兹或托莱多的下午 4 点——所有这些时刻相隔几个小时。

    要确定某个时刻,我们必须确定预期的时区。然后将该区域应用为ZoneId 以获得ZonedDateTime。然后我们就到了。

    Locale 不是时区

    我的设备的区域设置到任何地方

    Locale 与时区没有Locale 用于本地化生成的表示日期时间对象的文本。

    要本地化,请指定:

    • FormatStyle 确定字符串的长度或缩写。
    • Locale 确定:
      • 人类语言用于翻译日名、月名等。
      • 文化规范决定缩写、大写、标点、分隔符等问题。

    例子:

    Locale l = Locale.CANADA_FRENCH ;   // Or Locale.US, Locale.JAPAN, etc.
    DateTimeFormatter f = 
        DateTimeFormatter
        .ofLocalizedDateTime( FormatStyle.FULL )
        .withLocale( l )
    ;
    String output = myZonedDateTime.format( f );
    

    您可能有一位来自魁北克的工程师,他使用Locale.CANADA_FRENCH 来表示人类语言和文化规范,但在日本访问时使用Asia/Tokyo 时区来安排约会。

    ZonedDateTime

    回到你的LocalDateTime 对象。如果您确定它是为了代表突尼斯挂钟时间中的某个时刻,则应用Africa/Tunis 时区。

    ZoneId z = ZoneId.of( "Africa/Tunis" ) ;
    ZonedDateTime zdt = ldt.atZone( z ) ;
    

    你问:

    设备如何知道我给它的日期是哪个时区,以便它进行相应的计算?

    您使用了糟糕的日期时间类,未能解释缺少时区指示符或与 UTC 偏移的日期时间的概念。所以从技术上讲,你的代码是一团糟,一个黑客,在 Joda-Time 和它的继任者 java.time 之前的那些日子里是不可避免的。

    我建议花努力去理解DateCalendar 的行为。继续使用 java.time,这是业界领先的日期时间处理框架。

    【讨论】:

      【解决方案2】:

      Date 根本没有时区。 SimpleDateFormat 作为解析和格式化的默认值; Calendar 也是; Date 没有。

      鉴于以下操作顺序:

      TimeZone timeZone = TimeZone.getTimeZone("UTC");
      DateFormat format = new SimpleDateFormat(REQUEST_DATE_FORMAT);
      Date localDate = format.parse(dateToGetItsEpoch);
      
      Calendar cal = Calendar.getInstance(timeZone);
      cal.setTime(localDate);
      
      format.setTimeZone(timeZone);
      final String utcTime = format.format(cal.getTime());
      

      ...您最初使用设备的默认时区解析字符串,然后您将格式化它在UTC。请注意,Calendar 部分在这里无关紧要 - 你会得到相同的结果:

      TimeZone timeZone = TimeZone.getTimeZone("UTC");
      DateFormat format = new SimpleDateFormat(REQUEST_DATE_FORMAT);
      Date date = format.parse(dateToGetItsEpoch);
      format.setTimeZone(timeZone);
      final String utcTime = format.format(date);
      

      我个人建议在 Java 中尽可能使用 Joda Time 进行日期/时间工作,请注意。这是一个比Calendar/Date 更简洁的 API。

      【讨论】:

      • 感谢您的及时回复。如果我应用您建议的方法,如何在 epoch 中获得新的 UTC 时间?并且关于您的行“您最初是使用设备的默认时区解析字符串”..我们在这里这样做对吗?日期 localDate = format.parse(dateToGetItsEpoch);这是默认情况下发生的?
      • @tony9099:我不知道你所说的“新的 UTC 时间”是什么意思。但是,是的,当您调用format.parse 而没有在SimpleDateFormat 上指定时区(或日历)时,它将使用默认时区。
      • 我的意思是,如果我没有使用您建议的第二种方法将 'utcTime' 转换为纪元时间,我该如何将它转换为纪元时间。
      • @tony9099:再一次,你所说的“一个时代”是什么意思?你可以直接返回date.getTime()Date 没有时区 - 它总是是一个“自 unix 纪元以来的毫秒”包装器,基本上。因此,当您使用默认时区解析字符串时,它已经将其转换为该值。
      • @Johskeet,抱歉我不知道。非常感谢,现在 utcTime 已经以毫秒为单位了。感谢一百万向我澄清这一点。
      猜你喜欢
      • 2010-12-14
      • 2018-09-05
      • 2013-11-12
      • 2019-03-22
      • 1970-01-01
      • 1970-01-01
      • 2012-08-05
      • 1970-01-01
      • 2012-04-09
      相关资源
      最近更新 更多