I am updating my old date formatting code to Java 8 and trying the ZonedDateTime API.
The format of date is same as the Javascript Date object format, e.g. -
Thu May 25 2017 10:00:00 GMT+1200 (New Zealand Standard Time)
I was using the below format previously -
EEE MMM dd yyyy hh:mm:ss 'GMT'Z '('zzzz')'
This format fails to parse the date string using DateTimeFormatter.ofPattern method.
Here's the code:
public static final String DATE_FORMAT = "EEE MMM dd yyyy hh:mm:ss 'GMT'Z '('zzzz')'";
public static void main(String[] args) throws ParseException {
String sDate = "Thu May 25 2017 10:00:00 GMT+1200 (New Zealand Standard Time)";
parseDate(sDate);
}
private static void parseDate(String sDate) throws ParseException {
// works
DateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
Date oldDate = dateFormat.parse(sDate);
//FIXME: can't parse?!
ZonedDateTime newDate = ZonedDateTime.parse(
sDate, DateTimeFormatter.ofPattern(DATE_FORMAT)); // <- this is the line 25!
}
Here's my full code for reference that can be compiled and run - https://gist.github.com/bhabanism/470e03db54981ad6ddedbba316dcaa9a
This fails at line#25 with:
Exception in thread "main" java.time.format.DateTimeParseException:
Text 'Thu May 25 2017 10:00:00 GMT+1200 (New Zealand Standard Time)'
could not be parsed: Unable to obtain ZonedDateTime from
TemporalAccessor: {HourOfAmPm=10, MilliOfSecond=0, MinuteOfHour=0,
OffsetSeconds=43200, MicroOfSecond=0, NanoOfSecond=0,
SecondOfMinute=0},ISO,Pacific/Auckland resolved to 2017-05-25 of type
java.time.format.Parsed
Note, I can't change the input format of the Date, it has to be
Thu May 25 2017 10:00:00 GMT+1200 (New Zealand Standard Time)
I can surely modify the formatter
EEE MMM dd yyyy hh:mm:ss 'GMT'Z '('zzzz')'
It seems there was a bug in your format string all the time. Lowercase hh is for hour within AM or PM, in the range 1 through 12. Since you don’t have AM/PM in your string, I suspect this was never what you wanted, and I wonder how the error went unnoticed.
Uppercase HH is for hour of day, 0 through 23:
public static final String DATE_FORMAT = "EEE MMM dd yyyy HH:mm:ss 'GMT'Z '('zzzz')'";
With this change both the old and the new way of parsing works on my computer.
When adding Locale.ENGLISH to both formatters, that is. You may want to do the same.
The results I get are
Thu May 25 00:00:00 CEST 2017
2017-05-25T10:00+12:00[Pacific/Auckland]
Since CEST is 2 hours ahead of UTC, this is the same point in time, only rendered differently.
Related
This question already has answers here:
DateTimeParse Exception
(2 answers)
IST mapped to wrong ZoneId in java.time library
(3 answers)
Closed 5 months ago.
How to convert this string into instant:
String date = "Fri Sep 30 00:00:00 IST 2022";
Exception:
java.time.format.DateTimeParseException: Text 'Fri Sep 30 00:00:00 IST 2022' could not be parsed at index 0
at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2046)
at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1948)
at java.base/java.time.LocalDate.parse(LocalDate.java:428)
In DB it is saved in this format
ISODate("2022-09-29T18:30:00.000Z")
But while debugging in IDE it is coming in string format like this: "Fri Sep 30 00:00:00 IST 2022"
Now I want to convert it back to instant
I tried in this way:
DateTimeFormatter DATE_FORMAT_RULE = DateTimeFormatter.ofPattern("MM/dd/yyyy");
String date = "Fri Sep 30 00:00:00 IST 2022";
Instant instant = LocalDate.parse(date, DATE_FORMAT_RULE)
.atStartOfDay()
.toInstant(ZoneOffset.UTC);
The pattern you are using does not match the one of the String. Your pattern cannot parse a two-digit month first because the String starts with an abbreviated day of week, whose abbreviation even depends on the Locale, which you haven't specified, so it will take the system default.
However, you can try to parse it with a different pattern, but I don't think it will get you the desired result… Try DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss z uuuu", Locale.ENGLISH) instead of your DateTimeFormatter.ofPattern("MM/dd/yyyy") and see the result. On my machine, it assumes IST to be Iceland Standard Time!
See this example:
public static void main(String[] args) {
// example String
String toBeParsed = "Fri Sep 30 00:00:00 IST 2022";
// first formatter: tries to parse "IST" as ZONE TEXT
DateTimeFormatter dtfZ = new DateTimeFormatterBuilder()
.appendPattern("EEE MMM dd HH:mm:ss")
.appendLiteral(' ')
.appendZoneText(TextStyle.SHORT)
.appendLiteral(' ')
.appendPattern("uuuu")
.toFormatter(Locale.ENGLISH);
// second formatter: tries to parse "IST" as ZONE NAME
DateTimeFormatter dtfz = DateTimeFormatter.ofPattern(
"EEE MMM dd HH:mm:ss z uuuu",
Locale.ENGLISH
);
// parse to a ZonedDateTime with the first formatter
ZonedDateTime zdt = ZonedDateTime.parse(toBeParsed, dtfZ);
// print the result
System.out.println("ZonedDateTime: "
+ zdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
// parse to a ZonedDateTime with the second formatter
ZonedDateTime zdt2 = ZonedDateTime.parse(toBeParsed, dtfz);
// print that, too
System.out.println("ZonedDateTime: "
+ zdt2.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
// convert to an Instant
Instant instant = zdt.toInstant();
// print the epoch millis
System.out.println(instant.toEpochMilli());
}
Output:
ZonedDateTime: 2022-09-30T00:00:00Z[Atlantic/Reykjavik]
ZonedDateTime: 2022-09-30T00:00:00Z[Atlantic/Reykjavik]
1664496000000
If you want the LocalDate.atStartOfDay(), you could simply extract it by calling toLocalDate() on an instance of ZonedDateTime (in case that ZonedDateTime contains any time of day different from zero hours, minutes, seconds and further down the units).
Corresponding to 2022-09-29T18:30:00.000Z, the IST in Fri Sep 30 00:00:00 IST 2022 refers to Indian Standard Time which has a time offset of UTC+05:30. You can build a formatter using .appendZoneText(TextStyle.SHORT, Set.of(ZoneId.of("Asia/Kolkata"))) with DateTimeFormatterBuilder as shown in the demo below:
public class Main {
public static void main(String[] args) {
DateTimeFormatter formatter = new DateTimeFormatterBuilder()
.appendPattern("EEE MMM dd HH:mm:ss")
.appendLiteral(' ')
.appendZoneText(TextStyle.SHORT, Set.of(ZoneId.of("Asia/Kolkata")))
.appendLiteral(' ')
.appendPattern("uuuu")
.toFormatter(Locale.ENGLISH);
Instant instant = ZonedDateTime.parse("Fri Sep 30 00:00:00 IST 2022", formatter).toInstant();
System.out.println(instant);
}
}
Output:
2022-09-29T18:30:00Z
Learn more about the the modern date-time API from Trail: Date Time.
Works for me
DateTimeFormatter DATE_FORMAT_RULE = DateTimeFormatter.ofPattern("E MMM dd hh:mm:ss z yyyy", Locale.US);
String date = "Fri Sep 30 00:00:00 IST 2022";
Instant instant2 = LocalDate.parse(date, DATE_FORMAT_RULE)
.atStartOfDay()
.toInstant(ZoneOffset.UTC);
I have an array of Strings with the dates e.g.:
Tue, 09 Feb 2016 14:07:00 GMT;
Tue, 09 Feb 2016 19:55:00 GMT.
Now I want to find the most recent date on this list. In order to do that, I try to deserialize these strings to java.util.Date objects and after that compare them.
The code sample of java.util.Date object generation:
strDate = "Tue, 09 Feb 2016 14:07:00 GMT";
DateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z");
Date date;
try {
date = format.parse(strDate);
//Result: Tue Feb 09 16:07:00 IST 2016
System.out.println("Result: " + date.toString());
} catch(ParseException e) {
e.printStackTrace();
}
My questions:
Why is the result in IST 2016 time zone and not in GMT? What does the IST 2016 stand for? Is it India Standard Time or Irish Standard Time or Israel Standard Time?
The initial string is in EEE, dd MMM format, the SimpleDateFormat pattern is also in this format, thus, why the result is in EEE, MMM dd format?
How can get a java.util.Date object in the same timezone as the initial string, in my case — GMT?
Is the approach I'm using to find the most recent date in the list is OK or there is more convenient/modern way to do that in Java 8, e.g., with the usage of LocalDateTime?
You are relying to Date.toString() to print your date when you should format it to a String with a formatter. What you are seeing is just the default pattern of Date.toString(). What you must keep in mind is that a Date does not have a timezone. You are seeing the output with the IST timezone, this must be because the current locale for the JVM is set to some specific locale for which the timezone name is "IST".
With regard to your point 4, yes, you can do it much cleaner with Java Time API introduced in Java 8. You can create a List of your strings to parse, create a DateTimeFormatter to parse it, and keep the maximum date value.
public static void main(String[] args) {
List<String> dates = Arrays.asList("Tue, 09 Feb 2016 14:07:00 GMT", "Tue, 09 Feb 2016 19:55:00 GMT");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH);
ZonedDateTime maxDate = dates.stream()
.map(s -> ZonedDateTime.parse(s, formatter))
.max(ZonedDateTime::compareTo)
.get(); // or .orElse(null)
System.out.println(maxDate);
}
This code is using a ZonedDateTime to keep the time-zone of the incoming strings.
Your computer seems to be set to IST. To force GMT output, import java.util.TimeZone and do this in your try block:
format.setTimeZone(TimeZone.getTimeZone("GMT"));
date = format.parse(strDate);
System.out.println("Result: " + format.format(date));
I m getting follwing error:
val formatter = ISODateTimeFormat.dateTimeParser()
scala> val date2 = "Tue Dec 29 11:11:30 IST 2015"
date2: String = Tue Dec 29 11:11:30 IST 2015
scala> formatter.parseDateTime(date2)
java.lang.IllegalArgumentException: Invalid format: "Tue Dec 29 11:11:30 IST 2015" is malformed at "ue Dec 29 11:11:30 IST 2015"
how to resolve following error??
I think you have the wrong format here - using SimpleDateFormat and a bit of googling this works:
scala> val formatter = new java.text.SimpleDateFormat("EEE MMM d HH:mm:ss Z yyyy")
formatter: java.text.SimpleDateFormat = java.text.SimpleDateFormat#73342172
scala> formatter.parse("Tue Dec 29 11:11:30 IST 2015")
res1: java.util.Date = Tue Dec 29 09:11:30 GMT 2015
edit: errr don't forget the timezone and year like I originally did ;-)
First thing to note:
Your input is not in ISO-Format and contains the name of a timezone (here: IST). You tried Joda-Time, but used an ISO-Format. This cannot work because the ISO-format pattern does not match the non-ISO-input. Second reason against Joda-Time is the fact that Joda-Time cannot parse timezone names. So following approach using a theoretically correct pattern will fail:
String input = "Tue Dec 29 11:11:30 IST 2015";
DateTimeFormatter dtf =
DateTimeFormat.forPattern("EEE MMM d HH:mm:ss z yyyy").withLocale(Locale.ENGLISH);
DateTime unparseable = dtf.parseDateTime(input);
// java.lang.IllegalArgumentException:
// Invalid format: "Tue Dec 29 11:11:30 IST 2015" is malformed at "IST 2015"
So you can only change the library. An obvious candidate using the (horrible) class SimpleDateFormat is:
String input = "Tue Dec 29 11:11:30 IST 2015";
String pattern = "EEE MMM d HH:mm:ss z yyyy";
SimpleDateFormat sdf = new SimpleDateFormat(pattern, Locale.ENGLISH);
java.util.Date jud = sdf.parse(input);
System.out.println(jud); // Tue Dec 29 10:11:30 CET 2015 (in my local tz CET=+01:00)
Okay no exception. But this does not mean that the result is automatically correct. The result can only be explained by having an offset of (+02:00 for IST). But is this true??? So let's have a closer look at the timezone involved:
TimeZone india = TimeZone.getTimeZone("Asia/Kolkata");
TimeZone israel = TimeZone.getTimeZone("Asia/Jerusalem");
System.out.println(israel.getDisplayName(false, TimeZone.SHORT)); // IST
System.out.println(india.getDisplayName(false, TimeZone.SHORT)); // IST
System.out.println(israel.getOffset(jud.getTime()) / 1000); // 7200 = +02:00
System.out.println(india.getOffset(jud.getTime()) / 1000); // 19800 = +05:30
This should trigger an alarm. Timezone names (especially abbreviations, here: IST) are often ambivalent and denote different timezones with different offsets.
So if you have got the input from Israel then you can be happy, but if from India then the result is wrong by 3:30 hours. Maybe your result will be such that it will match the India case instead of Israel. This will depend on your local timezone configuration. What so ever, don't blindly trust the parsed offsets.
The alternative Java-8:
String input = "Tue Dec 29 11:11:30 IST 2015";
ZoneId india = ZoneId.of("Asia/Kolkata");
DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
builder.appendPattern("EEE MMM d HH:mm:ss ");
builder.appendZoneText(TextStyle.SHORT, Collections.singleton(india)); // preferred zone
builder.appendPattern(" yyyy");
DateTimeFormatter dtf = builder.toFormatter(Locale.ENGLISH);
ZonedDateTime zdt = ZonedDateTime.parse(input, dtf);
System.out.println(zdt); // 2015-12-29T11:11:30+05:30[Asia/Kolkata]
// compare dangerous standard approach (not specifying your zone preference)
String pattern = "EEE MMM d HH:mm:ss z yyyy";
zdt = ZonedDateTime.parse(input, DateTimeFormatter.ofPattern(pattern, Locale.ENGLISH));
System.out.println(zdt); // 2015-12-29T11:11:30+02:00[Asia/Jerusalem]
It will work if you specify your preferred timezone but you really need to think twice before you parse timezone names. The builder approach might appear a little bit awkward but cannot be avoided due to the difficulty of the problem. So Java-8 is very fine to give you a solution here.
By the way, if you use a strict style (DateTimeFormatter.ofPattern(pattern, Locale.ENGLISH).withResolverStyle(ResolverStyle.STRICT)) then the parser will throw an exception with the message:
java.time.format.DateTimeParseException: Text 'Tue Dec 29 11:11:30 IST
2015' could not be parsed: Unable to obtain ZonedDateTime from
TemporalAccessor: {YearOfEra=2015, DayOfMonth=29, DayOfWeek=2,
MonthOfYear=12},ISO,Asia/Jerusalem resolved to 11:11:30 of type
java.time.format.Parsed
The message is somehow mysterious but I assume it is because of the ambivalent name IST.
If you are working on a platform with older JDK (Java 6 or 7) then you might consider ThreetenBP. ThreetenBP has the advantage to make a future migration easy (just changing the import statements) but my own experiments with the builder approach failed, unfortunately (even failed with the newest version v1.3.1 - maybe this depends on the underlying JDK???):
String input = "Tue Dec 29 11:11:30 IST 2015";
ZoneId india = ZoneId.of("Asia/Kolkata");
DateTimeFormatterBuilder builder = new DateTimeFormatterBuilder();
builder.appendPattern("EEE MMM d HH:mm:ss ");
builder.appendZoneText(TextStyle.SHORT, Collections.singleton(india)); // preferred zone
builder.appendPattern(" yyyy");
DateTimeFormatter dtf = builder.toFormatter(Locale.ENGLISH);
ZonedDateTime zdt = ZonedDateTime.parse(input, dtf);
System.out.println(zdt); // 2015-12-29T11:11:30+02:00[Israel] // why???
Else you can try my library Time4J which works on Java-6 or 7 (or later). It works similar to Java-8:
String input = "Tue Dec 29 11:11:30 IST 2015";
TZID india = ASIA.KOLKATA;
ChronoFormatter<Moment> f = ChronoFormatter.setUp(Moment.axis(), Locale.ENGLISH)
.addPattern("EEE MMM d HH:mm:ss ", PatternType.CLDR)
.addShortTimezoneName(Collections.singleton(india)) // preferred zone
.addPattern(" yyyy", PatternType.CLDR)
.build();
System.out.println(f.parse(input)); // 2015-12-29T05:41:30Z
System.out.println(ZonalDateTime.parse(input, f)); // 2015-12-29T11:11:30UTC+05:30[Asia/Kolkata]
The error message in strict or smart parser style using
String input = "Tue Dec 29 11:11:30 IST 2015";
String pattern = "EEE MMM d HH:mm:ss z yyyy";
ChronoFormatter<Moment> f = // here smart standard style!
ChronoFormatter.ofMomentPattern(
pattern, PatternType.CLDR, Locale.ENGLISH, ZonalOffset.UTC);
f.parse(input);
will be:
Time zone name "IST" not found among preferred timezones in locale en,
candidates=[Asia/Colombo, Asia/Jerusalem, Asia/Kolkata, Europe/Dublin]
Then you will immediately see that "IST" can be associated with different timezones.
I have a date String "Sat Jan 28 00:00:00 IST 2012" and I am trying to parse it using DateTimeFormatter of Joda. I have the following code, dont know where, it went wrong.
DateTimeFormatter dateFmt = DateTimeFormat
.forPattern("EEE MMM dd HH:mm:SS ZZZ yyyy");
DateTime dateTime = dateFmt.parseDateTime(dateString);
Exception : java.lang.IllegalArgumentException: Invalid format: "Sat Jan 28 00:00:00 IST 2012" is malformed at "IST 2012". Please help me to get thro this. Thanks for any help.
IST is not recognized timezone by API, It can recognize only one of the timezone from getAvailableIds()
Use zzz (lowercase), not ZZZ (uppercase). From the API docs:
z time zone text Pacific Standard Time; PST
Z time zone offset/id zone -0800; -08:00; America/Los_Angeles
I don't know why, but it is working if I use SimpleDateFormat instead of DateTimeFormatter.
CODE:
public static void main(String[] args) throws ParseException {
String FORMAT = "EEE MMM dd HH:mm:SS zzz yyyy";
String dateString = "Sat Jan 28 00:00:00 IST 2012";
SimpleDateFormat dateFormat = new SimpleDateFormat(FORMAT);
Date date = dateFormat.parse(dateString);
System.out.println(new DateTime(date));
DateTimeFormatter dateFmt = DateTimeFormat.forPattern(FORMAT);
// System.out.println(dateFmt.parseLocalDateTime(dateString));
// System.out.println(dateFmt.parseDateTime(dateString));
System.out.println(dateFmt.parseLocalTime(dateString));
}
I have the following Java:
DateFormat formatter = new SimpleDateFormat(
"EEE MMM dd yyyy HH:mm:ss zZ (zzzz)", Locale.ENGLISH);
Calendar cal = Calendar.getInstance();
cal.set(2011, Calendar.APRIL, 1);
out.println(formatter.format(cal.getTime()));
out.println();
Date date;
try {
date = formatter
.parse("Fri Apr 01 2011 00:00:00 GMT-0400 (Eastern Daylight Time)");
} catch (ParseException e) {
out.println("Failed to parse date: " + e.getMessage());
e.printStackTrace(out);
}
This is in a servlet, and the Calendar-constructed date comes out as:
Fri Apr 01 2011 16:42:24 EDT-0400 (Eastern Daylight Time)
This looks like the same format as the date string I'm trying to parse, except for EDT-0400 versus the desired GMT-0400. The code fails when trying to parse the date string:
java.text.ParseException: Unparseable date: "Fri Apr 01 2011 00:00:00 GMT-0400 (Eastern Daylight Time)"
How can I parse such a string? This is coming from a JavaScript date in a Sencha Touch 1.1.1 model, stored in WebSQL local storage.
For some reason GMT-0400 isnt' working, and UTC-0400 is working. You can replace GMT with UTC.
Note that this part will be completely ignored - the timezone will be resolved from what's found in the brackets (at least on my machine, JDK 6)
I debugged SimpleDateFormat and it seems that it will only parse GMT-04:00 but not GMT-0400.
It will accept UTC-0400, however it will throw away the hours/minutes modifier and will incorrectly parse it as UTC. (This happens with any other timezone designation, except for GMT)
It will also parse -0400 correctly, so the most robust solution is probably to simply remove GMT from your date string.
The upshot of the story is that SimpleDateFormat is anything but simple.
Update: Another lesson is that I could've saved a lot of time by passing a ParsePosition object to the parse() method:
DateFormat formatter = new SimpleDateFormat(
"EEE MMM dd yyyy HH:mm:ss zzzz", Locale.ENGLISH);
Date date;
ParsePosition pos = new ParsePosition( 0 );
date = formatter
.parse("Fri Apr 01 2011 00:00:00 UTC-0400", pos);
System.out.println( pos.getIndex() );
Will print out 28, indicating that the parsing ended at character index 28, just after UTC.