File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\util\DateTimeUtils.java.html

1 /*
2 * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
3 * Version 1.0, and under the Eclipse Public License, Version 1.0
4 * (http://h2database.com/html/license.html).
5 * Initial Developer: H2 Group
6 * Iso8601:
7 * Initial Developer: Robert Rathsack (firstName dot lastName at gmx dot de)
8 */
9 package org.h2.util;
10
11 import java.sql.Date;
12 import java.sql.Time;
13 import java.sql.Timestamp;
14 import java.text.SimpleDateFormat;
15 import java.util.Calendar;
16 import java.util.GregorianCalendar;
17 import java.util.Locale;
18 import java.util.TimeZone;
19 import org.h2.constant.ErrorCode;
20 import org.h2.message.DbException;
21 import org.h2.value.Value;
22 import org.h2.value.ValueDate;
23 import org.h2.value.ValueNull;
24 import org.h2.value.ValueTime;
25 import org.h2.value.ValueTimestamp;
26
27 /**
28 * This utility class contains time conversion functions.
29 * <p>
30 * Date value: a bit field with bits for the year, month, and day.
31 * Absolute day: the day number (0 means 1970-01-01).
32 */
33 public class DateTimeUtils {
34
35 /**
36 * The number of milliseconds per day.
37 */
38 public static final long MILLIS_PER_DAY = 24 * 60 * 60 * 1000L;
39
40 private static final long NANOS_PER_DAY = MILLIS_PER_DAY * 1000000;
41
42 private static final int SHIFT_YEAR = 9;
43 private static final int SHIFT_MONTH = 5;
44
45 private static final int[] NORMAL_DAYS_PER_MONTH = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
46
47 /**
48 * Offsets of month within a year, starting with March, April,...
49 */
50 private static final int[] DAYS_OFFSET = { 0, 31, 61, 92, 122, 153, 184, 214, 245, 275, 306, 337, 366 };
51
52 private static int zoneOffset;
53 private static Calendar cachedCalendar;
54
55 private DateTimeUtils() {
56 // utility class
57 }
58
59 static {
60 getCalendar();
61 }
62
63 /**
64 * Reset the calendar, for example after changing the default timezone.
65 */
66 public static void resetCalendar() {
67 cachedCalendar = null;
68 getCalendar();
69 }
70
71 private static Calendar getCalendar() {
72 if (cachedCalendar == null) {
73 cachedCalendar = Calendar.getInstance();
74 zoneOffset = cachedCalendar.get(Calendar.ZONE_OFFSET);
75 }
76 return cachedCalendar;
77 }
78
79 /**
80 * Convert the date to the specified time zone.
81 *
82 * @param value the date (might be ValueNull)
83 * @param calendar the calendar
84 * @return the date using the correct time zone
85 */
86 public static Date convertDate(Value value, Calendar calendar) {
87 if (value == ValueNull.INSTANCE) {
88 return null;
89 }
90 ValueDate d = (ValueDate) value.convertTo(Value.DATE);
91 Calendar cal = (Calendar) calendar.clone();
92 cal.clear();
93 cal.setLenient(true);
94 long dateValue = d.getDateValue();
95 setCalendarFields(cal, yearFromDateValue(dateValue),
96 monthFromDateValue(dateValue),
97 dayFromDateValue(dateValue),
98 0, 0, 0, 0);
99 long ms = cal.getTimeInMillis();
100 return new Date(ms);
101 }
102
103 /**
104 * Convert the time to the specified time zone.
105 *
106 * @param value the time (might be ValueNull)
107 * @param calendar the calendar
108 * @return the time using the correct time zone
109 */
110 public static Time convertTime(Value value, Calendar calendar) {
111 if (value == ValueNull.INSTANCE) {
112 return null;
113 }
114 ValueTime t = (ValueTime) value.convertTo(Value.TIME);
115 Calendar cal = (Calendar) calendar.clone();
116 cal.clear();
117 cal.setLenient(true);
118 long nanos = t.getNanos();
119 long millis = nanos / 1000000;
120 nanos -= millis * 1000000;
121 long s = millis / 1000;
122 millis -= s * 1000;
123 long m = s / 60;
124 s -= m * 60;
125 long h = m / 60;
126 m -= h * 60;
127 setCalendarFields(cal, 1970, 1, 1,
128 (int) h, (int) m, (int) s, (int) millis);
129 long ms = cal.getTimeInMillis();
130 return new Time(ms);
131 }
132
133 /**
134 * Convert the timestamp to the specified time zone.
135 *
136 * @param value the timestamp (might be ValueNull)
137 * @param calendar the calendar
138 * @return the timestamp using the correct time zone
139 */
140 public static Timestamp convertTimestamp(Value value, Calendar calendar) {
141 if (value == ValueNull.INSTANCE) {
142 return null;
143 }
144 ValueTimestamp ts = (ValueTimestamp) value.convertTo(Value.TIMESTAMP);
145 Calendar cal = (Calendar) calendar.clone();
146 cal.clear();
147 cal.setLenient(true);
148 long dateValue = ts.getDateValue();
149 long nanos = ts.getNanos();
150 long millis = nanos / 1000000;
151 nanos -= millis * 1000000;
152 long s = millis / 1000;
153 millis -= s * 1000;
154 long m = s / 60;
155 s -= m * 60;
156 long h = m / 60;
157 m -= h * 60;
158 setCalendarFields(cal, yearFromDateValue(dateValue),
159 monthFromDateValue(dateValue),
160 dayFromDateValue(dateValue),
161 (int) h, (int) m, (int) s, (int) millis);
162 long ms = cal.getTimeInMillis();
163 Timestamp x = new Timestamp(ms);
164 x.setNanos((int) (nanos + millis * 1000000));
165 return x;
166 }
167
168 /**
169 * Convert the date using the specified calendar.
170 *
171 * @param x the date
172 * @param calendar the calendar
173 * @return the date
174 */
175 public static ValueDate convertDate(Date x, Calendar calendar) {
176 if (calendar == null) {
177 throw DbException.getInvalidValueException("calendar", null);
178 }
179 Calendar cal = (Calendar) calendar.clone();
180 cal.setTimeInMillis(x.getTime());
181 long dateValue = dateValueFromCalendar(cal);
182 return ValueDate.fromDateValue(dateValue);
183 }
184
185 /**
186 * Convert the time using the specified calendar.
187 *
188 * @param x the time
189 * @param calendar the calendar
190 * @return the time
191 */
192 public static ValueTime convertTime(Time x, Calendar calendar) {
193 if (calendar == null) {
194 throw DbException.getInvalidValueException("calendar", null);
195 }
196 Calendar cal = (Calendar) calendar.clone();
197 cal.setTimeInMillis(x.getTime());
198 long nanos = nanosFromCalendar(cal);
199 return ValueTime.fromNanos(nanos);
200 }
201
202 /**
203 * Convert a date to the specified time zone.
204 *
205 * @param x the date to convert
206 * @param target the calendar with the target timezone
207 * @return the milliseconds the milliseconds in UTC
208 */
209 public static long convertToLocal(java.util.Date x, Calendar target) {
210 if (target == null) {
211 throw DbException.getInvalidValueException("calendar", null);
212 }
213 target = (Calendar) target.clone();
214 Calendar local = Calendar.getInstance();
215 synchronized (local) {
216 local.setTime(x);
217 convertTime(local, target);
218 }
219 return target.getTime().getTime();
220 }
221
222 private static void convertTime(Calendar from, Calendar to) {
223 to.set(Calendar.ERA, from.get(Calendar.ERA));
224 to.set(Calendar.YEAR, from.get(Calendar.YEAR));
225 to.set(Calendar.MONTH, from.get(Calendar.MONTH));
226 to.set(Calendar.DAY_OF_MONTH, from.get(Calendar.DAY_OF_MONTH));
227 to.set(Calendar.HOUR_OF_DAY, from.get(Calendar.HOUR_OF_DAY));
228 to.set(Calendar.MINUTE, from.get(Calendar.MINUTE));
229 to.set(Calendar.SECOND, from.get(Calendar.SECOND));
230 to.set(Calendar.MILLISECOND, from.get(Calendar.MILLISECOND));
231 }
232
233 /**
234 * Convert the timestamp using the specified calendar.
235 *
236 * @param x the time
237 * @param calendar the calendar
238 * @return the timestamp
239 */
240 public static ValueTimestamp convertTimestamp(Timestamp x, Calendar calendar) {
241 if (calendar == null) {
242 throw DbException.getInvalidValueException("calendar", null);
243 }
244 Calendar cal = (Calendar) calendar.clone();
245 cal.setTimeInMillis(x.getTime());
246 long dateValue = dateValueFromCalendar(cal);
247 long nanos = nanosFromCalendar(cal);
248 nanos += x.getNanos() % 1000000;
249 return ValueTimestamp.fromDateValueAndNanos(dateValue, nanos);
250 }
251
252 /**
253 * Parse a date string. The format is: [+|-]year-month-day
254 *
255 * @param s the string to parse
256 * @param start the parse index start
257 * @param end the parse index end
258 * @return the date value
259 * @throws IllegalArgumentException if there is a problem
260 */
261 public static long parseDateValue(String s, int start, int end) {
262 if (s.charAt(start) == '+') {
263 // +year
264 start++;
265 }
266 // start at position 1 to support "-year"
267 int s1 = s.indexOf('-', start + 1);
268 int s2 = s.indexOf('-', s1 + 1);
269 if (s1 <= 0 || s2 <= s1) {
270 throw new IllegalArgumentException(s);
271 }
272 int year = Integer.parseInt(s.substring(start, s1));
273 int month = Integer.parseInt(s.substring(s1 + 1, s2));
274 int day = Integer.parseInt(s.substring(s2 + 1, end));
275 if (!isValidDate(year, month, day)) {
276 throw new IllegalArgumentException(year + "-" + month + "-" + day);
277 }
278 return dateValue(year, month, day);
279 }
280
281 /**
282 * Parse a time string. The format is: [-]hour:minute:second[.nanos]
283 *
284 * @param s the string to parse
285 * @param start the parse index start
286 * @param end the parse index end
287 * @param timeOfDay whether the result need to be within 0 (inclusive) and 1
288 * day (exclusive)
289 * @return the time in nanoseconds
290 * @throws IllegalArgumentException if there is a problem
291 */
292 public static long parseTimeNanos(String s, int start, int end, boolean timeOfDay) {
293 int hour = 0, minute = 0, second = 0;
294 long nanos = 0;
295 int s1 = s.indexOf(':', start);
296 int s2 = s.indexOf(':', s1 + 1);
297 int s3 = s.indexOf('.', s2 + 1);
298 if (s1 <= 0 || s2 <= s1) {
299 throw new IllegalArgumentException(s);
300 }
301 boolean negative;
302 hour = Integer.parseInt(s.substring(start, s1));
303 if (hour < 0) {
304 if (timeOfDay) {
305 throw new IllegalArgumentException(s);
306 }
307 negative = true;
308 hour = -hour;
309 } else {
310 negative = false;
311 }
312 minute = Integer.parseInt(s.substring(s1 + 1, s2));
313 if (s3 < 0) {
314 second = Integer.parseInt(s.substring(s2 + 1, end));
315 } else {
316 second = Integer.parseInt(s.substring(s2 + 1, s3));
317 String n = (s.substring(s3 + 1, end) + "000000000").substring(0, 9);
318 nanos = Integer.parseInt(n);
319 }
320 if (hour >= 2000000 || minute < 0 || minute >= 60 || second < 0 || second >= 60) {
321 throw new IllegalArgumentException(s);
322 }
323 if (timeOfDay && hour >= 24) {
324 throw new IllegalArgumentException(s);
325 }
326 nanos += ((((hour * 60L) + minute) * 60) + second) * 1000000000;
327 return negative ? -nanos : nanos;
328 }
329
330 /**
331 * Calculate the milliseconds since 1970-01-01 (UTC) for the given date and
332 * time (in the specified timezone).
333 *
334 * @param tz the timezone of the parameters
335 * @param year the absolute year (positive or negative)
336 * @param month the month (1-12)
337 * @param day the day (1-31)
338 * @param hour the hour (0-23)
339 * @param minute the minutes (0-59)
340 * @param second the number of seconds (0-59)
341 * @param millis the number of milliseconds
342 * @return the number of milliseconds (UTC)
343 */
344 public static long getMillis(TimeZone tz, int year, int month, int day, int hour, int minute, int second, int millis) {
345 try {
346 return getTimeTry(false, tz, year, month, day, hour, minute, second, millis);
347 } catch (IllegalArgumentException e) {
348 // special case: if the time simply doesn't exist because of
349 // daylight saving time changes, use the lenient version
350 String message = e.toString();
351 if (message.indexOf("HOUR_OF_DAY") > 0) {
352 if (hour < 0 || hour > 23) {
353 throw e;
354 }
355 return getTimeTry(true, tz, year, month, day, hour, minute, second, millis);
356 } else if (message.indexOf("DAY_OF_MONTH") > 0) {
357 int maxDay;
358 if (month == 2) {
359 maxDay = new GregorianCalendar().isLeapYear(year) ? 29 : 28;
360 } else {
361 maxDay = 30 + ((month + (month > 7 ? 1 : 0)) & 1);
362 }
363 if (day < 1 || day > maxDay) {
364 throw e;
365 }
366 // DAY_OF_MONTH is thrown for years > 2037
367 // using the timezone Brasilia and others,
368 // for example for 2042-10-12 00:00:00.
369 hour += 6;
370 return getTimeTry(true, tz, year, month, day, hour, minute, second, millis);
371 } else {
372 return getTimeTry(true, tz, year, month, day, hour, minute, second, millis);
373 }
374 }
375 }
376
377 private static long getTimeTry(boolean lenient, TimeZone tz,
378 int year, int month, int day, int hour, int minute, int second,
379 int millis) {
380 Calendar c;
381 if (tz == null) {
382 c = getCalendar();
383 } else {
384 c = Calendar.getInstance(tz);
385 }
386 synchronized (c) {
387 c.clear();
388 c.setLenient(lenient);
389 setCalendarFields(c, year, month, day, hour, minute, second, millis);
390 return c.getTime().getTime();
391 }
392 }
393
394 private static void setCalendarFields(Calendar cal, int year, int month, int day,
395 int hour, int minute, int second, int millis) {
396 if (year <= 0) {
397 cal.set(Calendar.ERA, GregorianCalendar.BC);
398 cal.set(Calendar.YEAR, 1 - year);
399 } else {
400 cal.set(Calendar.ERA, GregorianCalendar.AD);
401 cal.set(Calendar.YEAR, year);
402 }
403 // january is 0
404 cal.set(Calendar.MONTH, month - 1);
405 cal.set(Calendar.DAY_OF_MONTH, day);
406 cal.set(Calendar.HOUR_OF_DAY, hour);
407 cal.set(Calendar.MINUTE, minute);
408 cal.set(Calendar.SECOND, second);
409 cal.set(Calendar.MILLISECOND, millis);
410 }
411
412 /**
413 * Get the specified field of a date, however with years normalized to
414 * positive or negative, and month starting with 1.
415 *
416 * @param d the date
417 * @param field the field type
418 * @return the value
419 */
420 public static int getDatePart(java.util.Date d, int field) {
421 Calendar c = getCalendar();
422 synchronized (c) {
423 c.setTime(d);
424 if (field == Calendar.YEAR) {
425 return getYear(c);
426 }
427 int value = c.get(field);
428 if (field == Calendar.MONTH) {
429 return value + 1;
430 }
431 return value;
432 }
433 }
434
435 /**
436 * Get the year (positive or negative) from a calendar.
437 *
438 * @param calendar the calendar
439 * @return the year
440 */
441 private static int getYear(Calendar calendar) {
442 int year = calendar.get(Calendar.YEAR);
443 if (calendar.get(Calendar.ERA) == GregorianCalendar.BC) {
444 year = 1 - year;
445 }
446 return year;
447 }
448
449 /**
450 * Get the number of milliseconds since 1970-01-01 in the local timezone, but
451 * without daylight saving time into account.
452 *
453 * @param d the date
454 * @return the milliseconds
455 */
456 public static long getTimeLocalWithoutDst(java.util.Date d) {
457 return d.getTime() + zoneOffset;
458 }
459
460 /**
461 * Convert the number of milliseconds since 1970-01-01 in the local timezone
462 * to UTC, but without daylight saving time into account.
463 *
464 * @param millis the number of milliseconds in the local timezone
465 * @return the number of milliseconds in UTC
466 */
467 public static long getTimeUTCWithoutDst(long millis) {
468 return millis - zoneOffset;
469 }
470
471 /**
472 * Return the day of week according to the ISO 8601 specification. Week
473 * starts at Monday. See also http://en.wikipedia.org/wiki/ISO_8601
474 *
475 * @author Robert Rathsack
476 *
477 * @param date the date object which day of week should be calculated
478 * @return the day of the week, Monday as 1 to Sunday as 7
479 */
480 public static int getIsoDayOfWeek(java.util.Date date) {
481 Calendar cal = Calendar.getInstance();
482 cal.setTimeInMillis(date.getTime());
483 int val = cal.get(Calendar.DAY_OF_WEEK) - 1;
484 return val == 0 ? 7 : val;
485 }
486
487 /**
488 * Returns the week of the year according to the ISO 8601 specification. The
489 * spec defines the first week of the year as the week which contains at
490 * least 4 days of the new year. The week starts at Monday. Therefore
491 * December 29th - 31th could belong to the next year and January 1st - 3th
492 * could belong to the previous year. If January 1st is on Thursday (or
493 * earlier) it belongs to the first week, otherwise to the last week of the
494 * previous year. Hence January 4th always belongs to the first week while
495 * the December 28th always belongs to the last week.
496 *
497 * @author Robert Rathsack
498 * @param date the date object which week of year should be calculated
499 * @return the week of the year
500 */
501 public static int getIsoWeek(java.util.Date date) {
502 Calendar c = Calendar.getInstance();
503 c.setTimeInMillis(date.getTime());
504 c.setFirstDayOfWeek(Calendar.MONDAY);
505 c.setMinimalDaysInFirstWeek(4);
506 return c.get(Calendar.WEEK_OF_YEAR);
507 }
508
509 /**
510 * Returns the year according to the ISO week definition.
511 *
512 * @author Robert Rathsack
513 *
514 * @param date the date object which year should be calculated
515 * @return the year
516 */
517 public static int getIsoYear(java.util.Date date) {
518 Calendar cal = Calendar.getInstance();
519 cal.setTimeInMillis(date.getTime());
520 cal.setFirstDayOfWeek(Calendar.MONDAY);
521 cal.setMinimalDaysInFirstWeek(4);
522 int year = getYear(cal);
523 int month = cal.get(Calendar.MONTH);
524 int week = cal.get(Calendar.WEEK_OF_YEAR);
525 if (month == 0 && week > 51) {
526 year--;
527 } else if (month == 11 && week == 1) {
528 year++;
529 }
530 return year;
531 }
532
533 /**
534 * Formats a date using a format string.
535 *
536 * @param date the date to format
537 * @param format the format string
538 * @param locale the locale
539 * @param timeZone the timezone
540 * @return the formatted date
541 */
542 public static String formatDateTime(java.util.Date date, String format, String locale, String timeZone) {
543 SimpleDateFormat dateFormat = getDateFormat(format, locale, timeZone);
544 synchronized (dateFormat) {
545 return dateFormat.format(date);
546 }
547 }
548
549 /**
550 * Parses a date using a format string.
551 *
552 * @param date the date to parse
553 * @param format the parsing format
554 * @param locale the locale
555 * @param timeZone the timeZone
556 * @return the parsed date
557 */
558 public static java.util.Date parseDateTime(String date, String format, String locale, String timeZone) {
559 SimpleDateFormat dateFormat = getDateFormat(format, locale, timeZone);
560 try {
561 synchronized (dateFormat) {
562 return dateFormat.parse(date);
563 }
564 } catch (Exception e) {
565 // ParseException
566 throw DbException.get(ErrorCode.PARSE_ERROR_1, e, date);
567 }
568 }
569
570 private static SimpleDateFormat getDateFormat(String format, String locale, String timeZone) {
571 try {
572 // currently, a new instance is create for each call
573 // however, could cache the last few instances
574 SimpleDateFormat df;
575 if (locale == null) {
576 df = new SimpleDateFormat(format);
577 } else {
578 Locale l = new Locale(locale);
579 df = new SimpleDateFormat(format, l);
580 }
581 if (timeZone != null) {
582 df.setTimeZone(TimeZone.getTimeZone(timeZone));
583 }
584 return df;
585 } catch (Exception e) {
586 throw DbException.get(ErrorCode.PARSE_ERROR_1, e, format + "/" + locale + "/" + timeZone);
587 }
588 }
589
590 /**
591 * Verify if the specified date is valid.
592 *
593 * @param year the year
594 * @param month the month (January is 1)
595 * @param day the day (1 is the first of the month)
596 * @return true if it is valid
597 */
598 public static boolean isValidDate(int year, int month, int day) {
599 if (month < 1 || month > 12 || day < 1) {
600 return false;
601 }
602 if (year > 1582) {
603 // Gregorian calendar
604 if (month != 2) {
605 return day <= NORMAL_DAYS_PER_MONTH[month];
606 }
607 // February
608 if ((year & 3) != 0) {
609 return day <= 28;
610 }
611 return day <= ((year % 100 != 0) || (year % 400 == 0) ? 29 : 28);
612 } else if (year == 1582 && month == 10) {
613 // special case: days 1582-10-05 .. 1582-10-14 don't exist
614 return day <= 31 && (day < 5 || day > 14);
615 }
616 if (month != 2 && day <= NORMAL_DAYS_PER_MONTH[month]) {
617 return true;
618 }
619 return day <= ((year & 3) != 0 ? 28 : 29);
620 }
621
622 /**
623 * Convert a date value to a date, using the default timezone.
624 *
625 * @param dateValue the date value
626 * @return the date
627 */
628 public static Date convertDateValueToDate(long dateValue) {
629 long millis = getMillis(TimeZone.getDefault(),
630 yearFromDateValue(dateValue),
631 monthFromDateValue(dateValue),
632 dayFromDateValue(dateValue), 0, 0, 0, 0);
633 return new Date(millis);
634 }
635
636 /**
637 * Convert a date value / time value to a timestamp, using the default
638 * timezone.
639 *
640 * @param dateValue the date value
641 * @param nanos the nanoseconds since midnight
642 * @return the timestamp
643 */
644 public static Timestamp convertDateValueToTimestamp(long dateValue, long nanos) {
645 long millis = nanos / 1000000;
646 nanos -= millis * 1000000;
647 long s = millis / 1000;
648 millis -= s * 1000;
649 long m = s / 60;
650 s -= m * 60;
651 long h = m / 60;
652 m -= h * 60;
653 long ms = getMillis(TimeZone.getDefault(),
654 yearFromDateValue(dateValue),
655 monthFromDateValue(dateValue),
656 dayFromDateValue(dateValue),
657 (int) h, (int) m, (int) s, 0);
658 Timestamp ts = new Timestamp(ms);
659 ts.setNanos((int) (nanos + millis * 1000000));
660 return ts;
661 }
662
663 /**
664 * Convert a time value to a time, using the default
665 * timezone.
666 *
667 * @param nanos the nanoseconds since midnight
668 * @return the time
669 */
670 public static Time convertNanoToTime(long nanos) {
671 long millis = nanos / 1000000;
672 long s = millis / 1000;
673 millis -= s * 1000;
674 long m = s / 60;
675 s -= m * 60;
676 long h = m / 60;
677 m -= h * 60;
678 long ms = getMillis(TimeZone.getDefault(),
679 1970, 1, 1, (int) (h % 24), (int) m, (int) s, (int) millis);
680 return new Time(ms);
681 }
682
683 /**
684 * Get the year from a date value.
685 *
686 * @param x the date value
687 * @return the year
688 */
689 public static int yearFromDateValue(long x) {
690 return (int) (x >>> SHIFT_YEAR);
691 }
692
693 /**
694 * Get the month from a date value.
695 *
696 * @param x the date value
697 * @return the month (1..12)
698 */
699 public static int monthFromDateValue(long x) {
700 return (int) (x >>> SHIFT_MONTH) & 15;
701 }
702
703 /**
704 * Get the day of month from a date value.
705 *
706 * @param x the date value
707 * @return the day (1..31)
708 */
709 public static int dayFromDateValue(long x) {
710 return (int) (x & 31);
711 }
712
713 /**
714 * Get the date value from a given date.
715 *
716 * @param year the year
717 * @param month the month (1..12)
718 * @param day the day (1..31)
719 * @return the date value
720 */
721 public static long dateValue(long year, int month, int day) {
722 return (year << SHIFT_YEAR) | (month << SHIFT_MONTH) | day;
723 }
724
725 /**
726 * Calculate the date value (in the default timezone) from a given time in
727 * milliseconds in UTC.
728 *
729 * @param ms the milliseconds
730 * @return the date value
731 */
732 public static long dateValueFromDate(long ms) {
733 Calendar cal = getCalendar();
734 synchronized (cal) {
735 cal.clear();
736 cal.setTimeInMillis(ms);
737 return dateValueFromCalendar(cal);
738 }
739 }
740
741 /**
742 * Calculate the date value from a given calendar.
743 *
744 * @param cal the calendar
745 * @return the date value
746 */
747 private static long dateValueFromCalendar(Calendar cal) {
748 int year, month, day;
749 year = getYear(cal);
750 month = cal.get(Calendar.MONTH) + 1;
751 day = cal.get(Calendar.DAY_OF_MONTH);
752 return ((long) year << SHIFT_YEAR) | (month << SHIFT_MONTH) | day;
753 }
754
755 /**
756 * Calculate the nanoseconds since midnight (in the default timezone) from a
757 * given time in milliseconds in UTC.
758 *
759 * @param ms the milliseconds
760 * @return the nanoseconds
761 */
762 public static long nanosFromDate(long ms) {
763 Calendar cal = getCalendar();
764 synchronized (cal) {
765 cal.clear();
766 cal.setTimeInMillis(ms);
767 return nanosFromCalendar(cal);
768 }
769 }
770
771 /**
772 * Calculate the nanoseconds since midnight from a given calendar.
773 *
774 * @param cal the calendar
775 * @return the nanoseconds
776 */
777 private static long nanosFromCalendar(Calendar cal) {
778 int h = cal.get(Calendar.HOUR_OF_DAY);
779 int m = cal.get(Calendar.MINUTE);
780 int s = cal.get(Calendar.SECOND);
781 int millis = cal.get(Calendar.MILLISECOND);
782 return ((((((h * 60L) + m) * 60) + s) * 1000) + millis) * 1000000;
783 }
784
785 /**
786 * Calculate the normalized timestamp.
787 *
788 * @param absoluteDay the absolute day
789 * @param nanos the nanoseconds (may be negative or larger than one day)
790 * @return the timestamp
791 */
792 public static ValueTimestamp normalizeTimestamp(long absoluteDay, long nanos) {
793 if (nanos > NANOS_PER_DAY || nanos < 0) {
794 long d;
795 if (nanos > NANOS_PER_DAY) {
796 d = nanos / NANOS_PER_DAY;
797 } else {
798 d = (nanos - NANOS_PER_DAY + 1) / NANOS_PER_DAY;
799 }
800 nanos -= d * NANOS_PER_DAY;
801 absoluteDay += d;
802 }
803 return ValueTimestamp.fromDateValueAndNanos(dateValueFromAbsoluteDay(absoluteDay), nanos);
804 }
805
806 /**
807 * Calculate the absolute day from a date value.
808 *
809 * @param dateValue the date value
810 * @return the absolute day
811 */
812 public static long absoluteDayFromDateValue(long dateValue) {
813 long y = yearFromDateValue(dateValue);
814 int m = monthFromDateValue(dateValue);
815 int d = dayFromDateValue(dateValue);
816 if (m <= 2) {
817 y--;
818 m += 12;
819 }
820 long a = ((y * 2922L) >> 3) + DAYS_OFFSET[m - 3] + d - 719484;
821 if (y <= 1582 && ((y < 1582) || (m * 100 + d < 1005))) {
822 // Julian calendar (cutover at 1582-10-04 / 1582-10-15)
823 a += 13;
824 } else if (y < 1901 || y > 2099) {
825 // Gregorian calendar (slow mode)
826 a += (y / 400) - (y / 100) + 15;
827 }
828 return a;
829 }
830
831 /**
832 * Calculate the date value from an absolute day.
833 *
834 * @param absoluteDay the absolute day
835 * @return the date value
836 */
837 public static long dateValueFromAbsoluteDay(long absoluteDay) {
838 long d = absoluteDay + 719468;
839 long y100 = 0, offset;
840 if (d > 578040) {
841 // Gregorian calendar
842 long y400 = d / 146097;
843 d -= y400 * 146097;
844 y100 = d / 36524;
845 d -= y100 * 36524;
846 offset = y400 * 400 + y100 * 100;
847 } else {
848 // Julian calendar
849 d += 292200000002L;
850 offset = -800000000;
851 }
852 long y4 = d / 1461;
853 d -= y4 * 1461;
854 long y = d / 365;
855 d -= y * 365;
856 if (d == 0 && (y == 4 || y100 == 4)) {
857 y--;
858 d += 365;
859 }
860 y += offset + y4 * 4;
861 // month of a day
862 int m = ((int) d * 2 + 1) * 5 / 306;
863 d -= DAYS_OFFSET[m] - 1;
864 if (m >= 10) {
865 y++;
866 m -= 12;
867 }
868 return dateValue(y, m + 3, (int) d);
869 }
870
871 }
872