This seems to be a common question with many different answers. Before you answer, I have used both joda-time and atomdate and they work great. My interest here is not what library to use but instead a clarification on how RFC pattern should be defined in java.
From my understanding and this answer RFC 3339 is a profile of ISO 8601. PHP clearly defines the RFC 3339 datetime pattern to be Y-m-d\TH:i:sP
. If we were to transfer this definition to java 7 (to my knowledge) we would end up with this (which is also referred to in this answer):
// example "2005-08-15T15:52:01+00:00"
pattern = "yyyy-MM-dd'T'HH:mm:ssXXX";
However, several stack overflow answers like this one point to one of these (or both) as being the correct pattern for RFC 3339
// example "2016-11-01T20:44:39Z"
pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'";
// example "1937-01-01T12:00:27.87Z"
pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
To further complicate matters the official RFC 3339 documentation lists all of these following examples (I've added in what I think would be their corresponding patterns):
// 1996-12-19T16:39:57-08:00
pattern = "yyyy-MM-dd'T'HH:mm:ssXXX";
// 1990-12-31T23:59:60Z
pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'";
// 1990-12-31T15:59:60-08:00
pattern = "yyyy-MM-dd'T'HH:mm:ssXXX";
// 1937-01-01T12:00:27.87+00:20
pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX";
Side note: Android does not support the XXX
pattern for timezones but you can use ZZZZZ
instead as per this answer.
I think part of what's confusing me is I've always seen RFC 822 and RFC 2822 specifically referred to by one pattern each, so I assumed RFC 3339 could also be boiled down to a single pattern match:
static String RFC_822 = "EEE, dd MMM yy HH:mm:ss zzz";
static String RFC_2822 = "EEE, dd MMM yyyy HH:mm:ss zzz";
Unlike in php, RFC 3339 cannot be represented in java using only a single matching expression. Instead all of these are valid RFC 3339 patterns and must be checked when parsing a datetime String via SimpleDateFormat
:
static String[] RFC_3339_VARIANTS = {
"yyyy-MM-dd'T'HH:mm:ss'Z'",
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",
"yyyy-MM-dd'T'HH:mm:ssXXX",
"yyyy-MM-dd'T'HH:mm:ss.SSSXXX"
};
To complicate matters SimpleDateFormat does not seem to correctly handle the 'Z' timezone literal. Instead of assuming UTC as it should, it defaults to either PST or your local time (I'm not sure which). That means you might need to manually replace 'Z' literals with +00:00 to correct this behaviour?
As suggested I have created a utility class Gist which includes my currently running code. This should run on Android and also be compatible with Java 7+. Please feel free to ask any questions or leave comments. If there's enough interest I can move it over to Github so other people can contribute:
https://gist.github.com/oseparovic/d9ee771927ac5f3aefc8ba0b99c0cf38
Am I understanding this correctly or am I completely off? I would really appreciate any clarification you guys can offer about how to parse RFC 3339 strings in java 7.
You basically almost answered your own question except that even your gist is not correct for all cases... that is it requires even more patterns than the two you have (e.g. to deal with nano seconds).
And this is why Joda and Java 8 have special parsers for ISO 8601 (a superset).
I know you don't need references to other libraries but for others that are using Java 8 and or want to explicitly limit to RFC 3339 (the joda iso parsers I believe will take even more formats than rfc 3339) there is this library: https://github.com/ethlo/itu