Reputation: 571
I have a MapperUtility class that needs to map a string from a web service that sends a string time "Fri Nov 22 2013 12:12:13 GMT+0000 (UTC)"
Now, I am converting it to LocalDateTime with this code:
String time = "Fri Nov 22 2013 12:12:13 GMT+0000 (UTC)";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("E MMM dd yyyy HH:mm:ssZ");
dtf.withZone(ZoneId.of("UTC"));
LocalDateTime convertedDate = LocalDateTime.parse(time, dtf);
But I am having an exception starting the GMT+0000 (UTC). It works when I removed the characters beyond the GMT. After converting them to Date Time, I need to convert them to long milliseconds. Please advise. Thanks.
Upvotes: 2
Views: 2012
Reputation: 86323
String time = "Fri Nov 22 2013 12:12:13 GMT+0000 (UTC)";
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("E MMM dd yyyy HH:mm:ss 'GMT'xx (zzz)", Locale.ENGLISH);
ZonedDateTime zdt = ZonedDateTime.parse(time, dtf);
OffsetDateTime odt = OffsetDateTime.parse(time, dtf);
boolean offsetAgrees = zdt.getOffset().equals(odt.getOffset());
if (offsetAgrees) {
long millisecondsSinceEpoch = odt.toInstant().toEpochMilli();
System.out.println("Milliseoncds: " + millisecondsSinceEpoch);
} else {
System.out.println("Offset " + odt.getOffset() + " does not agree with time zone " + zdt.getZone());
}
Output:
Milliseoncds: 1385122333000
I am parsing GMT
as a literal, +0000
as an offset and UTC
as a time zone abbreviation. For the offset we could have used xx
or ZZZ
. Since Fri
and Nov
are in English, we need to specify an English speaking locale.
I have added a little complication compared to your code because we want to validate that the offset and the time zone agree. OffsetDateTime.parse
uses the offest (+0000
) directly whereas ZonedDateTime.parse
derives the offset from the time zone ( UTC
). My check is very bare-bones and may be extended to accept two possible offsets in the transition from summer time (DST) and multiple time zones sharing the same abbreviation.
PS Don’t use LocalDateTime
. This type can neither hold an offset nor time zone, so you can no longer attach your date and time to a specific point on the time line, which you need to do to obtain milliseconds since the epoch.
Upvotes: 0
Reputation: 14348
You may build such pattern using DateTimeFormatterBuilder
:
static final DateTimeFormatter DF = new DateTimeFormatterBuilder()
.append(DateTimeFormatter.ofPattern("E MMM dd yyyy HH:mm:ss"))
.appendLiteral(" GMT")
.appendOffset("+HHmm", "+0000")
.optionalStart()
.appendLiteral(" (")
.appendZoneId()
.appendLiteral(')')
.optionalEnd()
.toFormatter()
.withLocale(Locale.US);
Then, just:
String date = "Fri Nov 22 2013 12:12:13 GMT+0000 (UTC)";
long ms = OffsetDateTime.parse(date, DF).toInstant().toEpochMilli(); // 1385122333000
Upvotes: 1
Reputation: 9541
An inefficient way to make the parser accept your string verbatim is:
String[] timezones = {"UTC", "BST", "CET", "PST", ...};
StringBuilder sb = new StringBuilder(timezones.length * 8 + 38);
sb.append("E MMM dd yyyy HH:mm:ss' GMT'Z' ('");
for(String timezone : timezones)
sb.append("['").append(timezone).append("']");
sb.append("')'");
DateTimeFormatter dtf = DateTimeFormatter.ofPattern(sb.toString());
String time = "Fri Nov 22 2013 12:12:13 GMT+0000 (UTC)";
ZonedDateTime convertedDate = ZonedDateTime.parse(time, dtf);
System.out.println(convertedDate);
I changed to ZonedDateTime
too because otherwise it discards the timezone and always returns 12:12:13 regardless of what's after the GMT+
.
But it gets unwieldy pretty quick because of the inexhaustible list of possible time zone abbreviations.
A better way is to preprocess the string:
String time = "Fri Nov 22 2013 12:12:13 GMT+0000 (UTC)";
String preprocessed = time.replaceAll("(.*) GMT([+-][0-9]{4}).*", "$1$2");
System.out.println(preprocessed);
DateTimeFormatter dtf = DateTimeFormatter.ofPattern("E MMM dd yyyy HH:mm:ssZ");
ZonedDateTime convertedDate = ZonedDateTime.parse(preprocessed, dtf);
System.out.println(convertedDate);
Then the conversion to milliseconds is a bit tricky to find in the extensive java.time
API but eventually it turns out to be as simple as:
convertedDate.toInstant().toEpochMilli()
Upvotes: 1