SimpleDateFormat parsing date with 'Z' literal [duplicate]
I am trying to parse a date that looks like this:
2010-04-05T17:16:00Z
This is a valid date per http://www.ietf.org/rfc/rfc3339.txt. The 'Z' literal (quote) "imply that UTC is the preferred reference point for the specified time."
If I try to parse it using SimpleDateFormat and this pattern:
yyyy-MM-dd'T'HH:mm:ss
it will be parsed as a Mon Apr 05 17:16:00 EDT 2010
SimpleDateFormat
is unable to parse the string with these patterns:
yyyy-MM-dd'T'HH:mm:ssz
yyyy-MM-dd'T'HH:mm:ssZ
I can explicitly set the TimeZone to use on the SimpleDateFormat
to get the expected output, but I don't think that should be necessary. Is there something I am missing? Is there an alternative date parser?
Java doesn't parse ISO dates correctly.
Similar to McKenzie's answer.
Just fix the Z
before parsing.
Code
String string = "2013-03-05T18:05:05.000Z";
String defaultTimezone = TimeZone.getDefault().getID();
Date date = (new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")).parse(string.replaceAll("Z$", "+0000"));
System.out.println("string: " + string);
System.out.println("defaultTimezone: " + defaultTimezone);
System.out.println("date: " + (new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ")).format(date));
Result
string: 2013-03-05T18:05:05.000Z
defaultTimezone: America/New_York
date: 2013-03-05T13:05:05.000-0500
The date you are parsing is in ISO 8601 format.
In Java 7 the pattern to read and apply the timezone suffix should read yyyy-MM-dd'T'HH:mm:ssX
tl;dr
Instant.parse ( "2010-04-05T17:16:00Z" )
ISO 8601 Standard
Your String complies with the ISO 8601 standard (of which the mentioned RFC 3339 is a profile).
Avoid java.util.Date
The java.util.Date and .Calendar classes bundled with Java are notoriously troublesome. Avoid them.
Instead use either the Joda-Time library or the new java.time package in Java 8. Both use ISO 8601 as their defaults for parsing and generating string representations of date-time values.
java.time
The java.time framework built into Java 8 and later supplants the troublesome old java.util.Date/.Calendar classes. The new classes are inspired by the highly successful Joda-Time framework, intended as its successor, similar in concept but re-architected. Defined by JSR 310. Extended by the ThreeTen-Extra project. See the Tutorial.
The Instant
class in java.time represents a moment on the timeline in UTC time zone.
The Z
at the end of your input string means Zulu
which stands for UTC
. Such a string can be directly parsed by the Instant
class, with no need to specify a formatter.
String input = "2010-04-05T17:16:00Z";
Instant instant = Instant.parse ( input );
Dump to console.
System.out.println ( "instant: " + instant );
instant: 2010-04-05T17:16:00Z
From there you can apply a time zone (ZoneId
) to adjust this Instant
into a ZonedDateTime
. Search Stack Overflow for discussion and examples.
If you must use a java.util.Date
object, you can convert by calling the new conversion methods added to the old classes such as the static method java.util.Date.from( Instant )
.
java.util.Date date = java.util.Date.from( instant );
Joda-Time
Example in Joda-Time 2.5.
DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" ):
DateTime dateTime = new DateTime( "2010-04-05T17:16:00Z", timeZone );
Convert to UTC.
DateTime dateTimeUtc = dateTime.withZone( DateTimeZone.UTC );
Convert to a java.util.Date if necessary.
java.util.Date date = dateTime.toDate();
In the pattern, the inclusion of a 'z' date-time component indicates that timezone format needs to conform to the General time zone "standard", examples of which are Pacific Standard Time; PST; GMT-08:00
.
A 'Z' indicates that the timezone conforms to the RFC 822 time zone standard, e.g. -0800
.
I think you need a DatatypeConverter ...
@Test
public void testTimezoneIsGreenwichMeanTime() throws ParseException {
final Calendar calendar = javax.xml.bind.DatatypeConverter.parseDateTime("2010-04-05T17:16:00Z");
TestCase.assertEquals("gotten timezone", "GMT+00:00", calendar.getTimeZone().getID());
}
According to last row on the Date and Time Patterns table of the Java 7 API
X Time zone ISO 8601 time zone -08; -0800; -08:00
For ISO 8601 time zone you should use:
- X for (-08 or Z),
- XX for (-0800 or Z),
- XXX for (-08:00 or Z);
so to parse your "2010-04-05T17:16:00Z" you can use either "yyyy-MM-dd'T'HH:mm:ssX" or "yyyy-MM-dd'T'HH:mm:ssXX" or "yyyy-MM-dd'T'HH:mm:ssXXX" .
System.out.println(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX").parse("2010-04-05T17:16:00Z"));
System.out.println(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXX").parse("2010-04-05T17:16:00Z"));
System.out.println(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX").parse("2010-04-05T17:16:00Z"));
will correctly print out 'Mon Apr 05 13:16:00 EDT 2010'
The 'X' only works if partial seconds are not present: i.e. SimpleDateFormat pattern of
"yyyy-MM-dd'T'HH:mm:ssX"
Will correctly parse
"2008-01-31T00:00:00Z"
but
"yyyy-MM-dd'T'HH:mm:ss.SX"
Will NOT parse
"2008-01-31T00:00:00.000Z"
Sad but true, a date-time with partial seconds does not appear to be a valid ISO date: http://en.wikipedia.org/wiki/ISO_8601
Under Java 8 use the predefined DateTimeFormatter.ISO_DATE_TIME
DateTimeFormatter formatter = DateTimeFormatter.ISO_DATE_TIME;
ZonedDateTime result = ZonedDateTime.parse("2010-04-05T17:16:00Z", formatter);
I guess its the easiest way
The time zone should be something like "GMT+00:00" or 0000 in order to be properly parsed by the SimpleDateFormat - you can replace Z with this construction.
Since java 8 just use ZonedDateTime.parse("2010-04-05T17:16:00Z")
The restlet project includes an InternetDateFormat class that can parse RFC 3339 dates.
Restlet InternetDateFormat
Though, you might just want to replace the trailing 'Z' with "UTC" before you parse it.
I provide another answer that I found by api-client-library
by Google
try {
DateTime dateTime = DateTime.parseRfc3339(date);
dateTime = new DateTime(new Date(dateTime.getValue()), TimeZone.getDefault());
long timestamp = dateTime.getValue(); // get date in timestamp
int timeZone = dateTime.getTimeZoneShift(); // get timezone offset
} catch (NumberFormatException e) {
e.printStackTrace();
}
Installation guide,
https://developers.google.com/api-client-library/java/google-api-java-client/setup#download
Here is API reference,
https://developers.google.com/api-client-library/java/google-http-java-client/reference/1.20.0/com/google/api/client/util/DateTime
Source code of DateTime
Class,
https://github.com/google/google-http-java-client/blob/master/google-http-client/src/main/java/com/google/api/client/util/DateTime.java
DateTime
unit tests,
https://github.com/google/google-http-java-client/blob/master/google-http-client/src/test/java/com/google/api/client/util/DateTimeTest.java#L121
With regards to JSR-310 another project of interest might be threetenbp.
JSR-310 provides a new date and time library for Java SE 8. This project is the backport to Java SE 6 and 7.
In case you are working on an Android project you might want to checkout the ThreeTenABP library.
compile "com.jakewharton.threetenabp:threetenabp:${version}"
JSR-310 was included in Java 8 as the java.time.* package. It is a full replacement for the ailing Date and Calendar APIs in both Java and Android. JSR-310 was backported to Java 6 by its creator, Stephen Colebourne, from which this library is adapted.
精彩评论