KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > ibm > icu > util > UniversalTimeScale


1 /*
2  *********************************************************************************
3  * Copyright (C) 2004 -2006, International Business Machines Corporation and *
4  * others. All Rights Reserved. *
5  *********************************************************************************
6  *
7  */

8
9 package com.ibm.icu.util;
10
11 import com.ibm.icu.math.BigDecimal;
12 import java.lang.IllegalArgumentException JavaDoc;
13
14 /**
15  * There are quite a few different conventions for binary datetime, depending on different
16  * platforms and protocols. Some of these have severe drawbacks. For example, people using
17  * Unix time (seconds since Jan 1, 1970, usually in a 32-bit integer)
18  * think that they are safe until near the year 2038.
19  * But cases can and do arise where arithmetic manipulations causes serious problems. Consider
20  * the computation of the average of two datetimes, for example: if one calculates them with
21  * <code>averageTime = (time1 + time2)/2</code>, there will be overflow even with dates
22  * beginning in 2004. Moreover, even if these problems don't occur, there is the issue of
23  * conversion back and forth between different systems.
24  *
25  * <p>Binary datetimes differ in a number of ways: the datatype, the unit,
26  * and the epoch (origin). We refer to these as time scales.</p>
27  *
28  * <p>ICU implements a universal time scale that is similar to the
29  * .NET framework's System.DateTime. The universal time scale is a
30  * 64-bit integer that holds ticks since midnight, January 1st, 0001.
31  * (One tick is 100 nanoseconds.)
32  * Negative values are supported. This has enough range to guarantee that
33  * calculations involving dates around the present are safe.</p>
34  *
35  * <p>The universal time scale always measures time according to the
36  * proleptic Gregorian calendar. That is, the Gregorian calendar's
37  * leap year rules are used for all times, even before 1582 when it was
38  * introduced. (This is different from the default ICU calendar which
39  * switches from the Julian to the Gregorian calendar in 1582.
40  * See GregorianCalendar.setGregorianChange() and ucal_setGregorianChange().)</p>
41  *
42  * ICU provides conversion functions to and from all other major time
43  * scales, allowing datetimes in any time scale to be converted to the
44  * universal time scale, safely manipulated, and converted back to any other
45  * datetime time scale.</p>
46  *
47  * <p>For more details and background, see the
48  * <a HREF="http://icu.sourceforge.net/userguide/universalTimeScale.html">Universal Time Scale</a>
49  * chapter in the ICU User Guide.</p>
50  *
51  * @draft ICU 3.2
52  * @provisional This API might change or be removed in a future release.
53  */

