I saw some strange behavior in my production application; .apachemons.lang3.time.DateUtils
is throwing a java.text.ParseException
.
So I thought it was some code issue, but it turned out it's only having issues with March 9th, 2025, 2am time stamps. So I tried running some tests and found DateUtils.parseDateWithLeniency
is unable to parse the date but DateUtils.parseDate
is working.
All the dates are working before and after that one hour.
Not sure if it's a bug in the Java Calendar. I observed areAllFieldsSet
in the false
state in the Calendar
class. Does anyone has any insight into this?
Test code tried locally
import java.text.ParseException;
import java.util.Date;
import .apachemons.lang3.time.DateUtils;
public class Test11 {
public static void main(String[] args) {
String dateString = "2025-03-09T02:37:51.742";
try {
Date date = DateUtils.parseDateStrictly(dateString,new String[]{
"yyyy-MM-dd'T'HH:mm:ss.SSS"});
System.out.println(date);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
Unable to parse the date: 2025-03-09T02:37:51.742
at .apachemons.lang3.time.DateUtils.parseDateWithLeniency(DateUtils.java:391)
at .apachemons.lang3.time.DateUtils.parseDateStrictly(DateUtils.java:333)
at .apachemons.lang3.time.DateUtils.parseDateStrictly(DateUtils.java:311)
at com.test.Test11.main(Test11.java:17)
A few failed dates for reference:
2025-03-09T02:52:51.261
2025-03-09T02:45:25.032
2025-03-09T02:08:01.013
2025-03-09T02:59:06.913
2025-03-09T02:57:20.466
2025-03-09T02:19:46.928
2025-03-09T02:45:19.957
2025-03-09T02:49:34.463
2025-03-09T02:13:09.896
2025-03-09T02:04:15.99
2025-03-09T02:09:53.982
2025-03-09T02:10:24.47
2025-03-09T02:24:27.696
2025-03-09T02:17:03.064
2025-03-09T02:05:58.489
2025-03-09T02:24:05.871
2025-03-09T02:41:06.302
2025-03-09T02:04:24.478
2025-03-09T02:32:36.959
2025-03-09T02:26:20.384
2025-03-09T02:40:51.959
2025-03-09T02:33:41.93
2025-03-09T02:58:00.669
2025-03-09T02:48:26.187
I tried running the sample strings and observed the issue for a one hour period.
I saw some strange behavior in my production application; .apachemons.lang3.time.DateUtils
is throwing a java.text.ParseException
.
So I thought it was some code issue, but it turned out it's only having issues with March 9th, 2025, 2am time stamps. So I tried running some tests and found DateUtils.parseDateWithLeniency
is unable to parse the date but DateUtils.parseDate
is working.
All the dates are working before and after that one hour.
Not sure if it's a bug in the Java Calendar. I observed areAllFieldsSet
in the false
state in the Calendar
class. Does anyone has any insight into this?
Test code tried locally
import java.text.ParseException;
import java.util.Date;
import .apachemons.lang3.time.DateUtils;
public class Test11 {
public static void main(String[] args) {
String dateString = "2025-03-09T02:37:51.742";
try {
Date date = DateUtils.parseDateStrictly(dateString,new String[]{
"yyyy-MM-dd'T'HH:mm:ss.SSS"});
System.out.println(date);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
Unable to parse the date: 2025-03-09T02:37:51.742
at .apachemons.lang3.time.DateUtils.parseDateWithLeniency(DateUtils.java:391)
at .apachemons.lang3.time.DateUtils.parseDateStrictly(DateUtils.java:333)
at .apachemons.lang3.time.DateUtils.parseDateStrictly(DateUtils.java:311)
at com.test.Test11.main(Test11.java:17)
A few failed dates for reference:
2025-03-09T02:52:51.261
2025-03-09T02:45:25.032
2025-03-09T02:08:01.013
2025-03-09T02:59:06.913
2025-03-09T02:57:20.466
2025-03-09T02:19:46.928
2025-03-09T02:45:19.957
2025-03-09T02:49:34.463
2025-03-09T02:13:09.896
2025-03-09T02:04:15.99
2025-03-09T02:09:53.982
2025-03-09T02:10:24.47
2025-03-09T02:24:27.696
2025-03-09T02:17:03.064
2025-03-09T02:05:58.489
2025-03-09T02:24:05.871
2025-03-09T02:41:06.302
2025-03-09T02:04:24.478
2025-03-09T02:32:36.959
2025-03-09T02:26:20.384
2025-03-09T02:40:51.959
2025-03-09T02:33:41.93
2025-03-09T02:58:00.669
2025-03-09T02:48:26.187
I tried running the sample strings and observed the issue for a one hour period.
Share Improve this question edited Mar 18 at 18:40 President James K. Polk 42.1k29 gold badges109 silver badges145 bronze badges asked Mar 17 at 20:32 vv1vv1 231 silver badge2 bronze badges 4 |2 Answers
Reset to default 4java.time
I warmly recommend that you use java.time, the modern Java date and time API, for all of your date and time work.
String dateString = "2025-03-09T02:37:51.742";
LocalDateTime ldt = LocalDateTime.parse(dateString);
System.out.println(ldt);
Output from this snippet is:
2025-03-09T02:37:51.742
A LocalDateTime
is a date and time without time zone or offset from UTC. So it doesn’t know summer time (DST) and happily parses times that happen to fall in the spring gap that happens in your own default time zone between 2 and 3 in the night on this particular date where the clocks are turned forward.
Don’t we need a formatter? No, not in this case. Your string is in the standard ISO 8601 format. Which is good. The classes of java.time generally default parse the most common ISO 8601 variants natively, that is, without any explicit formatter. So parsing proceeds nicely without one. You also notice in the output that LocalDateTime
prints the same format back from its toString
method, just like the other java.time types do.
OffsetDateTime
In a comment you informed us:
The application is running on jdk11 and server is set to EST time zone. The payload received from vendor is UTC time zone.
When you know the date and time are in UTC, it’s advantageous (on the border of required) to use OffsetDateTime
:
OffsetDateTime odt = ldt.atOffset(ZoneOffset.UTC);
System.out.println(odt);
2025-03-09T02:37:51.742Z
The trailing Z
in the output is pronounced “Zulu” and denotes UTC. Now the date-time object also itself knows that it is in UTC, so you don’t need to keep track of it with the risks of error that that would have entailed.
An OffsetDateTime
is date and time with an offset from UTC — in this case offset 0.
Don’t use Date and the other legacy date-time types
The Date
class was always poorly designed and has fortunately been outdated since java.time was launched with Java 8 more than 10 years ago. Avoid it.
Only, if you need a Date
for a legacy API that you cannot afford to upgrade to java.time just now, convert the OffsetDateTime
from above:
Date oldfashionedDate = Date.from(odt.toInstant());
System.out.println(oldfashionedDate);
I ran this snippet in America/Toronto time zone, assuming that by “EST” you meant something in this direction. EST is ambiguous as many three letter abbreviations are, and it’s not a time zone since most of the people using North American Eastern Standard Time (EST) use Eastern Daylight Time (EDT) for the greater part of the year. In any case, in America/Toronto the output is:
Sat Mar 08 21:37:51 EST 2025
Since Eastern Standard Time is 5 hours behind UTC, this agrees with 02:37:51 on March 9th UTC from the string.
Tutorial link
Oracle Tutorial: Trail: Date Time explaining how to use java.time.
tl;dr
java.time.LocalDateTime // Represents a date with time-of-day but no zone or offset.
.parse ( "2025-03-09T02:37:51.742" ) // Parsing standard ISO 8601 format. Returns a `LocalDateTime` object.
.atOffset ( ZoneOffset.UTC ) // Applies an offset from UTC of zero hours-minutes-seconds. Returns a `OffsetDateTime` object.
java.time
You’re using terribly flawed date-time classes that are now legacy, supplanted by the modern java.time classes built into Java 8 and later.
LocalDateTime
Your string input represents a date with time of day, but lacks the context of a time zone or offset from UTC. So, parse as a LocalDateTime
.
LocalDateTime ldt = LocalDateTime.parse ( "2025-03-09T02:37:51.742" ) ;
Be aware that this object does not represent a moment, is not a point on the timeline. Without a zone or offset, we have no way of knowing if this is 2 AM in Tokyo Japan, 2 AM in Toulouse France, or 2 AM in Toledo Ohio US — three different moments several hours apart.
ZonedDateTime
If you are certain your input was meant to represent a moment as seen in a particular time zone, specify that zone to generate a ZonedDateTime
.
ZoneId z = ZoneId.of ( "America/New_York" ) ;
ZonedDateTime zdt = ldt.atZone ( z ) ;
Run this code at Ideone.
2025-03-09T03:37:51.742-04:00[America/New_York]
OffsetDateTime
Perhaps your input represents a moment “in UTC”, meaning an offset from UTC of zero hours-minutes-seconds. The constant ZoneOffset.UTC
holds an offset of zero.
For offsets rather than time zone, use OffsetDateTime
class rather than ZonedDateTime
class. (A time zone is a named history of the past, present, and future changes to the offset used by people of a particular region as determined by their politicians.)
OffsetDateTime odt = ldt.atOffset( ZoneOffset.UTC ) ;
LocalDateTime
. WithLocalDateTime
your parsing will work also for the mentioned date-times. – Anonymous Commented Mar 17 at 20:562025-03-09T02:37:51.742
may denote a range of points in time that is 24 hours wide or wider. Also, what do you need to use the parsed date-times for? The good solution depends on that. – Anonymous Commented Mar 17 at 20:58