blob: 149a1d2e1dae92b7be6c1638c3a196e5a268245f [file] [log] [blame]
/*
* Copyright 2009-2012 by The Regents of the University of California
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* you may obtain a copy of the License from
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Joda API (http://joda-time.sourceforge.net/) is under the protection of:
*
* Copyright 2001-2005 Stephen Colebourne
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package edu.uci.ics.asterix.om.base.temporal;
/**
* A simple implementation of the Gregorian calendar system.
* <p/>
*/
public class GregorianCalendarSystem implements ICalendarSystem {
public enum Fields {
YEAR,
MONTH,
DAY,
HOUR,
MINUTE,
SECOND,
MILLISECOND
};
//public static final int YEAR = 0, MONTH = 1, DAY = 2, HOUR = 3, MINUTE = 4, SECOND = 5, MILLISECOND = 6;
public static final int[] DAYS_OF_MONTH_ORDI = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
public static final int[] DAYS_OF_MONTH_LEAP = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
public static final int[] DAYS_SINCE_MONTH_BEGIN_ORDI = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
public static final int CHRONON_OF_SECOND = 1000;
public static final int CHRONON_OF_MINUTE = 60 * CHRONON_OF_SECOND;
public static final int CHRONON_OF_HOUR = 60 * CHRONON_OF_MINUTE;
public static final long CHRONON_OF_DAY = 24 * CHRONON_OF_HOUR;
/**
* Minimum feasible value of each field
*/
public static final int[] FIELD_MINS = { Integer.MIN_VALUE, // year
1, // month
1, // day
0, // hour
0, // minute
0, // second
0 // millisecond
};
public static final int[] FIELD_MAXS = { Integer.MAX_VALUE, // year
12, // month
31, // day
23, // hour
59, // minute
59, // second
999 // millisecond
};
public static final int TIMEZONE_HOUR_MIN = -12, TIMEZONE_HOUR_MAX = 14, TIMEZONE_MIN_MIN = -60,
TIMEZONE_MIN_MAX = 60;
/**
* From Joda API: GregorianChronology.java
*/
private static final long CHRONON_OF_YEAR = (long) (365.2425 * CHRONON_OF_DAY);
/**
* From Joda API: GregorianChronology.java
*/
private static final int DAYS_0000_TO_1970 = 719527;
private static final GregorianCalendarSystem instance = new GregorianCalendarSystem();
private GregorianCalendarSystem() {
}
public static GregorianCalendarSystem getInstance() {
return instance;
}
/**
* Check whether the given date time value is a valid date time following the gregorian calendar system.
*
* @param fields
* @return
*/
public boolean validate(int year, int month, int day, int hour, int min, int sec, int millis) {
// Check whether each field is within the value domain
if (year < FIELD_MINS[0] || year > FIELD_MAXS[0]) {
return false;
}
if (month < FIELD_MINS[1] || month > FIELD_MAXS[1]) {
return false;
}
if (day < FIELD_MINS[2] || day > FIELD_MAXS[2]) {
return false;
}
if (hour < FIELD_MINS[3] || hour > FIELD_MAXS[3]) {
return false;
}
if (min < FIELD_MINS[4] || min > FIELD_MAXS[4]) {
return false;
}
if (sec < FIELD_MINS[5] || sec > FIELD_MAXS[5]) {
return false;
}
if (millis < FIELD_MINS[6] || millis > FIELD_MAXS[6]) {
return false;
}
// Check whether leap month.
if (month == 2) {
if (isLeapYear(year)) {
if (month > DAYS_OF_MONTH_LEAP[1]) {
return false;
}
} else {
if (month > DAYS_OF_MONTH_ORDI[1]) {
return false;
}
}
}
return true;
}
/**
* Check whether the given time zone value is a valid time zone following the gregorian calendar system.
* <p/>
* The valid timezone is within the range of:<br/>
* - Hours: UTC -12 (Y, or Yankee Time Zone) to UTC +14 (LINT, or Line Islands Time) <br/>
* - Minutes: currently the available minutes include 00, 30 and 45.
* <p/>
* Reference: http://www.timeanddate.com/library/abbreviations/timezones/
* <p/>
*
* @param timezone
* @return
*/
public boolean validateTimeZone(int timezone) {
short tzMin = (short) ((timezone % 4) * 15);
if (tzMin < -60 || tzMin >= 60) {
return false;
}
short tzHr = (short) (timezone / 4);
if (tzHr < -12 && tzHr > 14) {
return false;
}
return true;
}
/**
* Validate the given chronon time and time zone.
*
* @param year
* @param month
* @param day
* @param hour
* @param min
* @param sec
* @param millis
* @param timezone
* @return
*/
public boolean validate(int year, int month, int day, int hour, int min, int sec, int millis, int timezone) {
return validate(year, month, day, hour, min, sec, millis) && validateTimeZone(timezone);
}
/**
* Get the UTC chronon time of the given date time and time zone.
*
* @param year
* @param month
* @param day
* @param hour
* @param min
* @param sec
* @param millis
* @param timezone
* @return
*/
public long getChronon(int year, int month, int day, int hour, int min, int sec, int millis, int timezone) {
// Added milliseconds for all fields but month and day
long chrononTime = chrononizeBeginningOfYear(year) + (hour - timezone / 4) * CHRONON_OF_HOUR
+ (min - (timezone % 4) * 15) * CHRONON_OF_MINUTE + sec * CHRONON_OF_SECOND + millis;
// Added milliseconds for days of the month.
chrononTime += (day - 1 + DAYS_SINCE_MONTH_BEGIN_ORDI[month - 1]) * CHRONON_OF_DAY;
// Adjust the leap year
if (month > 2 && isLeapYear(year)) {
chrononTime += CHRONON_OF_DAY;
}
return chrononTime;
}
/**
* Get the chronon time (number of milliseconds) of the given time and time zone.
*
* @param hour
* @param min
* @param sec
* @param millis
* @param timezone
* @return
*/
public int getChronon(int hour, int min, int sec, int millis, int timezone) {
// Added milliseconds for all fields but month and day
int chrononTime = (hour - timezone / 4) * CHRONON_OF_HOUR + (min - (timezone % 4) * 15) * CHRONON_OF_MINUTE
+ sec * CHRONON_OF_SECOND + millis;
return chrononTime;
}
/**
* Get the extended string representation of the given UTC chronon time under the given time zone. Only fields before
* the given field index will be returned.
* <p/>
* The extended string representation is like:<br/>
* [-]YYYY-MM-DDThh:mm:ss.xxx[Z|[+|-]hh:mm]
*
* @param chrononTime
* @param timezone
* @param sbder
* @param untilField
*/
public void getExtendStringRepWithTimezoneUntilField(long chrononTime, int timezone, StringBuilder sbder,
Fields startField, Fields untilField) {
int year = getYear(chrononTime);
int month = getMonthOfYear(chrononTime, year);
switch (startField) {
case YEAR:
sbder.append(String.format(year < 0 ? "%05d" : "%04d", year));
if (untilField == Fields.YEAR) {
return;
}
case MONTH:
if (startField != Fields.MONTH) {
sbder.append("-");
}
sbder.append(String.format("%02d", month));
if (untilField == Fields.MONTH) {
return;
}
case DAY:
if (startField != Fields.DAY) {
sbder.append("-");
}
sbder.append(String.format("%02d", getDayOfMonthYear(chrononTime, year, month)));
if (untilField == Fields.DAY) {
break;
}
case HOUR:
if (startField != Fields.HOUR) {
sbder.append("T");
}
sbder.append(String.format("%02d", getHourOfDay(chrononTime)));
if (untilField == Fields.HOUR) {
break;
}
case MINUTE:
if (startField != Fields.MINUTE) {
sbder.append(":");
}
sbder.append(String.format("%02d", getMinOfHour(chrononTime)));
if (untilField == Fields.MINUTE) {
break;
}
case SECOND:
if (startField != Fields.SECOND) {
sbder.append(":");
}
sbder.append(String.format("%02d", getSecOfMin(chrononTime)));
// add millisecond as the precision fields of a second
sbder.append(".").append(String.format("%03d", getMillisOfSec(chrononTime)));
break;
}
if (untilField.compareTo(Fields.DAY) > 0) {
sbder.append("Z");
} else {
short tzMin = (short) ((timezone % 4) * 15);
short tzHr = (short) (timezone / 4);
sbder.append((tzHr >= 0 ? "+" : "-")).append(String.format("%02d", (tzHr < 0 ? -tzHr : tzHr))).append(":")
.append(String.format("%02d", tzMin));
}
}
/**
* Get the basic string representation of a chronon time with the given time zone.
*
* @param chrononTime
* @param timezone
* @param sbder
*/
public void getBasicStringRepWithTimezoneUntilField(long chrononTime, int timezone, StringBuilder sbder,
Fields startField, Fields untilField) {
int year = getYear(chrononTime);
int month = getMonthOfYear(chrononTime, year);
switch (startField) {
case YEAR:
sbder.append(String.format(year < 0 ? "%05d" : "%04d", year));
if (untilField == Fields.YEAR) {
return;
}
case MONTH:
sbder.append(String.format("%02d", month));
if (untilField == Fields.MONTH) {
return;
}
case DAY:
sbder.append(String.format("%02d", getDayOfMonthYear(chrononTime, year, month)));
if (untilField == Fields.DAY) {
break;
}
case HOUR:
sbder.append(String.format("%02d", getHourOfDay(chrononTime)));
if (untilField == Fields.HOUR) {
break;
}
case MINUTE:
sbder.append(String.format("%02d", getMinOfHour(chrononTime)));
if (untilField == Fields.MINUTE) {
break;
}
case SECOND:
sbder.append(String.format("%02d", getSecOfMin(chrononTime)));
// add millisecond as the precision fields of a second
sbder.append(String.format("%03d", getMillisOfSec(chrononTime)));
break;
}
if (untilField.compareTo(Fields.DAY) > 0) {
sbder.append("Z");
} else {
short tzMin = (short) ((timezone % 4) * 15);
short tzHr = (short) (timezone / 4);
sbder.append((tzHr >= 0 ? "+" : "-")).append(String.format("%02d", (tzHr < 0 ? -tzHr : tzHr)))
.append(String.format("%02d", tzMin));
}
}
/**
* Get the extended string representation of the given months and milliseconds (duration) time. *
* <p/>
* The extended and simple string representation is like:<br/>
* [-]PnYnMnDTnHnMnS
*
* @param milliseconds
* @param months
* @param sbder
*/
public void getDurationExtendStringRepWithTimezoneUntilField(long milliseconds, int months, StringBuilder sbder) {
boolean positive = true;
// set the negative flag. "||" is necessary in case that months field is not there (so it is 0)
if (months < 0 || milliseconds < 0) {
months *= -1;
milliseconds *= -1;
positive = false;
}
int month = getDurationMonth(months);
int year = getDurationYear(months);
int millisecond = getDurationMillisecond(milliseconds);
int second = getDurationSecond(milliseconds);
int minute = getDurationMinute(milliseconds);
int hour = getDurationHour(milliseconds);
int day = getDurationDay(milliseconds);
if (!positive) {
sbder.append("-");
}
sbder.append("P");
sbder.append((year != 0) ? year + "Y" : "");
sbder.append((month != 0) ? month + "M" : "");
sbder.append((day != 0) ? day + "D" : "");
sbder.append((hour != 0 || minute != 0 || second != 0 || millisecond != 0) ? "T" : "");
sbder.append((hour != 0) ? hour + "H" : "");
sbder.append((minute != 0) ? minute + "M" : "");
sbder.append((second != 0 || millisecond != 0) ? second : "");
if (millisecond > 0) {
sbder.append("." + millisecond);
}
sbder.append((second != 0 || millisecond != 0) ? "S" : "");
}
/**
* Check whether a given year is a leap year.
*
* @param year
* @return
*/
protected boolean isLeapYear(int year) {
return ((year & 3) == 0) && ((year % 100) != 0 || (year % 400) == 0);
}
/**
* From Joda library GregorianChronology class: The basic calculation is
* (y / 4) - (y / 100) + (y / 400). <br/>
* Use y >> 2 ( (y + 3) >> 2 for negative y value) to replace y / 4 reveals eliminates two divisions.
*
* @param year
* @return
*/
private long chrononizeBeginningOfYear(int year) {
int leapYears = year / 100;
if (year < 0) {
// From Joda library GregorianChronology class
// The basic calculation is (y / 4) - (y / 100) + (y / 400)
// Use (y + 3) >> 2 to replace y / 4 reveals eliminates two divisions.
leapYears = ((year + 3) >> 2) - leapYears + ((leapYears + 3) >> 2) - 1;
} else {
leapYears = (year >> 2) - leapYears + (leapYears >> 2);
if (isLeapYear(year)) {
leapYears--;
}
}
return (year * 365L + (leapYears - DAYS_0000_TO_1970)) * CHRONON_OF_DAY;
}
/**
* Get the year for the given chronon time.
* <p/>
* This code is directly from the Joda library BadicChronology.java.<br/>
* The original authers are Stephen Colebourne, Brain S O'Neill and Guy Allard, and modified by JArod Wen on May 7th, 2012.
*
* @param chrononTime
* @return
*/
public int getYear(long chrononTime) {
// Get an initial estimate of the year, and the millis value that
// represents the start of that year. Then verify estimate and fix if
// necessary.
// Initial estimate uses values divided by two to avoid overflow.
long unitMillis = CHRONON_OF_YEAR / 2;
long i2 = (chrononTime >> 1) + (1970L * CHRONON_OF_YEAR) / 2;
if (i2 < 0) {
i2 = i2 - unitMillis + 1;
}
int year = (int) (i2 / unitMillis);
long yearStart = chrononizeBeginningOfYear(year);
long diff = chrononTime - yearStart;
if (diff < 0) {
year--;
} else if (diff >= CHRONON_OF_DAY * 365L) {
// One year may need to be added to fix estimate.
long oneYear;
if (isLeapYear(year)) {
oneYear = CHRONON_OF_DAY * 366L;
} else {
oneYear = CHRONON_OF_DAY * 365L;
}
yearStart += oneYear;
if (yearStart <= chrononTime) {
// Didn't go too far, so actually add one year.
year++;
}
}
return year;
}
/**
* Get the month of the year for the given chronon time and the year.
* <p/>
* This code is directly from the Joda library BasicGJChronology.java.<br/>
* The original authers are Stephen Colebourne, Brain S O'Neill and Guy Allard, and modified by JArod Wen on May 7th, 2012.
* <p/>
*
* @param millis
* @param year
* @return
*/
public int getMonthOfYear(long millis, int year) {
// Perform a binary search to get the month. To make it go even faster,
// compare using ints instead of longs. The number of milliseconds per
// year exceeds the limit of a 32-bit int's capacity, so divide by
// 1024. No precision is lost (except time of day) since the number of
// milliseconds per day contains 1024 as a factor. After the division,
// the instant isn't measured in milliseconds, but in units of
// (128/125)seconds.
int i = (int) ((millis - chrononizeBeginningOfYear(year)) >> 10);
// There are 86400000 milliseconds per day, but divided by 1024 is
// 84375. There are 84375 (128/125)seconds per day.
int leap = 0;
if (isLeapYear(year) == true) {
leap = 1; //Adding one day for the leap years
}
if (i < (181 + leap) * 84375) { /*Days before the end of June*/
if (i < (90 + leap) * 84375) { /*Days before the end of March*/
if (i < 31 * 84375) { /*Days before the end of January*/
return 1;
} else if (i < (59 + leap) * 84375) { /*Days before the end of February*/
return 2;
} else {
return 3;
}
} else if (i < (120 + leap) * 84375) { /*Days before the end of April*/
return 4;
} else if (i < (151 + leap) * 84375) { /*Days before the end of May*/
return 5;
} else {
return 6;
}
} else if (i < (273 + leap) * 84375) { /*Days before the end of September*/
if (i < (212 + leap) * 84375) { /*Days before the end of July*/
return 7;
} else if (i < (243 + leap) * 84375) { /*Days before the end of August*/
return 8;
} else {
return 9;
}
} else if (i < (304 + leap) * 84375) { /*Days before the end of October*/
return 10;
} else if (i < (334 + leap) * 84375) { /*Days before the end of November*/
return 11;
} else {
return 12;
}
}
/**
* Get the day of the given month and year for the input chronon time.
* <p/>
* This function is directly from Joda Library BasicChronology.java.<br/>
* The original authers are Stephen Colebourne, Brain S O'Neill and Guy Allard, and modified by JArod Wen on May 7th, 2012.
* <p/>
*
* @param millis
* @param year
* @param month
* @return
*/
public int getDayOfMonthYear(long millis, int year, int month) {
long dateMillis = chrononizeBeginningOfYear(year);
dateMillis += DAYS_SINCE_MONTH_BEGIN_ORDI[month - 1] * CHRONON_OF_DAY;
if (isLeapYear(year) && month > 2) {
dateMillis += CHRONON_OF_DAY;
}
return (int) ((millis - dateMillis) / CHRONON_OF_DAY) + 1;
}
/**
* Get the hour of the day for the given chronon time.
*
* @param millis
* @return
*/
public int getHourOfDay(long millis) {
int hour = (int) ((millis % CHRONON_OF_DAY) / CHRONON_OF_HOUR);
if (millis < 0) {
if (millis % CHRONON_OF_HOUR == 0) {
if (hour < 0) {
hour += 24;
}
} else {
hour += 23;
}
}
return hour;
}
/**
* Get the minute of the hour for the given chronon time.
*
* @param millis
* @return
*/
public int getMinOfHour(long millis) {
int min = (int) ((millis % CHRONON_OF_HOUR) / CHRONON_OF_MINUTE);
if (millis < 0) {
if (millis % CHRONON_OF_MINUTE == 0) {
if (min < 0) {
min += 60;
}
} else {
min += 59;
}
}
return min;
}
/**
* Get the second of the minute for the given chronon time.
*
* @param millis
* @return
*/
public int getSecOfMin(long millis) {
int sec = (int) ((millis % CHRONON_OF_MINUTE) / CHRONON_OF_SECOND);
if (millis < 0) {
if (millis % CHRONON_OF_SECOND == 0) {
if (sec < 0) {
sec += 60;
}
} else {
sec += 59;
}
}
return sec;
}
/**
* Get the millisecond of the second for the given chronon time.
*
* @param millis
* @return
*/
public int getMillisOfSec(long millis) {
int ms = (int) (millis % CHRONON_OF_SECOND);
if (millis < 0 && ms < 0) {
ms += 1000;
}
return ms;
}
public int getDurationMonth(int months) {
return (months % 12);
}
public int getDurationYear(int months) {
return (months / 12);
}
public int getDurationMillisecond(long milliseconds) {
return (int) (milliseconds % 1000);
}
public int getDurationSecond(long milliseconds) {
return (int) ((milliseconds % 60000) / 1000);
}
public int getDurationMinute(long milliseconds) {
return (int) ((milliseconds % 3600000) / 60000);
}
public int getDurationHour(long milliseconds) {
return (int) ((milliseconds % (86400000)) / 3600000);
}
public int getDurationDay(long milliseconds) {
return (int) (milliseconds / (86400000));
}
}