Multi Time Zone Environment - fieldenms/tg GitHub Wiki
Overview
When developing TG applications we [currently] use Date
and Joda-Time DateTime
for manipulating dates (e.g. to create now moment we may write new Date()
or new DateTime()
expression).
If we just need to persist date or to compare two dates it is perfectly okay to use java.util.Date
class and its before/after
methods for that. Things get more nuanced when we need to do some time-zone dependent manipulations:
- get day start or some concrete hour of the day (e.g.
8 PM
) - show human-readable (
toString
) date representation - midnight moment before now
3 AM
moment to perform some job
We need to always remember "In which time-zone?" question before making these manipulations. Currently, we create Joda-Time org.joda.time.DateTime
time-zone-aware instance and then use its methods to perform such manipulations.
If all users are in the same time-zone as a server (as specified explicitly in -Duser.timezone
) its okay to not pass actual time-zone everywhere (e.g. use new Date()
and new DateTime(...)
without time-zone, meaning it will be created in server time-zone). However, things get more messy if the user makes request in different time-zone. For example, their day start would not be the same as for the users in server time-zone.
It is important to run IDE tests, maven tests (...-[dao/web-server]/pom.xml
), PopulateDb
, Server
and Vulcanize
(!) in the intended server time-zone.
Special Considerations
In a multi time-zone environment, where users may reside in time-zones different from the server one, it is important to create / manipulate dates using our IDates
API methods:
DateTime zoned(Date date)
DateTime now()
String toString(final Date[Time] date)
The needed time-zone may be retrieved from DateTimeZone timeZone()
method. IUniversalConstants
methods DateTime now()
and DateTime today()
are also suitable, as well as DateTime now()
in CommonEntityDao/AbstractBefore[After]ChangeEventHandler
.
Please note, that these considerations are applicable for both dependent
and independent
time-zone modes
.
Migration to multi time-zone environment
new Date()
=>now().toDate()
new Date(System.currentTimeMillis())
=>now().toDate()
new Date(System.currentTimeMillis() + deltaMillis)
=>now().plus(deltaMillis).toDate()
new DateTime()
=>now()
new DateTime(date)
=>dates.zoned(date)
new DateTime(date.getTime())
=>dates.zoned(date)
- deprecated
new DateMidnight(dateTime)
=>dateTime.withTimeAtStartOfDay()
new DateTime(y, m, d, h, m)
=>new DateTime(y, m, d, h, m, dates.timeZone())
new DateTime(y, m, d, h, m, s)
=>new DateTime(y, m, d, h, m, s, dates.timeZone())
new DateTime(y, m, d, h, m, s, m)
=>new DateTime(y, m, d, h, m, s, m, dates.timeZone())
DateTime.now()
=> ournow()
Local[Date]Time.toDateTime[Today]()
=>Local[Date]Time.toDateTime[Today](dates.timeZone())
- TODO continue about
EntityUtils.toString
,DateTimeUtil.to[DateOnly]String
,SimpleDateFormat.format
... (see also Bijection between client- and sever-side date/time formats #1661)
Do not use:
java.util.Calendar
Date.toString()
- any deprecated
Date
method (new Date("...")
,new Date(2024, 5, 1)
,Date.parse("...")
etc.) - any deprecated Joda-Time class (
org.joda.time.DateMidnight
)
Safe operations (if all arguments were constructed properly):
DateTime.toDate()
Date.before/after(Date other)
Date.getTime()
Date.equals(Date date)
new Date(millis)
Lets consider situation where no IDates API was used. I.e. instead of IDates.now we have used new Date() and new DateTime(); instead of dates.zoned(date) we have used new DateTime(date). And instead of dates.toString(), Date.toString() and EntityUtils.toString() and DateTimeUtil.toString methods were used. In this case, for functionality to work in multiple time-zones we need:
- dependent time-zone mode -- all three methods must be used to properly work ('now', 'zoned' and 'toString')
- independent time-zone mode -- only 'now' method is essential, because 'zoned' = new DateTime(date, timeZone()) = new DateTime(date, getDefault()) = new DateTime(date); (and derived 'toString' will work too)