• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1---
2layout: default
3title: Universal Time Scale
4nav_order: 5
5parent: Date/Time
6---
7<!--
8© 2020 and later: Unicode, Inc. and others.
9License & terms of use: http://www.unicode.org/copyright.html
10-->
11
12# Universal Time Scale
13{: .no_toc }
14
15## Contents
16{: .no_toc .text-delta }
17
181. TOC
19{:toc}
20
21---
22
23## Overview
24
25There are quite a few different conventions for binary datetime, depending on
26the platform or protocol. Some of these have severe drawbacks. For example,
27people using Unix time (seconds since Jan 1, 1970, usually in a 32-bit integer)
28think that they are safe until near the year 2038. But cases can and do arise
29where arithmetic manipulations causes serious problems. Consider the computation
30of the average of two datetimes, for example: if one calculates them with
31`averageTime = (time1 + time2)/2`, there will be overflow even with dates
32beginning in 2004. Moreover, even if these problems don't occur, there is the
33issue of conversion back and forth between different systems.
34
35Binary datetimes differ in a number of ways: the data type, the unit, and the
36epoch (origin). We'll refer to these as time scales. For example: (Sorted by
37epoch and unit, descending. In Java, `int64_t`=`long` and `int32_t`=`int`.)
38
39| Source                                         | Data Type                                                                          | Epoch       | Unit                                                    |
40| ---------------------------------------------- | ---------------------------------------------------------------------------------- | ----------- | ------------------------------------------------------- |
41| MacOS X (`CFDate/NSDate`)                      | `double` (1.0=1s but fractional seconds are used as well; imprecise for 0.1s etc.) | 2001-Jan-01 | seconds (and fractions thereof)                         |
42| Unix `time_t`                                  | `int32_t` or `int64_t` (`signed int32_t` limited to 1970..2038)                    | 1970-Jan-01 | seconds                                                 |
43| Java `Date`                                    | `int64_t`                                                                          | 1970-Jan-01 | milliseconds                                            |
44| Joda `DateTime`                                | `int64_t`                                                                          | 1970-Jan-01 | milliseconds                                            |
45| ICU4C `UDate`                                  | `double` (does not use fractional milliseconds)                                    | 1970-Jan-01 | milliseconds                                            |
46| JavaScript `Date`                              | `double` (does not use fractional milliseconds; JavaScript Number stores a double) | 1970-Jan-01 | milliseconds                                            |
47| Unix `struct timeval (as in gettimeofday)`     | `struct: time_t` (seconds); suseconds_t (microseconds)                             | 1970-Jan-01 | microseconds                                            |
48| Gnome `g_get_real_time()`                      | `gint64`                                                                           | 1970-Jan-01 | microseconds                                            |
49| Unix `struct timespec` (as in `clock_gettime`) | `struct: time_t` (seconds); long (nanoseconds)                                     | 1970-Jan-01 | nanoseconds                                             |
50| MacOS (old)                                    | `uint32_t` (1904..2040)                                                            | 1904-Jan-01 | seconds                                                 |
51| Excel                                          | ?                                                                                  | 1899-Dec-31 | days                                                    |
52| DB2                                            | ?                                                                                  | 1899-Dec-31 | days                                                    |
53| Windows `FILETIME`                             | `int64_t`                                                                          | 1601-Jan-01 | ticks (100 nanoseconds; finest granularity in industry) |
54| .NET `DateTime`                                | `uint62` (only 0001-9999; only 62 bits; also 2-bit field for UTC/local)            | 0001-Jan-01 | ticks (100 nanoseconds; finest granularity in industry) |
55| ICU Universal Time Scale                       | `int64_t`                                                                          | 0001-Jan-01 | same as .Net but allows 29000BC..29000AD                |
56
57All of the epochs start at 00:00 am (the earliest possible time on the day in
58question), and are usually assumed to be UTC.
59
60The ranges, in years, for different data types are given in the following table.
61The range for integer types includes the entire range expressible with positive
62and negative values of the data type. The range for double is the range that
63would be allowed without losing precision to the corresponding unit.
64
65| Units                  | 64-bit integer          | Double         | 32-bit integer |
66| ---------------------- | ----------------------- | -------------- | -------------- |
67| 1 second               | 5.84542x10<sup>11</sup> | 285,420,920.94 | 136.10         |
68| 1 millisecond          | 584,542,046.09          | 285,420.92     | 0.14           |
69| 1 microsecond          | 584,542.05              | 285.42         | 0.00           |
70| 100 nanoseconds (tick) | 58,454.20               | 28.54          | 0.00           |
71| 1 nanosecond           | 584.5420461             | 0.2854         | 0.00           |
72
73ICU implements a universal time scale that is similar to the
74[.NET framework's System.DateTime](https://docs.microsoft.com/dotnet/api/system.datetime?view=netframework-4.8).
75The universal time scale is a 64-bit integer that holds ticks since midnight,
76January 1<sup>st</sup>, 0001. Negative values are supported. This has enough
77range to guarantee that calculations involving dates around the present are safe.
78
79The universal time scale always measures time according to the proleptic
80Gregorian calendar. That is, the Gregorian calendar's leap year rules are used
81for all times, even before 1582 when it was introduced. (This is different from
82the default ICU calendar which switches from the Julian to the Gregorian
83calendar in 1582. See `GregorianCalendar::setGregorianChange()` and
84`ucal_setGregorianChange()`).
85
86ICU provides conversion functions to and from all other major time scales,
87allowing datetimes in any time scale to be converted to the universal time
88scale, safely manipulated, and converted back to any other datetime time scale.
89
90## Background
91
92So how did we decide what to use for the universal time scale? Java time has
93plenty of range, but cannot represent a .NET `System.DateTime` value without
94severe loss of precision. ICU4C time addresses this by using a `double` that is
95otherwise equivalent to the Java time. However, there are disadvantages with
96doubles. They provide for much more graceful degradation in arithmetic
97operations. But they only have 53 bits of accuracy, which means that they will
98lose precision when converting back and forth to ticks. What would really be
99nice would be a `long double` (80 bits -- 64 bit mantissa), but that is not
100supported on most systems.
101
102The Unix extended time uses a structure with two components: time in seconds and
103a fractional field (microseconds). However, this is clumsy, slow, and prone to
104error (you always have to keep track of overflow and underflow in the fractional
105field). `BigDecimal` would allow for arbitrary precision and arbitrary range, but
106we did not want to use this as the normal type, because it is slow and does not
107have a fixed size.
108
109Because of these issues, we concluded that the .NET `System.DateTime` is the best
110timescale to use. However, we use the full range allowed by the data type,
111allowing for datetimes back to 29,000 BC and up to 29,000 AD. (`System.DateTime`
112uses only 62 bits and only supports dates from 0001 AD to 9999 AD). This time
113scale is very fine grained, does not lose precision, and covers a range that
114will meet almost all requirements. It will not handle the range that Java times
115do, but frankly, being able to handle dates before 29,000 BC or after 29,000 AD
116is of very limited interest.
117
118## Constants
119
120ICU provides routines to convert from other timescales to the universal time
121scale, to convert from the universal time scale to other timescales, and to get
122information about a particular timescale. In all of these routines, the
123timescales are referenced using an integer constant, according to the following
124table:
125
126| Source                 | ICU4C                         | ICU4J                    |
127| ---------------------- | ----------------------------- | ------------------------ |
128| Java                   | `UDTS_JAVA_TIME`              | `JAVA_TIME`              |
129| Unix                   | `UDTS_UNIX_TIME`              | `UNIX_TIME`              |
130| ICU4C                  | `UDTS_ICU4C_TIME`             | `ICU4C_TIME`             |
131| Windows FILETIME       | `UDTS_WINDOWS_FILE_TIME`      | `WINDOWS_FILE_TIME`      |
132| .NET DateTime          | `UDTS_DOTNET_DATE_TIME`       | `DOTNET_DATE_TIME`       |
133| Macintosh (old)        | `UDTS_MAC_OLD_TIME`           | `MAC_OLD_TIME`           |
134| Macintosh              | `UDTS_MAC_TIME`               | `MAC_TIME`               |
135| Excel                  | `UDTS_EXCEL_TIME`             | `EXCEL_TIME`             |
136| DB2                    | `UDTS_DB2_TIME`               | `DB2_TIME`               |
137| Unix with microseconds | `UDTS_UNIX_MICROSECONDS_TIME` | `UNIX_MICROSECONDS_TIME` |
138
139The routine that gets a particular piece of information about a timescale takes
140an integer constant that identifies the particular piece of information,
141according to the following table:
142
143| Value                | ICU4C                      | ICU4J                |
144| -------------------- | -------------------------- | -------------------- |
145| Precision            | `UTSV_UNITS_VALUE`         | `UNITS_VALUE`        |
146| Epoch offset         | `UTSV_EPOCH_OFFSET_VALUE`  | `EPOCH_OFFSET_VALUE` |
147| Minimum "from" value | `UTSV_FROM_MIN_VALUE`      | `FROM_MIN_VALUE`     |
148| Maximum "from" value | `UTSV_FROM_MAX_VALUE`      | `FROM_MAX_VALUE`     |
149| Minimum "to" value   | `UTSV_TO_MIN_VALUE`        | `TO_MIN_VALUE`       |
150| Maximum "to" value   | `UTSV_TO_MAX_VALUE`        | `TO_MAX_VALUE`       |
151
152Here is what the values mean:
153
154* Precision -- the precision of the timescale, in ticks.
155* Epoch offset -- the distance from the universal timescale's epoch to the timescale's epoch, in the timescale's precision.
156* Minimum "from" value -- the minimum timescale value that can safely be converted to the universal timescale.
157* Maximum "from" value -- the maximum timescale value that can safely be converted to the universal timescale.
158* Minimum "to" value -- the minimum universal timescale value that can safely be converted to the timescale.
159* Maximum "to" value -- the maximum universal timescale value that can safely be converted to the timescale.
160
161## Converting
162
163You can convert from other timescale values to the universal timescale using the
164"from" methods. In ICU4C, you use `utmscale_fromInt64`:
165
166```c
167UErrorCode err = U_ZERO_ERROR;
168int64_t unixTime = ...;
169int64_t universalTime;
170
171universalTime = utmscale_fromInt64(unixTime, UDTS_UNIX_TIME, &err);
172```
173
174In ICU4J, you use `UniversalTimeScale.from`:
175
176```java
177long javaTime = ...;
178long universalTime;
179
180universalTime = UniversalTimeScale.from(javaTime, UniversalTimeScale.JAVA_TIME);
181```
182
183You can convert values in the universal timescale to other timescales using the
184"to" methods. In ICU4C, you use `utmscale_toInt64`:
185
186```c
187UErrorCode err = U_ZERO_ERROR;
188int64_t universalTime = ...;
189int64_t unixTime;
190
191unixTime = utmscale_toInt64(universalTime, UDTS_UNIX_TIME, &err);
192```
193
194In ICU4J, you use `UniversalTimeScale.to`:
195
196```java
197long universalTime = ...;
198long javaTime;
199
200javaTime = UniversalTimeScale.to(universalTime, UniversalTimeScale.JAVA_TIME);
201```
202
203That's all there is to it!
204
205If the conversion is out of range, the ICU4C routines
206will set the error code to `U_ILLEGAL_ARGUMENT_ERROR`, and the ICU4J methods will
207throw `IllegalArgumentException`. In ICU4J, you can avoid out of range conversions
208by using the `BigDecimal` methods:
209
210```java
211long fileTime = ...;
212double icu4cTime = ...;
213BigDecimal utICU4C, utFile, utUnix, unixTime, macTime;
214
215utFile   = UniversalTimeScale.bigDecimalFrom(fileTime, UniversalTime.WINDOWS_FILE_TIME);
216
217utICU4C  = UniversalTimeScale.bigDecimalFrom(icu4cTime, UniversalTimeScale.ICU4C_TIME);
218
219unixTime = UniversalTimeScale.toBigDecimal(utFile, UniversalTime.UNIX_TIME);
220macTime  = UniversalTimeScale.toBigDecimal(utICU4C, UniversalTime.MAC_TIME);
221
222utUnix   = UniversalTimeScale.bigDecimalFrom(unixTime, UniversalTime.UNIX_TIME);
223```
224
225> :point_right: **Note**: Because the Universal Time Scale has a finer resolution
226> than some other time scales, time values that can be represented exactly in the
227> Universal Time Scale will be rounded when converting to these time scales, and
228> resolution will be lost. If you convert these values back to the Universal Time
229> Scale, you will not get the same time value that you started with. If the time
230> scale to which you are converting uses a double to represent the time value, you
231> may loose precision even though the double supports a range that is larger than
232> the range supported by the Universal Time Scale.
233
234## Formatting and Parsing
235
236Currently, ICU does not support direct formatting or parsing of Universal Time
237Scale values. If you want to format a Universal Time Scale value, you will need
238to convert it to an ICU time scale value first. Use `UTDS_ICU4C_TIME` with ICU4C,
239and `UniversalTimeScale.JAVA_TIME` with ICU4J.
240
241When you parse a datetime string, the result will be an ICU time scale value.
242You can convert this value to a Universal Time Scale value using `UDTS_ICU4C_TIME`
243with ICU4C, and `UniversalTime.JAVA_TIME` for ICU4J.
244
245See the previous section, *Converting*, for details of how to do the conversion.
246
247## Getting Timescale Information
248
249To get information about a particular timescale in ICU4C, use
250`utmscale_getTimeScaleValue`:
251
252```c
253UErrorCode err = U_ZERO_ERROR;
254int64_t unixEpochOffset = utmscale_getTimeScaleValue(
255    UDTS_UNIX_TIME,
256    UTSV_EPOCH_OFFSET_VALUE,
257    &err);
258```
259
260In ICU4J, use `UniversalTimeScale.getTimeScaleValue`:
261
262```java
263long javaEpochOffset = UniversalTimeScale.getTimeScaleValue(
264    UniversalTimeScale.JAVA_TIME,
265    UniversalTimeScale.EPOCH_OFFSET_VALUE);
266```
267
268If the integer constants for selecting the timescale or the timescale value are
269out of range, the ICU4C routines will set the error code to
270`U_ILLEGAL_ARGUMENT_ERROR`, and the ICU4J methods will throw
271`IllegalArgumentException`.
272