You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@freemarker.apache.org by Daniel Dekany <da...@gmail.com> on 2020/07/05 20:58:17 UTC

java.time formatting troubles with time zones

I was trying to progress with FREEMARKER-35. So we have settings akin to
the good old data_format/datatime_format/time_format, but they are named
after the java.time temporal classes, like zoned_date_time_format,
local_date_time_format, etc. The setting value is a pattern as defined by
java.time.format.DateTimeFormatter, like "uuuu-MM-dd HH:mm". (Actually it
can also have other kind of values, like "medium", "iso", etc. but for now
it doesn't matter.)

Problem 1: When the zone is not shown

If a DateTimeFormatter doesn't show the time zone (or the offset), it of
course still will not convert it to the current time zone (dictated by the
FreeMarker time_zone setting). So if I have ZonedDateTime 2000-01-01
15:00+05, and the current time_zone is +02, and the format doesn't contain
a time zone part, you just see "2000-01-01 15:00", and not "2000-01-01
12:00". That's unlike with java.util.Date, where no such accident can
happen; if you see no time zone, then you see the local date-time in the
current time zone. But unlike java.util.Date, ZonedDateTime doesn't store
an instant of time, instead it stores a local date-time and a zone. So I'm
not supposed to mess with that zone, just show it as is. But on the same
time, I can't tell if the DateTimeFormatter will actually show the zone (I
don't see an API to query that). Worse, typical local default formats like
"MEDIUM" and "SHORT", do not contain time zone (checked on Java 8 and 13,
with a dozen of locales). So users will burn themselves with this, showing
misleading information.

Proposed solution:
When a new format is provided to FreeMarker, it will detect if the output
of that format is affected by the zone (or offset) of the input temporal,
by providing two dummy inputs that only differ in time zone, and see if the
outputs differ. That's admittedly a dirty hack (and bit of runtime
overhead), but I see no API to query such information about a
DateTimeFormatter. If I have found that the zone (or offset) is not shown,
they I will convert the input temporals to the current time zone  as part
of the formatting. Otherwise, don't do time zone conversion, and so the
zone will be shown as is. So we deviate from what java.time does, but
hopefully do what's safest, and practical.


Problem 2: Default formats are failing for some input types

It's about DateTimeFormatter.ofLocalizedXxx(FormatStyle), where
FormatStyle can be FULL, LONG, MEDIUM, and SHORT. The goal of this API is
give the default formatter for given locale, each my uses a different
pattern internally.

The problem is that the provided formatters only works reliably for
ZonedDateTime and LocalDate, but not for LocalDateTime, OffsetDateTime, or
OffsetTime, because the localized pattern my displays the time zone, even
if your input type has no such field! So if you try to format a
LocalDateTime with LONG, you get an exception with an otherwise quite
unhelpful error message. So how to shield the users from this?

Based on a dozen of locales and Java 8 and 13, it seems that MEDIUM and
SHORT never contains zone, so they are safe, while FULL and LONG always do.
Although, it's just an observation, I see no such promise in the API.
Luckily, our default format will be MEDIUM, as that's the default for
java.util.Date too. (That's also why Problem 1 has a big impact.) So we are
kind of fine there... hoping that they won't add time zone to the MEDIUM
format of some locale in the future.

Other than that, I don't know what to do if someone uses LONG format for
example. I think adding and displaying zones to temporals that are not
zoned by their very nature (LocalDateTime, etc.), is not acceptable. We had
one job after all: showing data, without distortion. We don't know if that
LocalDateTime or LocalTime is the instant in a given time zone, that we
would show, if we add the current time zone to it. There's also the wild
possibility of somehow editing the zone out of the format given, but I
don't see how, as I can't even get hold of the pattern of the
DateTimeFormatter returned by DateTimeFormatter.ofLocalizedXxx. So, for
now, I plan to let it fail, except I will try if it still fails if I have
zone, and if it doesn't, then tell the user in the error message that this
format only works for inputs that contains a time zone. We will have to add
some built-in to explicitly add a time zone to local stuff.

Anybody has some other ideas, or wants me to not do some of these, please
tell it!

-- 
Best regards,
Daniel Dekany