问题
Decided to ask this, as I couldn't find a similar example in StackOverflow.
I want to parse a date string and its timezone using SimpleDateFormat. I (hope) I read the documentation carefully, and wrote this program that replicates the issue.
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.");
}
}
If I use "EDT" or any other supported timezone designator in my input string, then the calling getTimeZone() on the dateFormat object returns the correct timezone and the raw offset from 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.
However when I use the +0000 notation, the date millisconds is correctly returned as the time since epoch in UTC, but the timezone always defaults to my local timezone.
$ 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.
Does this mean I can only use the "EDT", "BST" in the input strings or am I misusing the SimpleDateFormat API?
回答1:
When using time zone specifiers such as "+0000" and "-0800", the DateFormat class will parse the date string and will respect the ZoneOffset, as you have proved by comparing the millis since the Epoch. However, the internal TimeZone of the DateFormat will not be changed!
Now here comes the odd bit. If you use time zone specifiers like GMT or PST, this will actually change in internal TimeZone of your DateFormat. This is demonstrated by my program, but I'm not sure what the point of this is. I believe you are misusing the API by expecting the TimeZone of the DateFormat object to reflect the "last parsed" Date, but as both your code and mine shows, this is not the case. In fact, the Java Doc for DateFormat.parse()
mentions nothing about how the TimeZone
of the formatter will change, therefore we should not rely on this "feature".
In fact, if we inspect the API available on the Date
object, we find that TimeZone support is really very poor. Note in my program, Date.toString()
gives the same output regardless of the parsed input String. Java recognized this very early on by deprecating the Date.getTimezoneOffset()
method in Java 1.1. The conclusion is that the Date
object only represents millis from the Epoch - no TimeZone support!
This odd behavior, and many other such examples, is why you should avoid the Java 7 Date and Time handling classes. From the Oracle website on Java 7 Dates:
Some of the date and time classes [in Java 7] also exhibit quite poor API design.
If you are able to use Java 8, you will find the behavior of classes in the java.time
package much better designed. I have given one example in my program. If you cannot move to Java 8, then Joda time is the way to go. There is a Java 8 example below showing how even after parsing, TimeZone information is remembered and respected. Joda Time works just as well, and either of these approaches will avoid the issues you are facing here.
My program:
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());
}
}
And the output:
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
来源:https://stackoverflow.com/questions/31818533/parsing-timezone-from-a-string-with-offset-using-simpledateformat