I'm trying to parse the date returned as a value from the HTML5 datetime
input field. Try it in Opera to see an example. The date returned looks like this: 2011-05-03T11:58:01Z
.
I'd like to parse that into a Java Date or Calendar Object.
Ideally a solution should have the following things:
- No external libraries (jars)
- Handles all acceptable RFC 3339 formats
- A String should be able to be easily validated to see if it is a valid RFC 3339 date
Here is a simple method to do so. It may suit your needs.
tl;dr
ISO 8601
Actually, RFC 3339 is but a mere self-proclaimed “profile” of the actual standard, ISO 8601.
The RFC is different in that it purposely violates ISO 8601 to allow a negative offset of zero hours (
-00:00
) and gives that a semantic meaning of “offset unknown“. That semantic seems like a very bad idea to me. I advise sticking with the more sensible ISO 8601 rules. In ISO 8601, having no offset at all means the offset is unknown – an obvious meaning, whereas the RFC rule is abstruse.The modern java.time classes use the ISO 8601 formats by default when parsing/generating strings.
Your input string represents a moment in UTC. The
Z
on the end is short forZulu
and means UTC.Instant
(notDate
)The modern class
Instant
represents a moment in UTC. This class replacesjava.util.Date
, and uses a finer resolution of nanoseconds rather than milliseconds.ZonedDateTime
(notCalendar
)To see that same moment through the wall-clock time used by the people of a certain region (a time zone), apply a
ZoneId
to get aZonedDateTime
. This classZonedDateTime
replaces thejava.util.Calendar
class.Converting
I strongly recommend avoiding the legacy date-time classes when possible. But if you must inter-operate with old code not yet updated to java.time, you may convert back-and-forth. Call new methods added to the old classes.
Instant
replacesjava.util.Date
.ZonedDateTime
replacesGregorianCalendar
.If you have a
java.util.Calendar
that is actually aGregorianCalendar
, cast.Bulleted concerns
Regarding your Question’s specific issues…
The java.time classes are built into Java 8, 9, 10, and later. An implementation is also included in later Android. For earlier Java and earlier Android, see the next section of this Answer.
The various java.time classes handle every ISO 8601 format I know of. They even handle some formats that mysteriously disappeared from later editions of the standard.
For other formats, see the
parse
andtoString
methods of the various classes such asLocalDate
,OffsetDateTime
, and so on. Also, search Stack Overflow as there are many examples and discussions on this topic.To validate input strings, trap for
DateTimeParseException
.About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as
java.util.Date
,Calendar
, &SimpleDateFormat
.The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for
java.sql.*
classes.Where to obtain the java.time classes?
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as
Interval
,YearWeek
,YearQuarter
, and more.Just found that google implemented Rfc3339 parser in Google HTTP Client Library
https://github.com/google/google-http-java-client/blob/dev/google-http-client/src/main/java/com/google/api/client/util/DateTime.java
Tested. It works well to parse varies sub seconds time fragment.
Maybe not the most elegant way, but certainly working one I recently made:
So, in principle this would be done using different SimpleDateFormat patterns.
Here a list of patterns for the individual declarations in RFC 3339:
yyyy
MM
dd
HH
mm
ss
.SSS
(S
means millisecond, though - it is not clear what would happen if there are more or less than 3 digits of these.)+02:00
seems to be not supported - instead it supports the formats+0200
,GMT+02:00
and some named time zones usingz
andZ
.)'Z'
(not supporting other time zones) - you should useformat.setTimezone(TimeZone.getTimeZone("UTC"))
before using this.)HH:mm:ss
orHH:mm:ss.SSS
.HH:mm:ss'Z'
orHH:mm:ss.SSS'Z'
.yyyy-MM-dd
yyyy-MM-dd'T'HH:mm:ss'Z'
oryyyy-MM-dd'T'HH:mm:ss.SSS'Z'
As we can see, this seems not to be able to parse everything. Maybe it would be a better idea to implement an
RFC3339DateFormat
from scratch (using regular expressions, for simplicity, or parsing by hand, for efficiency).