【问题标题】:Parsing timezone from a string with offset using SimpleDateFormat使用 SimpleDateFormat 从带有偏移量的字符串中解析时区
【发布时间】:2015-10-27 09:42:06
【问题描述】:

决定问这个,因为我在 StackOverflow 中找不到类似的例子。

我想使用 SimpleDateFormat 解析日期字符串及其时区。我(希望)我仔细阅读了文档,并编写了这个复制问题的程序。

import java.text.DateFormat;
import java.util.Date;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Locale;

public class SDF {

  private static final String FORMAT = "EEE, d MMM yyyy HH:mm:ss Z";  

  public static void main(String[] args) throws ParseException {
    DateFormat formatter = new SimpleDateFormat(FORMAT, Locale.ENGLISH);
    formatter.setLenient(false);
    String dateString = args[0];

    System.out.println(" Format: " + FORMAT);
    Date date = formatter.parse(dateString);
    System.out.println(" Parsed time in millis: " + date.getTime());
    System.out.println(" Parsed timezone: " +  formatter.getTimeZone().getDisplayName());
    System.out.println(" Parsed offset: " + formatter.getTimeZone().getRawOffset() / 1000 / 60 / 60 + "hrs.");
    }
}

如果我在输入字符串中使用“EDT”或任何其他受支持的时区指示符,则在 dateFormat 对象上调用 getTimeZone() 会返回正确的时区和与 GMT 的原始偏移量。

$ java SDF "Thu, 1 Jan 2015 00:00:00 EDT"
  Parsed time in millis: 1420084800000
  Parsed timezone: Eastern Standard Time
  Parsed offset: -5hrs.

$ java SDF "Thu, 1 Jan 2015 00:00:00 PST"
  Parsed time in millis: 1420099200000
  Parsed timezone: Pacific Standard Time
  Parsed offset: -8hrs.

但是,当我使用 +0000 表示法时,日期毫秒数会正确返回为自 UTC 纪元以来的时间,但时区始终默认为我的本地时区。

$ java SDF "Thu, 1 Jan 2015 00:00:00 +0000"
  Parsed time in millis: 1420070400000
  Parsed timezone: Central European Time
  Parsed offset: 1hrs.

$ java SDF "Thu, 1 Jan 2015 00:00:00 -0800"
  Parsed time in millis: 1420099200000
  Parsed timezone: Central European Time
  Parsed offset: 1hrs.

这是否意味着我只能在输入字符串中使用“EDT”、“BST”,还是我误用了 SimpleDateFormat API?

