4

I'm encountering a problem using LocalDate in UTC. My server uses UTC, and my database uses UTC. I used LocalDate to store a billingDate for a subscription based application.

What happens is that we bill at midnight UTC (when doing comparisions like billingDate <= LocalDate.now()). We actually mean to bill sometime after midnight PST.

I really felt like using LocalDate was appropriate here, because we just want to bill at some point during that day. However, it doesn't seem practical when doing comparisons either directly in the code or in the database (billing_date <= CURRENT_DATE()). Did I make a mistake, should this be a ZonedDateTime in PST? Or should we be converting to ZonedDateTime for comparisons? It feels error prone, we need to remember to convert any time we do a comparision, but perhaps this is the correct solution?

Does anyone have experience with this situation and found a nice solution?

I've taken a look at this question, but it doesn't answer my question: Spring REST LocalDate UTC differs of one day

Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
brub
  • 1,083
  • 8
  • 18

2 Answers2

3

I suggest that this is just a matter of passing the desired time zone to LocalDate.now(ZoneId).

  • Use LocalDate.now(ZoneId.of("Asia/Manila")) for Philippine Standard Time. At the moment it yeilds 2019-07-09.
  • Use LocalDate.now(ZoneId.of("Pacific/Pitcairn")) for Pitcairn Standard Time. It just gave 2019-07-08.

I am assuming that you didn’t mean Pacific Standard Time since no time zone uses Pacific Standard Time as we speak (those that do in winter, are on Pacific Daylight Time now). In any case, mind you that three letter time zone abbreviations are often ambiguous.

The java.time classes that have a now method generally have three overloaded variants of it:

  1. One that takes a ZoneId arguments that I recommend for general use.
  2. One that takes a Clock argument that is great for testability. A Clock includes a time zone, so this one too gets you the current date and/or time in that specified time zone.
  3. One that doesn’t take any arguments and uses the JVM’s default time zone. I recommend that you never use it. It’s nice for the reader to know that you have considered time zone and chosen which one you want. And the default time zone can be changed at any time by any program running in the same JVM, so is not stable enough to rely on for real work.
Ole V.V.
  • 81,772
  • 15
  • 137
  • 161
  • actually this ended up being the solution. I ended up storing data in the database as UTC. I configured hibernate to read the data in America/Los_Angeles timezone: spring.jpa.properties.hibernate.jdbc.time_zone=America/Los_Angeles and in the mysql connection string: useLegacyDatetimeCode=false&serverTimezone=America/Los_Angeles. I set the default timezone of my server to America/Los_Angeles. At that point LocalDates worked as expected, and were treated as midnight to 11:59 in the LA timezone. – brub Jul 18 '19 at 14:07
2

I feel like you should be using Instants.

I really felt like using LocalDate was appropriate here, because we just want to bill at some point during that day.

Well, no. You do care about the time you bill, because your database cares about the time. It stores the billing time as 00:00 UTC. Since that is an instant in time, I think Instant would be the most suitable choice here. You could use a ZonedDateTime as well, but considering that you are probably getting a java.sql.Date from your database, which has a toInstant method already, using Instants is more convenient.

You can get an instant from a year, month, day like this:

LocalDate ld = LocalDate.of(2019, 7, 8);
Instant i = ld.atStartOfDay(ZoneId.of("America/Los_Angeles")).toInstant();

America/Los_Angeles is PST.

Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • Yeah I think this is the correct solution. I think I should have used Instant. I'm thinking LocalDate is probably not so useful. – brub Jul 09 '19 at 13:34