com.ibm.icu.impl.Grego Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of icu4j Show documentation
Show all versions of icu4j Show documentation
International Component for Unicode for Java (ICU4J) is a mature, widely used Java library
providing Unicode and Globalization support
// © 2016 and later: Unicode, Inc. and others.
// License & terms of use: http://www.unicode.org/copyright.html
/**
*******************************************************************************
* Copyright (C) 2003-2014, International Business Machines Corporation and
* others. All Rights Reserved.
*******************************************************************************
* Partial port from ICU4C's Grego class in i18n/gregoimp.h.
*
* Methods ported, or moved here from OlsonTimeZone, initially
* for work on Jitterbug 5470:
* tzdata2006n Brazil incorrect fall-back date 2009-mar-01
* Only the methods necessary for that work are provided - this is not a full
* port of ICU4C's Grego class (yet).
*
* These utilities are used by both OlsonTimeZone and SimpleTimeZone.
*/
package com.ibm.icu.impl;
import java.util.Locale;
/**
* A utility class providing proleptic Gregorian calendar functions
* used by time zone and calendar code. Do not instantiate.
*
* Note: Unlike GregorianCalendar, all computations performed by this
* class occur in the pure proleptic GregorianCalendar.
*/
public class Grego {
// Max/min milliseconds
public static final long MIN_MILLIS = -184303902528000000L;
public static final long MAX_MILLIS = 183882168921600000L;
public static final int MILLIS_PER_SECOND = 1000;
public static final int MILLIS_PER_MINUTE = 60*MILLIS_PER_SECOND;
public static final int MILLIS_PER_HOUR = 60*MILLIS_PER_MINUTE;
public static final int MILLIS_PER_DAY = 24*MILLIS_PER_HOUR;
// January 1, 1 CE Gregorian
private static final int JULIAN_1_CE = 1721426;
// January 1, 1970 CE Gregorian
private static final int JULIAN_1970_CE = 2440588;
private static final int[] MONTH_LENGTH = new int[] {
31,28,31,30,31,30,31,31,30,31,30,31,
31,29,31,30,31,30,31,31,30,31,30,31
};
private static final int[] DAYS_BEFORE = new int[] {
0,31,59,90,120,151,181,212,243,273,304,334,
0,31,60,91,121,152,182,213,244,274,305,335 };
/**
* Return true if the given year is a leap year.
* @param year Gregorian year, with 0 == 1 BCE, -1 == 2 BCE, etc.
* @return true if the year is a leap year
*/
public static final boolean isLeapYear(int year) {
// year&0x3 == year%4
return ((year&0x3) == 0) && ((year%100 != 0) || (year%400 == 0));
}
/**
* Return the number of days in the given month.
* @param year Gregorian year, with 0 == 1 BCE, -1 == 2 BCE, etc.
* @param month 0-based month, with 0==Jan
* @return the number of days in the given month
*/
public static final int monthLength(int year, int month) {
return MONTH_LENGTH[month + (isLeapYear(year) ? 12 : 0)];
}
/**
* Return the length of a previous month of the Gregorian calendar.
* @param year Gregorian year, with 0 == 1 BCE, -1 == 2 BCE, etc.
* @param month 0-based month, with 0==Jan
* @return the number of days in the month previous to the given month
*/
public static final int previousMonthLength(int year, int month) {
return (month > 0) ? monthLength(year, month-1) : 31;
}
/**
* Convert a year, month, and day-of-month, given in the proleptic
* Gregorian calendar, to 1970 epoch days.
* @param year Gregorian year, with 0 == 1 BCE, -1 == 2 BCE, etc.
* @param month 0-based month, with 0==Jan
* @param dom 1-based day of month
* @return the day number, with day 0 == Jan 1 1970
*/
public static long fieldsToDay(int year, int month, int dom) {
int y = year - 1;
long julian =
365 * y + floorDivide(y, 4) + (JULIAN_1_CE - 3) + // Julian cal
floorDivide(y, 400) - floorDivide(y, 100) + 2 + // => Gregorian cal
DAYS_BEFORE[month + (isLeapYear(year) ? 12 : 0)] + dom; // => month/dom
return julian - JULIAN_1970_CE; // JD => epoch day
}
/**
* Return the day of week on the 1970-epoch day
* @param day the 1970-epoch day (integral value)
* @return the day of week
*/
public static int dayOfWeek(long day) {
long[] remainder = new long[1];
floorDivide(day + 5 /* Calendar.THURSDAY */, 7, remainder);
int dayOfWeek = (int)remainder[0];
dayOfWeek = (dayOfWeek == 0) ? 7 : dayOfWeek;
return dayOfWeek;
}
public static int[] dayToFields(long day, int[] fields) {
if (fields == null || fields.length < 5) {
fields = new int[5];
}
// Convert from 1970 CE epoch to 1 CE epoch (Gregorian calendar)
day += JULIAN_1970_CE - JULIAN_1_CE;
long[] rem = new long[1];
long n400 = floorDivide(day, 146097, rem);
long n100 = floorDivide(rem[0], 36524, rem);
long n4 = floorDivide(rem[0], 1461, rem);
long n1 = floorDivide(rem[0], 365, rem);
int year = (int)(400 * n400 + 100 * n100 + 4 * n4 + n1);
int dayOfYear = (int)rem[0];
if (n100 == 4 || n1 == 4) {
dayOfYear = 365; // Dec 31 at end of 4- or 400-yr cycle
}
else {
++year;
}
boolean isLeap = isLeapYear(year);
int correction = 0;
int march1 = isLeap ? 60 : 59; // zero-based DOY for March 1
if (dayOfYear >= march1) {
correction = isLeap ? 1 : 2;
}
int month = (12 * (dayOfYear + correction) + 6) / 367; // zero-based month
int dayOfMonth = dayOfYear - DAYS_BEFORE[isLeap ? month + 12 : month] + 1; // one-based DOM
int dayOfWeek = (int)((day + 2) % 7); // day 0 is Monday(2)
if (dayOfWeek < 1 /* Sunday */) {
dayOfWeek += 7;
}
dayOfYear++; // 1-based day of year
fields[0] = year;
fields[1] = month;
fields[2] = dayOfMonth;
fields[3] = dayOfWeek;
fields[4] = dayOfYear;
return fields;
}
/*
* Convert long time to date/time fields
*
* result[0] : year
* result[1] : month
* result[2] : dayOfMonth
* result[3] : dayOfWeek
* result[4] : dayOfYear
* result[5] : millisecond in day
*/
public static int[] timeToFields(long time, int[] fields) {
if (fields == null || fields.length < 6) {
fields = new int[6];
}
long[] remainder = new long[1];
long day = floorDivide(time, 24*60*60*1000 /* milliseconds per day */, remainder);
dayToFields(day, fields);
fields[5] = (int)remainder[0];
return fields;
}
public static long floorDivide(long numerator, long denominator) {
// We do this computation in order to handle
// a numerator of Long.MIN_VALUE correctly
return (numerator >= 0) ?
numerator / denominator :
((numerator + 1) / denominator) - 1;
}
private static long floorDivide(long numerator, long denominator, long[] remainder) {
if (numerator >= 0) {
remainder[0] = numerator % denominator;
return numerator / denominator;
}
long quotient = ((numerator + 1) / denominator) - 1;
remainder[0] = numerator - (quotient * denominator);
return quotient;
}
/*
* Returns the ordinal number for the specified day of week in the month.
* The valid return value is 1, 2, 3, 4 or -1.
*/
public static int getDayOfWeekInMonth(int year, int month, int dayOfMonth) {
int weekInMonth = (dayOfMonth + 6)/7;
if (weekInMonth == 4) {
if (dayOfMonth + 7 > monthLength(year, month)) {
weekInMonth = -1;
}
} else if (weekInMonth == 5) {
weekInMonth = -1;
}
return weekInMonth;
}
/**
* Convenient method for formatting time to ISO 8601 style
* date string.
* @param time long time
* @return ISO-8601 date string
*/
public static String timeToString(long time) {
int[] fields = timeToFields(time, null);
int millis = fields[5];
int hour = millis / MILLIS_PER_HOUR;
millis = millis % MILLIS_PER_HOUR;
int min = millis / MILLIS_PER_MINUTE;
millis = millis % MILLIS_PER_MINUTE;
int sec = millis / MILLIS_PER_SECOND;
millis = millis % MILLIS_PER_SECOND;
return String.format((Locale)null, "%04d-%02d-%02dT%02d:%02d:%02d.%03dZ",
fields[0], fields[1] + 1, fields[2], hour, min, sec, millis);
}
}