决定问这个,因为我在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?
答案 0 :(得分:1)
当使用时区说明符(如“+0000”和“-0800”)时,DateFormat类将解析日期字符串并将尊重ZoneOffset,正如您通过比较自Epoch以来的毫秒数所证明的那样。但是,DateFormat的内部TimeZone将不会更改!
现在来了奇怪的一点。如果您使用GMT或PST等时区说明符,则实际上会更改DateFormat的内部TimeZone。我的程序证明了这一点,但我不确定这是什么意思。我相信你滥用API是因为期望DateFormat对象的TimeZone反映“最后解析的”日期,但是正如你的代码和我的代码都显示的那样,情况并非如此。实际上,DateFormat.parse()
的{{3}}未提及格式化程序的TimeZone
将如何更改,因此我们不应该依赖此“功能”。
事实上,如果我们检查Date
对象上可用的API,我们发现TimeZone支持非常糟糕。请注意,在我的程序中,无论解析的输入字符串如何,Date.toString()
都会提供相同的输出。 Java很早就通过弃用Java 1.1中的Date.getTimezoneOffset()
方法来认识到这一点。 结论是Date
对象仅表示来自Epoch的毫秒 - 没有TimeZone支持!
这种奇怪的行为以及许多其他此类示例是您应该避免Java 7日期和时间处理类的原因。来自Java Doc
[Java 7]中的一些日期和时间类也表现出相当差的API设计。
如果您能够使用Java 8,您会发现java.time
包中的类的行为设计得更好。我在我的程序中给出了一个例子。如果您无法迁移到Java 8,那么Oracle website on Java 7 Dates:就是您的选择。下面有一个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