My embedded Java application running on Linux should allow the user to change the timezone through a GUI. My application is running in an OSGI container (see below why I believe this is relevant) and should not need to restart before using the new timezone.
What is the recommended way of persistently setting the timezone from my Java/OSGi application?
I can think of the following approaches, for which I list some Pros and Cons. Am I missing something? What's recommended?:
- From the app, change the underlying OS timezone, additionally use
TimeZone.setDefault(...)
for the currently running JVM and renew allClock
instances which hold the old TZ (so some kind of event is necessary). Con: this method is OS dependent and quite low level, also I would like to keep the OS clock UTC. Pros: OS takes care of storing the TZ, TZ is immediately correct upon next startup of app. - From the app, change the
-Duser.timezone=...
parameter used for launching. Con: very ugly, even lower level, but allows to leave the OS clock in UTC, while having the app start with the correct TZ. Also need to renewClock
instances on change. - Don't touch the OS, and only use
TimeZone.setDefault(...)
and call it early on startup. This will need a separate persistence (preferences) to save it. Here also, in the currently running JVM, allClock
instances which reference the old TZ, need to be renewed upon change (needs event). When running in an OSGi container, the startup order of bundles is not guaranteed, so I cannot be sure that the default TZ gets set before it is being used. How can I guarantee this? Also, JSR310 explicitely advises against using the "default TZ" in Clock. - Not use the "default" TimeZone at all, use a separate global variable and upon each conversion between
Instant
andLocalXXX
values, pass the timezone explicitly. This gets rid of needing an event to updateClock
instance. But we need to pay attention not to useLocalDate.now(clock)
, as this uses the clock's TZ (which is then no longer correct). How to have this global variable in OSGi? usingConfigAdmin
? How to make code behave correctly which I have no control over (eg. logging time stamps)?
Edit: To get rid of the need to update the Clock
, I could use a clock which always checks the default TimeZone, but this seems suboptimal from a performance POV:
public class DefaultZoneClock extends Clock {
private final Clock ref = Clock.systemUTC();
@Override
public ZoneId getZone() {
return ZoneId.systemDefault(); // probed on each request
}
@Override
public Clock withZone(ZoneId zone) {
return ref.withZone(zone);
}
@Override
public Instant instant() {
return ref.instant();
}
}
Is that a good idea?
Edit 2:
About my performance concerns above: they are obviously not justified. When you call LocalDate.now()
, internally a new SytemClock
is built which sets the current ZoneID
by searching it in a Map - this is exactly the same as using my DefaultZoneClock
above, the difference being that using my code I can inject any other Clock
for testing. (all client code would use LocalDate.now(clock)
)
Answers below suggest not to change the JVM TimeZone but to do the conversion whenever it is necessary based on a user-defined TimeZone, this means that I have to take care not to use the java.time methods which call the TimeZone from Clock
, eg. if I need a LocaTime
use
// OK, TimeZone is set explicitely from user data
LocalTime t = clock.instant().atZone(myUserZoneID).toLocalTime();
// Not OK, uses the Clock's internal TimeZone which may not have been set or updated
LocalTime t2 = LocalTime.now(clock);