0

Let's assume that I have client's time saved in my database as 2020-09-22T10:50:37.276240900

I need to present this date in web-service for client app depending on client timezone, for example I need to add 2 hours to saved date if client lives in UTC+2 timezone.

So what am I doing for ?

Getting date from entity and adding timezone to time taken from database (startDate: LocalDateTime)

entity.startDate.atZone(ZoneId.of("Europe/Vienna"))

what gives me the value of ZonedDateTime 2020-09-22T10:50:37.276240900+02:00[Europe/Vienna]

This value is what I'm expecting for, basically "initial time plus 2 hours". After that I would to format this time to have output with this 2 hours of being added, some kind of this

12:50 22.09.2020

but when I do format like this

entity.startDate
 .atZone(ZoneId.of("Europe/Vienna"))
 .format(DateTimeFormatter.ofPattern(NotificationListener.EUROPEAN_DATE_FORMAT, Locale.ENGLISH))

where const val EUROPEAN_DATE_FORMAT = "HH:mm dd.MM.yyyy"

I get this output 10:50 22.09.2020 which looks like my format is not applied properly, so I cannot see my 2 hours.

So my questions are:

  1. am I correct to adding timezone of client app in described way ?
  2. how to apply timezone in more precise way and format this date to see timezone zone applied ?
Eugene Shmorgun
  • 2,083
  • 12
  • 42
  • 67
  • What type is `entity.startDate`? – Sweeper Sep 22 '20 at 11:10
  • 2
    "Let's assume that I have client's time save din my database as 2020-09-22T10:50:37.276240900" - and is that meant to be in UTC? What type are you using in Java or Kotlin to represent that? A [mcve] would make it much easier to help you. – Jon Skeet Sep 22 '20 at 11:11
  • One thing to keep in mind is that a date and time *without time zone information* does NOT uniquely identify a specific point in time. You *need* to use some timezone information (even if it's the "default" UTC) to convert that into a timestamp that has physical meaning. In other words: `LocalDateTime` is **not** a specific point in time, but `ZonedDateTime` is! – Joachim Sauer Sep 22 '20 at 11:13
  • What are the contents of `EUROPEAN_DATE_FORMAT`? – MC Emperor Sep 22 '20 at 11:16
  • 2
    Stepping back a moment here, the underlying issue is that you know something the database doesn't: you know that the dates it stores are in UTC. (The question doesn't state that, but seems to assume it.)  Because the DB doesn't know that, it gives you a LocalDateTime, which has _no_ zone.  In order to do the conversion, you need to tell it that that is UTC before you can convert it to some other zone (e.g. converting to a ZonedDateTime, as per Joachin's answer). – gidds Sep 22 '20 at 13:28

1 Answers1

2

LocalDateTime.atZone does not "move" the point in time. In fact it tries to present the point in time where the local time in the given timezone is exactly what the LocalDateTime shows.

In other words: if your LocalDateTime represented 10:00 at some date, then the ZonedDateTime output of atZone will also represent 10:00 local time at the specified time zone (except in cases where that local time doesn't exist due to DST changes).

So if your stored time is actually in UTC, you need to add one more step:

ZonedDateTime utcTime = entity.startDate.atZone(ZoneOffset.UTC);
ZonedDateTime localTime = utcTime.withZoneSameInstant(ZoneId.of("Europe/Vienna"));

Alternatively you can avoid calculating the localTime each time and instead configure the DateTimeFormatter to use a given time zone (which means it'll do the necessary calculations internally) using DateTimeFormatter.withZone. If you do this then you can pass the utcTime to it directly.

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
  • Thank you for great explanation. Is this approach applicable for summer/winter time changes ? – Eugene Shmorgun Sep 22 '20 at 11:26
  • 1
    One thing that's outside the strict Java library scope is that your DB mapping layer might be able to do the `atZone` step if you tell it that the time in the DB is always stored in UTC. That way you can have your entity hold a `ZonedDateTime` and avoid a potential source of mistakes. – Joachim Sauer Sep 22 '20 at 11:29