View Javadoc
1   package org.djunits.unit;
2   
3   import java.util.GregorianCalendar;
4   
5   import org.djunits.unit.quantity.Quantity;
6   import org.djunits.unit.scale.OffsetLinearScale;
7   import org.djunits.unit.si.SIPrefixes;
8   import org.djunits.unit.unitsystem.UnitSystem;
9   
10  /**
11   * Standard absolute time units. Note that when the offset of a stored absolute Time becomes large, precision of a float or
12   * double might not be enough for the required resolution of a Time. A float has around 7 significant digits (23 bit mantissa),
13   * whereas a double has around 16 significant digits (52 bit mantissa). This means that when we need to have a float time that
14   * is precise to microseconds, the Time value should not go above 2^22 = 4.0E6. This is <b>not</b> enough to store Epoch values!
15   * So feeding System.TimeInMillis() to a FloatTime with TimeUnit.BASE as its unit is not having the required precision. For a
16   * (double) Time with TimeUnit.BASE as its unit, the largest value where the ms precision is reached is 2^51 = 2.3E15, which is
17   * around 71000 years. This is sufficient to store a date on an Epoch level precise to a ms.
18   * <p>
19   * Copyright (c) 2015-2023 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
20   * BSD-style license. See <a href="https://djunits.org/docs/license.html">DJUNITS License</a>.
21   * <p>
22   * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
23   */
24  public class TimeUnit extends AbsoluteLinearUnit<TimeUnit, DurationUnit>
25  {
26      /** */
27      private static final long serialVersionUID = 20140607L;
28  
29      /** The base, with "m2" as the SI signature. */
30      public static final Quantity<TimeUnit> BASE = new Quantity<>("Time", "s");
31  
32      /**
33       * The base unit for time with an artifical "zero" point with a calculation in seconds. Note that when the offset becomes
34       * large, precision of a float or double might not be enough for the required resolution of a Time. A float has around 7
35       * significant digits (23 bit mantissa), whereas a double has around 16 significant digits (52 bit mantissa). This means
36       * that when we need to have a float time that is precise to microseconds, the Time value should not go above 2^22 = 4.0E6.
37       * This is <b>not</b> enough to store Epoch values! So feeding System.TimeInMillis() to a FloatTime with TimeUnit.BASE as
38       * its unit is not having the required precision. For a (double) Time with TimeUnit.BASE as its unit, the largest value
39       * where the ms precision is reached is 2^51 = 2.3E15, which is around 71000 years. This is sufficient to store a date on an
40       * Epoch level precise to a ms.
41       */
42      public static final TimeUnit BASE_SECOND = new TimeUnit().build(new AbsoluteLinearUnit.Builder<TimeUnit, DurationUnit>()
43              .setQuantity(BASE).setId("s").setName("second").setUnitSystem(UnitSystem.SI_DERIVED)
44              .setSiPrefixes(SIPrefixes.UNIT, 1.0).setRelativeUnit(DurationUnit.SECOND).setScale(new OffsetLinearScale(1.0, 0.0))
45              .setDefaultDisplayAbbreviation("s").setDefaultTextualAbbreviation("s").setAdditionalAbbreviations("sec"));
46  
47      /** The default unit for time is BASE_SECOND. */
48      public static final TimeUnit DEFAULT = BASE_SECOND;
49  
50      /** The base unit for time with an artificial "zero" point with a calculation in microseconds. */
51      public static final TimeUnit BASE_MICROSECOND = BASE_SECOND.deriveLinearOffset(1.0E-6, 0.0, DurationUnit.MICROSECOND, "mus",
52              "microsecond", UnitSystem.SI_DERIVED, "\u03BCs", "mus", "\u03BCsec", "musec");
53  
54      /** The base unit for time with an artificial "zero" point with a calculation in milliseconds. */
55      public static final TimeUnit BASE_MILLISECOND = BASE_SECOND.deriveLinearOffset(1.0E-3, 0.0, DurationUnit.MILLISECOND, "ms",
56              "millisecond", UnitSystem.SI_DERIVED, "ms", "ms", "msec");
57  
58      /** The base unit for time with an artificial "zero" point with a calculation in minutes. */
59      public static final TimeUnit BASE_MINUTE = BASE_SECOND.deriveLinearOffset(60.0, 0.0, DurationUnit.MINUTE, "min", "minute",
60              UnitSystem.SI_DERIVED, "min", "min");
61  
62      /** The base unit for time with an artificial "zero" point with a calculation in hours. */
63      public static final TimeUnit BASE_HOUR = BASE_SECOND.deriveLinearOffset(3600.0, 0.0, DurationUnit.HOUR, "h", "hour",
64              UnitSystem.SI_DERIVED, "h", "h", "hr", "hour");
65  
66      /** The base unit for time with an artificial "zero" point with a calculation in days. */
67      public static final TimeUnit BASE_DAY = BASE_SECOND.deriveLinearOffset(24.0 * 3600.0, 0.0, DurationUnit.DAY, "day", "day",
68              UnitSystem.SI_DERIVED, "day", "day");
69  
70      /** The base unit for time with an artificial "zero" point with a calculation in weeks. */
71      public static final TimeUnit BASE_WEEK = BASE_SECOND.deriveLinearOffset(7.0 * 24.0 * 3600.0, 0.0, DurationUnit.WEEK, "wk",
72              "week", UnitSystem.SI_DERIVED, "wk", "wk", "week");
73  
74      /**
75       * The POSIX and Gregorian Epoch: January 1, 1970 at 00:00 UTC with a calculation in seconds. The base should be taken in
76       * such a way that a resolution of a millisecond is still 'visible' on a date in, say, 2020. When 1-1-1970 is used as the
77       * origin, 1-1-2021 has a value of 1,577,836,800,000 milliseconds = 1.6E12 ms. If we want to be precise on the ms level, we
78       * need 12 significant digits. A float has around 7 significant digits (23 bit mantissa), whereas a double has around 16
79       * significant digits (52 bit mantissa). This means that a float time with an offset of 1-1-1970 is at best precise to a
80       * minute level. A double time is precise to microseconds. Therefore, avoid using float times that use the EPOCH.
81       */
82      public static final TimeUnit EPOCH_SECOND = BASE_SECOND.deriveLinearOffset(1.0, 0.0, DurationUnit.SECOND, "s(Y1970)",
83              "seconds since 1/1/70", UnitSystem.OTHER, "s(Y1970)", "s(Y1970)", "sec(Y1970)");
84  
85      /** The POSIX and Gregorian Epoch: January 1, 1970 at 00:00 UTC with a calculation in microseconds. */
86      public static final TimeUnit EPOCH_MICROSECOND =
87              BASE_SECOND.deriveLinearOffset(1.0E-6, 0.0, DurationUnit.MICROSECOND, "mus(Y1970)", "microseconds since 1/1/70",
88                      UnitSystem.OTHER, "\u03BCs(Y1970)", "mus(Y1970)", "\u03BCsec(Y1970)", "musec(Y1970)");
89  
90      /** The POSIX and Gregorian Epoch: January 1, 1970 at 00:00 UTC with a calculation in milliseconds. */
91      public static final TimeUnit EPOCH_MILLISECOND = BASE_SECOND.deriveLinearOffset(1.0E-3, 0.0, DurationUnit.MILLISECOND,
92              "ms(Y1970)", "milliseconds since 1/1/70", UnitSystem.OTHER, "ms(Y1970)", "ms(Y1970)", "msec(Y1970)");
93  
94      /** The POSIX and Gregorian Epoch: January 1, 1970 at 00:00 UTC with a calculation in minutes. */
95      public static final TimeUnit EPOCH_MINUTE = BASE_SECOND.deriveLinearOffset(60.0, 0.0, DurationUnit.MINUTE, "min(Y1970)",
96              "minutes since 1/1/70", UnitSystem.OTHER, "min(Y1970)", "min(Y1970)");
97  
98      /** The POSIX and Gregorian Epoch: January 1, 1970 at 00:00 UTC with a calculation in hours. */
99      public static final TimeUnit EPOCH_HOUR = BASE_SECOND.deriveLinearOffset(3600.0, 0.0, DurationUnit.HOUR, "h(Y1970)",
100             "hours since 1/1/70", UnitSystem.OTHER, "h(Y1970)", "h(Y1970)", "hour(Y1970)", "hr(Y1970)");
101 
102     /** The POSIX and Gregorian Epoch: January 1, 1970 at 00:00 UTC with a calculation in days. */
103     public static final TimeUnit EPOCH_DAY = BASE_SECOND.deriveLinearOffset(24.0 * 3600.0, 0.0, DurationUnit.DAY, "day(Y1970)",
104             "days since 1/1/70", UnitSystem.OTHER, "day(Y1970)", "day(Y1970)");
105 
106     /** The POSIX and Gregorian Epoch: January 1, 1970 at 00:00 UTC with a calculation in weeks. */
107     public static final TimeUnit EPOCH_WEEK = BASE_SECOND.deriveLinearOffset(7.0 * 24.0 * 3600.0, 0.0, DurationUnit.WEEK,
108             "wk(Y1970)", "weeks since 1/1/70", UnitSystem.OTHER, "wk(Y1970)", "wk(Y1970)", "week(Y1970)");
109 
110     /**
111      * The Epoch with 0001-01-01 AD at 00:00 as the origin with a calculation in seconds. When 1-1-0001 is used as the origin,
112      * 1-1-2021 has a value of around 6.4E13 ms. If we want to be precise on the ms level, we need 13 significant digits. A
113      * float has around 7 significant digits (23 bit mantissa), whereas a double has around 16 significant digits (52 bit
114      * mantissa). This means that a float time with an offset of 1-1-0001 is at best precise to an hour level. A double time is
115      * precise to microseconds. Therefore, avoid using float times that use the EPOCH_YEAR1_SECOND.
116      */
117     public static final TimeUnit EPOCH_YEAR1_SECOND = EPOCH_SECOND.deriveLinearOffset(1.0,
118             new GregorianCalendar(1, 0, 1, 0, 0, 0).getTimeInMillis() / 1000.0, DurationUnit.SECOND, "s(Y1)",
119             "seconds since 1-1-0001 00:00", UnitSystem.OTHER, "s(Y1)", "s(Y1)", "sec(Y1)");
120 
121     /**
122      * The Epoch with J2000.0 as the origin, which is The Gregorian date January 1, 2000 at 12:00 GMT (noon) with a calculation
123      * in seconds. When 1-1-2000 is used as the origin, 1-1-2021 has a value of around 6.3E11 ms. If we want to be precise on
124      * the ms level, we need 11 significant digits. A float has around 7 significant digits (23 bit mantissa), whereas a double
125      * has around 16 significant digits (52 bit mantissa). This means that a float time with an offset of 1-1-2000 is at best
126      * precise to a minute level. A double time is precise to fractions of microseconds. Therefore, avoid using float times that
127      * use the EPOCH_J2000_SECOND.
128      */
129     public static final TimeUnit EPOCH_J2000_SECOND = EPOCH_SECOND.deriveLinearOffset(1.0,
130             new GregorianCalendar(2000, 0, 1, 12, 0, 0).getTimeInMillis() / 1000.0, DurationUnit.SECOND, "s(Y2000)",
131             "seconds since 1-1-2000 12:00 GMT", UnitSystem.OTHER, "s(Y2000)", "s(Y2000)", "sec(Y2000)");
132 
133 }