The handling of date and time is a complicated mess of historical
conventions, which are still changed from time to time by governments around
the world. To keep this away from day-to-day activities of developers,
the OX App Suite platform provides the module date
, which performs
conversion between different time zones, formatting and parsing of date and time
values.
The built-in JavaScript class Date
supports calculations using
UTC and the operating system's local time zone. Unfortunately, this is not
enough. Examples include using a time zone different from the time zone of
the client system, or displaying times in multiple time zones at once.
The class Local
is almost a drop-in replacement for
Date
, with the main difference being that it operates in
the default time zone of the OX App Suite user, even if it is different from
the time zone of the browser's operating system. The following code displays
the current date and time. It will show a different time when called after
changing the user's time zone.
To work with other time zones, the function getTimeZone()
can be
used to create replacement classes similar to Local
, which are
all descendants of the private class LocalDate
(for which all
methods are documented below). Since the time zone definitions are loaded
on-demand, the function returns a jQuery promise, which is resolved to the class
constructor once the time zone definition is loaded. This class can then be used
to e.g. convert between time zones.
The function getTimeZone()
is memoized for performance reasons,
i.e. multiple calls with the same argument will return the same promise and will
therefore resolve to the same class object.
The default LocalDate.prototype.toString()
method displays
the full date and time, including the day of week and the time zone. To allow
better control of the resulting string, the method
LocalDate.prototype.format()
accepts a set of format flags.
The flags determine, which fields should be included in the output. Currently,
there are four flags:
Multiple flags can be combined by adding them, or by using one of
the predefined combination constants. Not all possible combinations produce
unique results. When DAYOFWEEK
is specified together with any other
fields, DATE
is automatically included in the output. Similarly,
TIMEZONE
implies TIME
when used with other fields.
The format flags select one of several predefined format strings, which is stored in the current locale settings. This frees both the developers and the translators from having to remember arcane letter combinations.
For the case that even finer control of the generated output is required,
LocalDate.prototype.format()
accepts a format string directly.
The syntax of the format strings is a subset of
CLDR date format
patterns. Format specifiers which are not used in the Gregorian calendar
will be implemented on-demand.
In general, if format strings are required, this indicates that the current
date
API should be extended. Feedback is always welcome.
One situation where direct access to format strings is useful, is
the manipulation of the localized format strings, e.g. to decorate individual
fields in a displayed date with HTML markup. To achieve this, the localized
format string is retrieved with getFormat()
, and parts of it passed
individually to LocalDate.prototype.format()
.
Does anybody want this use case as an API function?
In addition to formatting individual dates, LocalDate
instances
can format intervals. The difference to simply inserting two dates into
a translated string is that short intervals may have a compacter representation,
e.g. 'Jan 1–10, 2012' instead of 'Jan 1, 2012 – Jan 10, 2012'.
The methods LocalDate.prototype.formatInterval()
and
LocalDate.prototype.getIntervalFormat()
are used like
LocalDate.prototype.format()
and
getFormat()
, with two differences. First, the interval functions
accept the end of the interval as first parameter before the format flags.
And second, since the format string depends on the actual interval,
LocalDate.prototype.getIntervalFormat()
is a member function and
requires the same end value as used later for
LocalDate.prototype.formatInterval()
.
Parsing is the reverse of formatting, except that ideally, users should be
able to enter almost anything, as long as it is not ambiguous. The practice
looks a bit more restricted. The LocalDate.parse()
function takes
a format parameter like the formatting functions, and expects the parsed string
to match it very closely.
If there is demand, we can implement some heuristics. There are a lot of abstract algorithm descriptions in the standards, which describe how to parse almost anything while using the format string only as disambiguation help.
LocalDate
objectsSince the LocalDate
class is designed as a drop-in for
the Date
class, its instances support getter and setter methods for
all fields of a date. They can be used to modify an existing
LocalDate
object, e. g. to round a date to the nearest day or
hour.
One manipulation, which is often needed in calendars, is iteration over time.
This operation seems simple, at least for iteration steps with a constant
duration like hours, days or weeks. One just needs to add the step duration
to the timestamp. While there is a method to do exactly this,
LocalDate.prototype.addUTC()
, you will encounter problems as soon
as the iteration transcends a daylight savings switch. First, days and weeks are
not actually constant. And second, sometimes the iteration might need to follow
the displayed 'wall clock' time instead of the physical time. For these cases,
the method LocalDate.prototype.add()
increments the local time
instead of the UTC time. Both methods accept an increment value in milliseconds,
which can also be negative. There are predefined
constants for the most common (almost-)fixed-duration
periods.
Iteration with larger intervals like months and years has the additional
difficulty, that a single numeric parameter like 30 * date.DAY
cannot be interpreted as a month, because it might be just really 30 days. To
solve this, there are separate methods for months and years:
LocalDate.prototype.addMonths()
and
LocalDate.prototype.addYears()
. They accept the number of months,
respective years as parameter.
Finally, most calendars default to displaying the current date, and therefore
need to figure out the iteration range based on an arbitrary point inside that
range. In most cases, the start can be computed by simply calling
the appropriate setters with all zeros as parameters to find the start of
the range, and adding the range duration to find the end. One exception is
the week. There is no setter for the day of the week. Instead, the method
LocalDate.prototype.setStartOfWeek()
can be used to find the start
of a week.
In addition to explicit method calls, the JavaScript standard method
LocalDate.prototype.valueOf()
allows native comparison, addition
and subtraction operators to work on LocalDate
objects. The result
of LocalDate.prototype.valueOf()
, and therefore of addition and
subtraction, is a timestamp, which can be passed to the LocalDate
constructor to get a LocalDate
object again.
LocalDate
and Date
The LocalDate
classes duplicate most of the Date
API with a few exceptions:
Date
constructor does in this case nothing useful anyway.Date.UTC()
should not be necessary, but since it returns
a numeric timestamp and has nothing to do with time zones, it can still be used
directly.LocalDate.prototype.toString()
, all
to*String()
methods are replaced by
LocalDate.prototype.format()
.LocalDate
class
for the time zone 'UTC' instead.getYear
and
setYear
instead of getFullYear
and
setFullYear
since we have no legacy code with Y2K issues.Date.prototype.getTimeZoneOffset()
should not be necessary,
since hiding these details is the whole point of the date
module.
If still necessary, LocalDate.getTTInfo()
can be used instead.This section provides a short reference of the date
API.
A locale describes the localization settings which usually depend not only
on thelanguage, but also on the region and optionally even on the personal
preferences of the user. The locale
object is loaded at startup
and provides various settings and translations.
Various arrays containing translations can be used directly without any other
date
function.
am
and pm
are used in the 12h time format, but other members may be useful for greetings.
Is anyone interested in a function which returns the correct
greeting period for a given time? If not, maybe remove everything except AM and
PM?Diverse week-based calculations need to know on which day the week starts and how the first week of the year is defined.
Deprecated and internal fields should not be used since they can change or disappear entirely without notice. They are still documented here for completeness.
DATE
instead.DATE_TIME
instead.%1$s
) and a date
(%2$s
). Not used yet, will probably disappear.getFormat()
.
Maybe remove it from the public interface after loading,
since it's internal?LocalDate.prototype.getIntervalFormat()
.
Maybe remove it from the public interface after loading,
since it's internal?%1$s
) and
end (%2$s
) of an interval when none of the other members of
locale.intervals
apply. Maybe remove it from
the public interface after loading, since it's internal?TIME
instead.The main entry points of the date
API are the class
Local
which replaces Date
for use with the user's
default time zone and a function to generate similar classes for arbitrary
time zones.
LocalDate
class which operates in the specified
time zone. Multiple calls with the same name will return the same object.
name
String - The name of the requested time
zone. It must be one of the values returned by
api/config/availableTimeZones
.LocalDate
class which uses the requested time
zone.LocalDate
class which uses the user's current
time zone.The core of the date
API is the abstract class
LocalDate
which is the superclass of time zone specific classes.
The class itself is not publically available, only its subclasses can be created
by calling getTimeZone()
. The subclasses and their instances are
referred to as LocalDate
classes and LocalDate
objects.
The constructor mimics the behavior of the Date
class, but it
can't be called as a function.
Date
constructor.The entire functionality of the class is based on a few low level functions.
They should not be necessary outside the date
module itself,
assuming the API is complete. If you find you need these
functions, please let's extend the high level APIs instead.
t
Timestamp - The timestamp.!isdst
) or 'CEST' (!!isdst
).LocalDate.utc()
are used. (It is actually implemented as
a wrapper for this function.)
t
Number - The local time.!isdst
) or 'CEST' (!!isdst
).t
Timestamp - The UTC timestamp to
convert.t
Number - The local time to convert.Metadata about a time zone is stored directly on the LocalDate
object.
Parsing of date and time strings as entered by a user is done using format flags from Constants. The current implementation expects the string to match the corresponding localized format string pretty closely. Any difficulties with parsing common entered dates and times should be taken as an opportunity to extend the parsing heuristics.
string
String - The string to parse.format
String or Number - Either a format
string with the syntax of
CLDR
date format patterns, or one of the format
flag constants. In the second case, the actual format string is
localized for the current user's locale according to
locale.formats
.LocalDate
object which represents the parsed date and
time or null if the string could not be parsed.Methods of LocalDate
instances can be grouped into several
categories. The first are the setters and getters for individual fields from
Date
. Since each LocalDate
class has its own time
zone, There are no UTC variants of each getter and setter. They all work with
local time.
year
Number - The year.month
Number - The month. Values range from
0 for January to 11 for December.date
Number - The date. Values range from 1
to 31.hour
Number - The hours. Values range from 0
to 23.min
Number - The minutes. Values range from
0 to 59.sec
Number - The seconds. Values range from
0 to 59.ms
Number - The milliseconds. Values range
from 0 to 999.Date
. It returns
the abbreviation of the specific time zone. The abbreviation indicate
the GMT offset and is therefore different between daylight savings time and
standard time.
Other getters and setters work with the entire timestamp and not just individual fields.
DAY
).
time
Timestamp - The new UTC timestamp of
this object.While setters can be used to perform date arithmetic, LocalDate
provides convenience functions for the most frequent cases of adding and
subtracting a time period and finding the start of a week.
time
Number - The time period to add, in
milliseconds. Use negative values to subtract. See also
Constants.time
Number - The time period to add, in
milliseconds. Use negative values to subtract. See also
Constants.months
Number - The number of months to add.
Use negative values to subtract.years
Number - The number of years to add.
Use negative values to subtract.locale.weekStart
. The time is reset to midnight.
Formatting functions implement the conversion of dates and intervals into localized strings which are suitable for direct presentation to the user.
format
String or Number - Either a format
string with the syntax of
CLDR
date format patterns, or one of the format
flag constants. In the second case, the actual format string is
localized for the current user's locale according to
locale.formats
. The default value is
DATE_TIME
.LocalDate
object as the end.
end
LocalDate - The end of
the interval.format
String or Number - Either a format
string with the syntax of
CLDR
date format patterns, or one of the format
flag constants. In the second case, the actual format string is
localized for the current user's locale according to
locale.intervals
and locale.formats
.
The default value is DATE_TIME
.LocalDate
object as the end.
end
LocalDate - The end of
the interval.format
String or Number - Either a format
string with the syntax of
CLDR
date format patterns, or one of the format
flag constants. In the second case, the actual format string is
localized for the current user's locale according to
locale.intervals
and locale.formats
.
The default value is DATE_TIME
.Finally, the JavaScript standard conversion functions allow easy debugging and arithmetic on timestamps.
this.format(FULL_DATE)
.
LocalDate
objects and as the single parameter to
new LocalDate()
.
All date classes operate on timestamps expressed as milliseconds since
the UNIX epoch, 1970-01-01 00:00 UTC. The date
module defines
constants for common time intervals with a constant duration.
Format flags for parsing and formatting functions are defined as constants. Multiple flags can be combined via addition or bitwise ORing.
Valid format flag combinations have dedicated constants. In a combination,
DAYOFWEEK
implies DATE
and
TIMEZONE
implies TIME
.
DAYOFWEEK + DATE
DATE + TIME
DAYOFWEEK + DATE + TIME
TIME + TIMEZONE
DATE + TIME + TIMEZONE
DAYOFWEEK + DATE + TIME + TIMEZONE
See Formatting for usage of this function.
format
String or Number - Either a format
string with the syntax of
CLDR
date format patterns, or one of the format
flag constants. In the second case, the actual format string is
localized for the current user's locale according to
locale.formats
. The default value is
DATE_TIME
.format
is
a string, that string is returned unmodified.