54
55 public final class UniversalTimeScale
56 {
57     /**
58      * Used in the JDK. Data is a <code>long</code>. Value
59      * is milliseconds since January 1, 1970.
60      *
61      * @draft ICU 3.2
62      * @provisional This API might change or be removed in a future release.
63      */

64     public static final int JAVA_TIME = 0;
65
66     /**
67      * Used in Unix systems. Data is an <code>int</code> or a <code>long</code>. Value
68      * is seconds since January 1, 1970.
69      *
70      * @draft ICU 3.2
71      * @provisional This API might change or be removed in a future release.
72      */

73     public static final int UNIX_TIME = 1;
74
75     /**
76      * Used in the ICU4C. Data is a <code>double</code>. Value
77      * is milliseconds since January 1, 1970.
78      *
79      * @draft ICU 3.2
80      * @provisional This API might change or be removed in a future release.
81      */

82     public static final int ICU4C_TIME = 2;
83
84     /**
85      * Used in Windows for file times. Data is a <code>long</code>. Value
86      * is ticks (1 tick == 100 nanoseconds) since January 1, 1601.
87      *
88      * @draft ICU 3.2
89      * @provisional This API might change or be removed in a future release.
90      */

91     public static final int WINDOWS_FILE_TIME = 3;
92
93     /**
94      * Used in the .NET framework's <code>System.DateTime</code> structure.
95      * Data is a <code>long</code>. Value is ticks (1 tick == 100 nanoseconds) since January 1, 0001.
96      *
97      * @draft ICU 3.2
98      * @provisional This API might change or be removed in a future release.
99      */

100     public static final int DOTNET_DATE_TIME = 4;
101
102     /**
103      * Used in older Macintosh systems. Data is an <code>int</code>. Value
104      * is seconds since January 1, 1904.
105      *
106      * @draft ICU 3.2
107      * @provisional This API might change or be removed in a future release.
108      */

109     public static final int MAC_OLD_TIME = 5;
110
111     /**
112      * Used in the JDK. Data is a <code>double</code>. Value
113      * is milliseconds since January 1, 2001.
114      *
115      * @draft ICU 3.2
116      * @provisional This API might change or be removed in a future release.
117      */

118     public static final int MAC_TIME = 6;
119
120     /**
121      * Used in Excel. Data is a <code>?unknown?</code>. Value
122      * is days since December 31, 1899.
123      *
124      * @draft ICU 3.2
125      * @provisional This API might change or be removed in a future release.
126      */

127     public static final int EXCEL_TIME = 7;
128
129     /**
130      * Used in DB2. Data is a <code>?unknown?</code>. Value
131      * is days since December 31, 1899.
132      *
133      * @draft ICU 3.2
134      * @provisional This API might change or be removed in a future release.
135      */

136     public static final int DB2_TIME = 8;
137     
138     /**
139      * This is the first unused time scale value.
140      *
141      * @draft ICU 3.2
142      * @provisional This API might change or be removed in a future release.
143      */

144     public static final int MAX_SCALE = 9;
145     
146     /**
147      * The constant used to select the units value
148      * for a time scale.
149      *
150      *
151      * @draft ICU 3.2
152      * @provisional This API might change or be removed in a future release.
153      */

154     public static final int UNITS_VALUE = 0;
155     
156     /**
157      * The constant used to select the epoch offset value
158      * for a time scale.
159      *
160      * @see #getTimeScaleValue
161      *
162      * @draft ICU 3.2
163      * @provisional This API might change or be removed in a future release.
164      */

165     public static final int EPOCH_OFFSET_VALUE = 1;
166     
167     /**
168      * The constant used to select the minimum from value
169      * for a time scale.
170      *
171      * @see #getTimeScaleValue
172      *
173      * @draft ICU 3.2
174      * @provisional This API might change or be removed in a future release.
175      */

176     public static final int FROM_MIN_VALUE = 2;
177     
178     /**
179      * The constant used to select the maximum from value
180      * for a time scale.
181      *
182      * @see #getTimeScaleValue
183      *
184      * @draft ICU 3.2
185      * @provisional This API might change or be removed in a future release.
186      */

187     public static final int FROM_MAX_VALUE = 3;
188     
189     /**
190      * The constant used to select the minimum to value
191      * for a time scale.
192      *
193      * @see #getTimeScaleValue
194      *
195      * @draft ICU 3.2
196      * @provisional This API might change or be removed in a future release.
197      */

198     public static final int TO_MIN_VALUE = 4;
199     
200     /**
201      * The constant used to select the maximum to value
202      * for a time scale.
203      *
204      * @see #getTimeScaleValue
205      *
206      * @draft ICU 3.2
207      * @provisional This API might change or be removed in a future release.
208      */

209     public static final int TO_MAX_VALUE = 5;
210     
211     /**
212      * The constant used to select the epoch plus one value
213      * for a time scale.
214      *
215      * NOTE: This is an internal value. DO NOT USE IT. May not
216      * actually be equal to the epoch offset value plus one.
217      *
218      * @see #getTimeScaleValue
219      *
220      * @draft ICU 3.2
221      * @provisional This API might change or be removed in a future release.
222      */

223     public static final int EPOCH_OFFSET_PLUS_1_VALUE = 6;
224     
225     /**
226      * The constant used to select the epoch offset minus one value
227      * for a time scale.
228      *
229      * NOTE: This is an internal value. DO NOT USE IT. May not
230      * actually be equal to the epoch offset value minus one.
231      *
232      * @see #getTimeScaleValue
233      *
234      * @internal
235      * @deprecated This API is ICU internal only.
236      */

237     public static final int EPOCH_OFFSET_MINUS_1_VALUE = 7;
238     
239     /**
240      * The constant used to select the units round value
241      * for a time scale.
242      *
243      * NOTE: This is an internal value. DO NOT USE IT.
244      *
245      * @see #getTimeScaleValue
246      *
247      * @internal
248      * @deprecated This API is ICU internal only.
249      */

250     public static final int UNITS_ROUND_VALUE = 8;
251     
252     /**
253      * The constant used to select the minimum safe rounding value
254      * for a time scale.
255      *
256      * NOTE: This is an internal value. DO NOT USE IT.
257      *
258      * @see #getTimeScaleValue
259      *
260      * @internal
261      * @deprecated This API is ICU internal only.
262      */

263     public static final int MIN_ROUND_VALUE = 9;
264     
265     /**
266      * The constant used to select the maximum safe rounding value
267      * for a time scale.
268      *
269      * NOTE: This is an internal value. DO NOT USE IT.
270      *
271      * @see #getTimeScaleValue
272      *
273      * @internal
274      * @deprecated This API is ICU internal only.
275      */

276     public static final int MAX_ROUND_VALUE = 10;
277     
278     /**
279      * The number of time scale values.
280      *
281      * NOTE: This is an internal value. DO NOT USE IT.
282      *
283      * @see #getTimeScaleValue
284      *
285      * @internal
286      * @deprecated This API is ICU internal only.
287      */

288     public static final int MAX_SCALE_VALUE = 11;
289     
290     private static final long ticks = 1;
291     private static final long microseconds = ticks * 10;
292     private static final long milliseconds = microseconds * 1000;
293     private static final long seconds = milliseconds * 1000;
294     private static final long minutes = seconds * 60;
295     private static final long hours = minutes * 60;
296     private static final long days = hours * 24;
297     
298     /**
299      * This class holds the data that describes a particular
300      * time scale.
301      *
302      * @internal
303      * @deprecated This API is ICU internal only.
304      */

305     private static final class TimeScaleData
306     {
307         TimeScaleData(long theUnits, long theEpochOffset,
308                        long theToMin, long theToMax,
309                        long theFromMin, long theFromMax)
310         {
311             units = theUnits;
312             unitsRound = theUnits / 2;
313             
314             minRound = Long.MIN_VALUE + unitsRound;
315             maxRound = Long.MAX_VALUE - unitsRound;
316                         
317             epochOffset = theEpochOffset / theUnits;
318             
319             if (theUnits == 1) {
320                 epochOffsetP1 = epochOffsetM1 = epochOffset;
321             } else {
322                 epochOffsetP1 = epochOffset + 1;
323                 epochOffsetM1 = epochOffset - 1;
324             }
325             
326             toMin = theToMin;
327             toMax = theToMax;
328             
329             fromMin = theFromMin;
330             fromMax = theFromMax;
331         }
332         
333         long units;
334         long epochOffset;
335         long fromMin;
336         long fromMax;
337         long toMin;
338         long toMax;
339         
340         long epochOffsetP1;
341         long epochOffsetM1;
342         long unitsRound;
343         long minRound;
344         long maxRound;
345     }
346     
347     private static final TimeScaleData[] timeScaleTable = {
348         new TimeScaleData(milliseconds, 621355968000000000L, -9223372036854774999L, 9223372036854774999L, -984472800485477L, 860201606885477L), // JAVA_TIME
349
new TimeScaleData(seconds, 621355968000000000L, -9223372036854775808L, 9223372036854775807L, -984472800485L, 860201606885L), // UNIX_TIME
350
new TimeScaleData(milliseconds, 621355968000000000L, -9223372036854774999L, 9223372036854774999L, -984472800485477L, 860201606885477L), // ICU4C_TIME
351
new TimeScaleData(ticks, 504911232000000000L, -8718460804854775808L, 9223372036854775807L, -9223372036854775808L, 8718460804854775807L), // WINDOWS_FILE_TIME
352
new TimeScaleData(ticks, 000000000000000000L, -9223372036854775808L, 9223372036854775807L, -9223372036854775808L, 9223372036854775807L), // DOTNET_DATE_TIME
353
new TimeScaleData(seconds, 600527520000000000L, -9223372036854775808L, 9223372036854775807L, -982389955685L, 862284451685L), // MAC_OLD_TIME
354
new TimeScaleData(seconds, 631139040000000000L, -9223372036854775808L, 9223372036854775807L, -985451107685L, 859223299685L), // MAC_TIME
355
new TimeScaleData(days, 599265216000000000L, -9223372036854775808L, 9223372036854775807L, -11368793L, 9981605L), // EXCEL_TIME
356
new TimeScaleData(days, 599265216000000000L, -9223372036854775808L, 9223372036854775807L, -11368793L, 9981605L) // DB2_TIME
357
};
358     
359     
360     /*
361      * Prevent construction of this class.
362      */

363     private UniversalTimeScale()
364     {
365         // nothing to do
366
}
367     
368     /**
369      * Convert a <code>long</code> datetime from the given time scale to the universal time scale.
370      *
371      * @param otherTime The <code>long</code> datetime
372      * @param timeScale The time scale to convert from
373      *
374      * @return The datetime converted to the universal time scale
375      *
376      * @draft ICU 3.2
377      * @provisional This API might change or be removed in a future release.
378      */

379     public static long from(long otherTime, int timeScale)
380     {
381         TimeScaleData data = fromRangeCheck(otherTime, timeScale);
382                 
383         return (otherTime + data.epochOffset) * data.units;
384     }
385
386     /**
387      * Convert a <code>double</code> datetime from the given time scale to the universal time scale.
388      * All calculations are done using <code>BigDecimal</code> to guarantee that the value
389      * does not go out of range.
390      *
391      * @param otherTime The <code>double</code> datetime
392      * @param timeScale The time scale to convert from
393      *
394      * @return The datetime converted to the universal time scale
395      *
396      * @draft ICU 3.2
397      * @provisional This API might change or be removed in a future release.
398      */

399     public static BigDecimal bigDecimalFrom(double otherTime, int timeScale)
400     {
401         TimeScaleData data = getTimeScaleData(timeScale);
402         BigDecimal other = new BigDecimal(String.valueOf(otherTime));
403         BigDecimal units = new BigDecimal(data.units);
404         BigDecimal epochOffset = new BigDecimal(data.epochOffset);
405         
406         return other.add(epochOffset).multiply(units);
407     }
408
409     /**
410      * Convert a <code>long</code> datetime from the given time scale to the universal time scale.
411      * All calculations are done using <code>BigDecimal</code> to guarantee that the value
412      * does not go out of range.
413      *
414      * @param otherTime The <code>long</code> datetime
415      * @param timeScale The time scale to convert from
416      *
417      * @return The datetime converted to the universal time scale
418      *
419      * @draft ICU 3.2
420      * @provisional This API might change or be removed in a future release.
421      */

422     public static BigDecimal bigDecimalFrom(long otherTime, int timeScale)
423     {
424         TimeScaleData data = getTimeScaleData(timeScale);
425         BigDecimal other = new BigDecimal(otherTime);
426         BigDecimal units = new BigDecimal(data.units);
427         BigDecimal epochOffset = new BigDecimal(data.epochOffset);
428         
429         return other.add(epochOffset).multiply(units);
430     }
431
432     /**
433      * Convert a <code>BigDecimal</code> datetime from the given time scale to the universal time scale.
434      * All calculations are done using <code>BigDecimal</code> to guarantee that the value
435      * does not go out of range.
436      *
437      * @param otherTime The <code>BigDecimal</code> datetime
438      * @param timeScale The time scale to convert from
439      *
440      * @return The datetime converted to the universal time scale
441      *
442      * @draft ICU 3.2
443      * @provisional This API might change or be removed in a future release.
444      */

445     public static BigDecimal bigDecimalFrom(BigDecimal otherTime, int timeScale)
446     {
447         TimeScaleData data = getTimeScaleData(timeScale);
448         
449         BigDecimal units = new BigDecimal(data.units);
450         BigDecimal epochOffset = new BigDecimal(data.epochOffset);
451         
452         return otherTime.add(epochOffset).multiply(units);
453     }
454
455     /**
456      * Convert a datetime from the universal time scale stored as a <code>BigDecimal</code> to a
457      * <code>long</code> in the given time scale.
458      *
459      * Since this calculation requires a divide, we must round. The straight forward
460      * way to round by adding half of the divisor will push the sum out of range for values
461      * within have the divisor of the limits of the precision of a <code>long</code>. To get around this, we do
462      * the rounding like this:
463      *
464      * <p><code>
465      * (universalTime - units + units/2) / units + 1
466      * </code>
467      *
468      * <p>
469      * (i.e. we subtract units first to guarantee that we'll still be in range when we
470      * add <code>units/2</code>. We then need to add one to the quotent to make up for the extra subtraction.
471      * This simplifies to:
472      *
473      * <p><code>
474      * (universalTime - units/2) / units - 1
475      * </code>
476      *
477      * <p>
478      * For negative values to round away from zero, we need to flip the signs:
479      *
480      * <p><code>
481      * (universalTime + units/2) / units + 1
482      * </code>
483      *
484      * <p>
485      * Since we also need to subtract the epochOffset, we fold the <code>+/- 1</code>
486      * into the offset value. (i.e. <code>epochOffsetP1</code>, <code>epochOffsetM1</code>.)
487      *
488      * @param universalTime The datetime in the universal time scale
489      * @param timeScale The time scale to convert to
490      *
491      * @return The datetime converted to the given time scale
492      *
493      * @draft ICU 3.2
494      * @provisional This API might change or be removed in a future release.
495      */

496     public static long toLong(long universalTime, int timeScale)
497     {
498         TimeScaleData data = toRangeCheck(universalTime, timeScale);
499         
500         if (universalTime < 0) {
501             if (universalTime < data.minRound) {
502                 return (universalTime + data.unitsRound) / data.units - data.epochOffsetP1;
503             }
504             
505             return (universalTime - data.unitsRound) / data.units - data.epochOffset;
506         }
507         
508         if (universalTime > data.maxRound) {
509             return (universalTime - data.unitsRound) / data.units - data.epochOffsetM1;
510         }
511         
512         return (universalTime + data.unitsRound) / data.units - data.epochOffset;
513     }
514     
515     /**
516      * Convert a datetime from the universal time scale to a <code>BigDecimal</code> in the given time scale.
517      *
518      * @param universalTime The datetime in the universal time scale
519      * @param timeScale The time scale to convert to
520      *
521      * @return The datetime converted to the given time scale
522      *
523      * @draft ICU 3.2
524      * @provisional This API might change or be removed in a future release.
525      */

526     public static BigDecimal toBigDecimal(long universalTime, int timeScale)
527     {
528         TimeScaleData data = getTimeScaleData(timeScale);
529         BigDecimal universal = new BigDecimal(universalTime);
530         BigDecimal units = new BigDecimal(data.units);
531         BigDecimal epochOffset = new BigDecimal(data.epochOffset);
532         
533         return universal.divide(units, BigDecimal.ROUND_HALF_UP).subtract(epochOffset);
534     }
535     
536     /**
537      * Convert a datetime from the universal time scale to a <code>BigDecimal</code> in the given time scale.
538      *
539      * @param universalTime The datetime in the universal time scale
540      * @param timeScale The time scale to convert to
541      *
542      * @return The datetime converted to the given time scale
543      *
544      * @draft ICU 3.2
545      * @provisional This API might change or be removed in a future release.
546      */

547     public static BigDecimal toBigDecimal(BigDecimal universalTime, int timeScale)
548     {
549         TimeScaleData data = getTimeScaleData(timeScale);
550         BigDecimal units = new BigDecimal(data.units);
551         BigDecimal epochOffset = new BigDecimal(data.epochOffset);
552         
553         return universalTime.divide(units, BigDecimal.ROUND_HALF_UP).subtract(epochOffset);
554     }
555     
556     /**
557      * Return the <code>TimeScaleData</code> object for the given time
558      * scale.
559      *
560      * @param scale - the time scale
561      *
562      * @return the <code>TimeScaleData</code> object for the given time scale
563      *
564      * @internal
565      * @deprecated This API is ICU internal only.
566      */

567     private static TimeScaleData getTimeScaleData(int scale)
568     {
569         if (scale < 0 || scale >= MAX_SCALE) {
570             throw new IllegalArgumentException JavaDoc("scale out of range: " + scale);
571         }
572         
573         return timeScaleTable[scale];
574     }
575     
576     /**
577      * Get a value associated with a particular time scale.
578      *
579      * @param scale - the time scale
580      * @param value - a constant representing the value to get
581      *
582      * @return - the value.
583      *
584      * @draft ICU 3.2
585      * @provisional This API might change or be removed in a future release.
586      */

587     public static long getTimeScaleValue(int scale, int value)
588     {
589         TimeScaleData data = getTimeScaleData(scale);
590         
591         switch (value)
592         {
593         case UNITS_VALUE:
594             return data.units;
595             
596         case EPOCH_OFFSET_VALUE:
597             return data.epochOffset;
598         
599         case FROM_MIN_VALUE:
600             return data.fromMin;
601             
602         case FROM_MAX_VALUE:
603             return data.fromMax;
604             
605         case TO_MIN_VALUE:
606             return data.toMin;
607             
608         case TO_MAX_VALUE:
609             return data.toMax;
610             
611         case EPOCH_OFFSET_PLUS_1_VALUE:
612             return data.epochOffsetP1;
613             
614         case EPOCH_OFFSET_MINUS_1_VALUE:
615             return data.epochOffsetM1;
616             
617         case UNITS_ROUND_VALUE:
618             return data.unitsRound;
619         
620         case MIN_ROUND_VALUE:
621             return data.minRound;
622             
623         case MAX_ROUND_VALUE:
624             return data.maxRound;
625             
626         default:
627             throw new IllegalArgumentException JavaDoc("value out of range: " + value);
628         }
629     }
630     
631     private static TimeScaleData toRangeCheck(long universalTime, int scale)
632     {
633         TimeScaleData data = getTimeScaleData(scale);
634           
635         if (universalTime >= data.toMin && universalTime <= data.toMax) {
636             return data;
637         }
638         
639         throw new IllegalArgumentException JavaDoc("universalTime out of range:" + universalTime);
640     }
641     
642     private static TimeScaleData fromRangeCheck(long otherTime, int scale)
643     {
644         TimeScaleData data = getTimeScaleData(scale);
645           
646         if (otherTime >= data.fromMin && otherTime <= data.fromMax) {
647             return data;
648         }
649         
650         throw new IllegalArgumentException JavaDoc("otherTime out of range:" + otherTime);
651     }
652     
653     /**
654      * Convert a time in the Universal Time Scale into another time
655      * scale. The division used to do the conversion rounds down.
656      *
657      * NOTE: This is an internal routine used by the tool that
658      * generates the to and from limits. Use it at your own risk.
659      *
660      * @param universalTime the time in the Universal Time scale
661      * @param timeScale the time scale to convert to
662      * @return the time in the given time scale
663      *
664      * @internal
665      * @deprecated This API is ICU internal only.
666      */

667     public static BigDecimal toBigDecimalTrunc(BigDecimal universalTime, int timeScale)
668     {
669         TimeScaleData data = getTimeScaleData(timeScale);
670         BigDecimal units = new BigDecimal(data.units);
671         BigDecimal epochOffset = new BigDecimal(data.epochOffset);
672         
673         return universalTime.divide(units, BigDecimal.ROUND_DOWN).subtract(epochOffset);
674     }
675 }
676
Popular Tags