【问题讨论】:

    标签: java date parsing timezone simpledateformat


    【解决方案1】:

    当使用“+0000”和“-0800”等时区说明符时,DateFormat 类将解析日期字符串并尊重 ZoneOffset,正如您通过比较自 Epoch 以来的毫秒所证明的那样。但是,DateFormat 的内部 TimeZone 不会改变!

    现在奇怪的地方来了。如果您使用 GMT 或 PST 等时区说明符,这实际上会在 DateFormat 的内部 TimeZone 中发生变化。我的程序证明了这一点,但我不确定这是什么意思。我相信您通过期望 DateFormat 对象的 TimeZone 反映“最后解析的”日期来滥用 API,但正如您的代码和我的代码所示,情况并非如此。事实上,DateFormat.parse()Java Doc 并没有提到格式化程序的TimeZone 将如何变化,因此我们不应该依赖这个“功能”。

    事实上,如果我们检查Date 对象上可用的API,我们会发现TimeZone 支持真的很差。请注意,在我的程序中,Date.toString() 给出相同的输出,而不管解析的输入字符串如何。 Java 很早就意识到了这一点,在 Java 1.1 中弃用了 Date.getTimezoneOffset() 方法。 结论是 Date 对象仅代表 Epoch 中的毫秒 - 不支持 TimeZone!

    这种奇怪的行为以及许多其他此类示例是您应该避免使用 Java 7 日期和时间处理类的原因。来自Oracle website on Java 7 Dates:

    [Java 7] 中的一些日期和时间类的 API 设计也很差。

    如果您能够使用 Java 8,您会发现 java.time 包中的类的行为设计得更好。我在我的程序中给出了一个例子。如果您无法迁移到 Java 8,那么 Joda time 是您的最佳选择。下面有一个 Java 8 示例,展示了即使在解析之后,TimeZone 信息如何被记住和尊重。 Joda Time 也同样有效,这两种方法都可以避免您在这里遇到的问题。

    我的程序:

    package com.company;
    
    import java.text.DateFormat;
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.time.OffsetDateTime;
    import java.time.format.DateTimeFormatter;
    import java.util.Date;
    import java.util.Locale;
    
    public class Main {
    
        private static final String FORMAT = "EEE, d MMM yyyy HH:mm:ss Z";
    
        public static void main(String[] args) throws ParseException {
            DateFormat formatter = new SimpleDateFormat(FORMAT, Locale.ENGLISH);
            formatter.setLenient(false);
            String dateString = "Thu, 1 Jan 2015 00:00:00 EDT";
            String dateString2 = "Thu, 1 Jan 2015 00:00:00 PST";
            String dateString3 = "Thu, 1 Jan 2015 00:00:00 +0000";
            String dateString4 = "Thu, 1 Jan 2015 00:00:00 -0800";
    
    
            runParseTest(dateString, formatter);
            runParseTest(dateString2, formatter);
            runParseTest(dateString3, formatter);
            runParseTest(dateString4, formatter);
    
            runJava8Test();
        }
    
        private static void runParseTest(String dateString, DateFormat formatter) throws ParseException {
            System.out.println(" Format: " + FORMAT);
            System.out.println(" Formatter time zone: " + formatter.getTimeZone().getDisplayName());
            System.out.println(" Parse String: " + dateString);
            Date date = formatter.parse(dateString);
            System.out.println(" Formatter time zone: " + formatter.getTimeZone().getDisplayName());
            System.out.println(" Parsed time in millis: " + date.getTime());
            System.out.println(" Parsed date: " +  date.toString());
            System.out.println();
        }
    
        private static void runJava8Test() {
            OffsetDateTime parsed = OffsetDateTime
                    .parse(
                            "Thu, 1 Jan 2015 00:00:00 -1300",
                            DateTimeFormatter.ofPattern(FORMAT)
                    );
    
            System.out.println(" Java 8 parsed offset: " + parsed.getOffset().toString());
        }
    }
    

    还有输出:

     Format: EEE, d MMM yyyy HH:mm:ss Z
     Formatter time zone: Greenwich Mean Time
     Parse String: Thu, 1 Jan 2015 00:00:00 EDT
     Formatter time zone: Eastern Standard Time
     Parsed time in millis: 1420084800000
     Parsed date: Thu Jan 01 04:00:00 GMT 2015
    
     Format: EEE, d MMM yyyy HH:mm:ss Z
     Formatter time zone: Eastern Standard Time
     Parse String: Thu, 1 Jan 2015 00:00:00 PST
     Formatter time zone: Pacific Standard Time
     Parsed time in millis: 1420099200000
     Parsed date: Thu Jan 01 08:00:00 GMT 2015
    
     Format: EEE, d MMM yyyy HH:mm:ss Z
     Formatter time zone: Pacific Standard Time
     Parse String: Thu, 1 Jan 2015 00:00:00 +0000
     Formatter time zone: Pacific Standard Time
     Parsed time in millis: 1420070400000
     Parsed date: Thu Jan 01 00:00:00 GMT 2015
    
     Format: EEE, d MMM yyyy HH:mm:ss Z
     Formatter time zone: Pacific Standard Time
     Parse String: Thu, 1 Jan 2015 00:00:00 -0800
     Formatter time zone: Pacific Standard Time
     Parsed time in millis: 1420099200000
     Parsed date: Thu Jan 01 08:00:00 GMT 2015
    
     Java 8 parsed offset: -13:00
    

    【讨论】:

    • 这是一个很好且全面的答案,它证实了我的观察。我没有可能切换到使用 DateTimeFormatter 或 Joda,所以我会找到另一种方法来处理这个问题。我至少可以首先控制输入字符串的格式。
    猜你喜欢
    • 2021-12-05
    • 1970-01-01
    • 1970-01-01
    • 2015-02-02
    • 2014-04-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多