There’s hardly any doubt that your unexpected observations are due to one or more time zone issues.
So the first thing you need to do is make sure you know which time zones are involved.
- Which time zone is your front end using for sending date and time to you?
- Which time zone is your database using for storing the date and time and displaying them to you when you check them? (UTC would be recommended for storing the times.)
Once you know this you can check:
- Is the conversion from 1/29/1900 07:01:01 AM from the front end in some time zone to an
Instant
of 1900-01-29T07:01:01Z
correct? The Instant
displays its time in UTC (denoted by the trailing Z
).
- Is the conversion from the
Instant
to 02:01:01 AM in the database time zone correct?
- Is the time being fetched correctly from the database? I am assuming you are fetching it back into Java?
- Is the time you’ve got in Java being converted correctly to 02:01:01 AM on the front end? Again I am assuming that on page reload your are displaying the time fetched from the database, but I don’t think you have told us, so I could be wrong.
To answer your question:
Am I constructing the time or the instant incorrectly?
It depends; it’s certainly possible.
- Your construction of the time is assuming that
pm
is always in lower case and that 12 o’clock (midnight or noon) is given as 0. On one hand I find both assumptions more or less unlikely, on the other hand they cannot account for the discrepancy of 5 hours that you observed. 12 would conventionally be given as 12 (not 0) on a 12 hour clock. And your question gives PM
in upper case.
- Your construction of the
Instant
assumes that the front end sent the time in UTC. To me this sounds unlikely too, and it may be the reason or one of the reasons why you observed an incorrect time being displayed back after page reoload.
Code example
In the following snippet I am making the opposite assumptions: 12 is given as 12, AM/PM may be in any case, and the front end time zone is America/New_York. It’s probably way off, but there may be a detail that you can pick and use for your purpose.
DateTimeFormatter timeFormatter = new DateTimeFormatterBuilder()
.parseCaseInsensitive() // Accept all of am, AM, aM and Am
.appendPattern("h:m:sa")
.toFormatter(Locale.US);
ZoneId zone = ZoneId.of("America/New_York");
LocalDate date = LocalDate.of(1900, Month.JANUARY, 29);
int hours = 7;
int minutes = 1;
int seconds = 1;
String amPm = "AM";
String constructedTimeString
= "" + hours + ':' + minutes + ':' + seconds + amPm;
LocalTime time = LocalTime.parse(constructedTimeString, timeFormatter);
Instant instant = date.atTime(time).atZone(zone).toInstant();
System.out.println(instant);
Output is:
1900-01-29T12:01:01Z
Geeky section: avoiding formatting time into a string and parsing it back
I couldn’t help thinking about whether it would be possible to have java.time parse the AM/PM string without having to construct a string for the time of day and parse it. It is possible, but we need to use the low-level TemporalAccessor
interface, which is otherwise usually unnecessary.
DateTimeFormatter amPmFormatter = new DateTimeFormatterBuilder()
.parseCaseInsensitive() // Accept all of am, AM, aM and Am
.appendPattern("a")
.toFormatter(Locale.US);
int hours = 7;
int minutes = 1;
int seconds = 1;
String amPm = "AM";
TemporalAccessor parsedAmPm = amPmFormatter.parse(amPm);
LocalTime time = LocalTime.of(0, minutes, seconds)
.with(ChronoField.AMPM_OF_DAY, parsedAmPm.get(ChronoField.AMPM_OF_DAY))
.with(ChronoField.CLOCK_HOUR_OF_AMPM, hours);
System.out.println(time);
07:01:01
Construction of the Instant
proceeds as before.