/*************************************************************************** * Copyright © 2005-2009 by Gabriele Svelto * * gabriele.svelto@gmail.com * * * * This file is part of Jelatine. * * * * Jelatine is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * Jelatine is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with Jelatine. If not, see . * ***************************************************************************/ package jelatine; import java.util.Calendar; import java.util.Date; /** * Internal implementation of the default Calendar class */ public final class VMCalendar extends Calendar { /** * Constant representing the era BC (Before Christ). */ public static final int BC = 0; /** * Constant representing the era AD (Anno Domini). */ public static final int AD = 1; /** * The point at which the Gregorian calendar rules were used. * This may be changed by using setGregorianChange; * The default is midnight (UTC) on October 5, 1582 (Julian), * or October 15, 1582 (Gregorian). * * @serial the changeover point from the Julian calendar * system to the Gregorian. */ private long gregorianCutover = (new Date((24 * 60 * 60 * 1000L) * (((1582 * (365 * 4 + 1)) / 4 + (java.util.Calendar.OCTOBER * (31 + 30 + 31 + 30 + 31) - 9) / 5 + 5) - ((1970 * (365 * 4 + 1)) / 4 + 1 - 13)))).getTime(); /** * Days in the epoch. Relative Jan 1, year '0' which is not a leap year. * (although there is no year zero, this does not matter.) * This is consistent with the formula: * = (year-1)*365L + ((year-1) >> 2) * * Plus the gregorian correction: * Math.floor((year-1) / 400.) - Math.floor((year-1) / 100.); * For a correct julian date, the correction is -2 instead. * * The gregorian cutover in 1582 was 10 days, so by calculating the * correction from year zero, we have 15 non-leap days (even centuries) * minus 3 leap days (year 400,800,1200) = 12. Subtracting two corrects * this to the correct number 10. */ private static final int EPOCH_DAYS = 719162; /** * Constructs a new GregorianCalender representing the current * time, using the default time zone and the default locale. */ public VMCalendar() { super(); setTimeInMillis(System.currentTimeMillis()); complete(); } /** * Implements the abstract computeTime() method */ protected void computeTime() { int millisInDay = 0; int year = fields[YEAR]; int month = fields[MONTH]; int day = fields[DAY_OF_MONTH]; int minute = fields[MINUTE]; int second = fields[SECOND]; int millis = fields[MILLISECOND]; int[] month_days = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; int[] dayCount = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 }; int hour = 0; // rest of code assumes day/month/year set // should negative BC years be AD? // get the hour (but no check for validity) if (isSet[HOUR]) { hour = fields[HOUR]; if (fields[AM_PM] == PM) hour += 12; } else hour = fields[HOUR_OF_DAY]; // Read the era,year,month,day fields and convert as appropriate. // Calculate number of milliseconds into the day // This takes care of both h, m, s, ms over/underflows. long allMillis = (((hour * 60L) + minute) * 60L + second) * 1000L + millis; day += allMillis / (24 * 60 * 60 * 1000L); millisInDay = (int) (allMillis % (24 * 60 * 60 * 1000L)); if (month < 0) { year += (int) month / 12; month = month % 12; if (month < 0) { month += 12; year--; } } if (month > 11) { year += (month / 12); month = month % 12; } month_days[1] = isLeapYear(year) ? 29 : 28; while (day <= 0) { if (month == 0) { year--; month_days[1] = isLeapYear(year) ? 29 : 28; } month = (month + 11) % 12; day += month_days[month]; } while (day > month_days[month]) { day -= (month_days[month]); month = (month + 1) % 12; if (month == 0) { year++; month_days[1] = isLeapYear(year) ? 29 : 28; } } // ok, by here we have valid day,month,year,era and millisinday int dayOfYear = dayCount[month] + day - 1; // (day starts on 1) if (isLeapYear(year) && month > 1) dayOfYear++; int relativeDay = (year - 1) * 365 + ((year - 1) >> 2) + dayOfYear - EPOCH_DAYS; // gregorian days from 1 to epoch. int gregFactor = (int) Math.floor((double) (year - 1) / 400.) - (int) Math.floor((double) (year - 1) / 100.); if ((relativeDay + gregFactor) * 60L * 60L * 24L * 1000L >= gregorianCutover) relativeDay += gregFactor; else relativeDay -= 2; time = relativeDay * (24 * 60 * 60 * 1000L) + millisInDay; // the epoch was a Thursday. int weekday = (int) (relativeDay + THURSDAY) % 7; if (weekday <= 0) weekday += 7; fields[DAY_OF_WEEK] = weekday; /* No Time zone corrections are required since the only time zone * which must be supported by the CLDC spec is GMT. */ isTimeSet = true; } /** * Implements the abstract computeFields() method */ protected void computeFields() { boolean gregorian = (time >= gregorianCutover); long day = time / (24 * 60 * 60 * 1000L); int millisInDay = (int) (time % (24 * 60 * 60 * 1000L)); if (millisInDay < 0) { millisInDay += (24 * 60 * 60 * 1000); day--; } calculateDay(fields, day, gregorian); if (millisInDay >= 24 * 60 * 60 * 1000) { millisInDay -= 24 * 60 * 60 * 1000; calculateDay(fields, ++day, gregorian); } int hourOfDay = millisInDay / (60 * 60 * 1000); fields[AM_PM] = (hourOfDay < 12) ? AM : PM; int hour = hourOfDay % 12; fields[HOUR] = hour; fields[HOUR_OF_DAY] = hourOfDay; millisInDay %= (60 * 60 * 1000); fields[MINUTE] = millisInDay / (60 * 1000); millisInDay %= (60 * 1000); fields[SECOND] = millisInDay / (1000); fields[MILLISECOND] = millisInDay % 1000; areFieldsSet = isSet[YEAR] = isSet[MONTH] = isSet[DAY_OF_MONTH] = isSet[DAY_OF_WEEK] = isSet[AM_PM] = isSet[HOUR] = isSet[HOUR_OF_DAY] = isSet[MINUTE] = isSet[SECOND] = isSet[MILLISECOND] = true; } private boolean isLeapYear(int year) { // Only years divisible by 4 can be leap years if ((year & 3) != 0) return false; // Is the leap-day a Julian date? Then it's a leap year if (! isGregorian(year, 31 + 29 - 1)) return true; // Apply gregorian rules otherwise return ((year % 100) != 0 || (year % 400) == 0); } private boolean isGregorian(int year, int dayOfYear) { int relativeDay = (year - 1) * 365 + ((year - 1) >> 2) + dayOfYear - EPOCH_DAYS; // gregorian days from 1 to epoch. int gregFactor = (int) Math.floor((double) (year - 1) / 400.) - (int) Math.floor((double) (year - 1) / 100.); return ((relativeDay + gregFactor) * 60L * 60L * 24L * 1000L >= gregorianCutover); } /** * Converts the given linear day into era, year, month, * day_of_year, day_of_month, day_of_week, and writes the result * into the fields array. * * @param day the linear day. * @param gregorian true, if we should use Gregorian rules. */ private void calculateDay(int[] fields, long day, boolean gregorian) { // the epoch was a Thursday. int weekday = (int) (day + THURSDAY) % 7; if (weekday <= 0) weekday += 7; fields[DAY_OF_WEEK] = weekday; // get a first approximation of the year. This may be one // year too big. int year = 1970 + (int) (gregorian ? ((day - 100L) * 400L) / (365L * 400L + 100L - 4L + 1L) : ((day - 100L) * 4L) / (365L * 4L + 1L)); if (day >= 0) year++; long firstDayOfYear = getLinearDay(year, 1, gregorian); // Now look in which year day really lies. if (day < firstDayOfYear) { year--; firstDayOfYear = getLinearDay(year, 1, gregorian); } day -= firstDayOfYear - 1; // day of year, one based. if (year <= 0) { fields[YEAR] = 1 - year; } else { fields[YEAR] = year; } int leapday = isLeapYear(year) ? 1 : 0; if (day <= 31 + 28 + leapday) { fields[MONTH] = (int) day / 32; // 31->JANUARY, 32->FEBRUARY fields[DAY_OF_MONTH] = (int) day - 31 * fields[MONTH]; } else { // A few more magic formulas int scaledDay = ((int) day - leapday) * 5 + 8; fields[MONTH] = scaledDay / (31 + 30 + 31 + 30 + 31); fields[DAY_OF_MONTH] = (scaledDay % (31 + 30 + 31 + 30 + 31)) / 5 + 1; } } /** * Get the linear day in days since the epoch, using the * Julian or Gregorian calendar as specified. If you specify a * nonpositive year it is interpreted as BC as following: 0 is 1 * BC, -1 is 2 BC and so on. * * @param year the year of the date. * @param dayOfYear the day of year of the date; 1 based. * @param gregorian true, if we should use the Gregorian rules. * @return the days since the epoch, may be negative. */ private long getLinearDay(int year, int dayOfYear, boolean gregorian) { // The 13 is the number of days, that were omitted in the Gregorian // Calender until the epoch. // We shift right by 2 instead of dividing by 4, to get correct // results for negative years (and this is even more efficient). long julianDay = (year - 1) * 365L + ((year - 1) >> 2) + (dayOfYear - 1) - EPOCH_DAYS; // gregorian days from 1 to epoch. if (gregorian) { // subtract the days that are missing in gregorian calendar // with respect to julian calendar. // // Okay, here we rely on the fact that the gregorian // calendar was introduced in the AD era. This doesn't work // with negative years. // // The additional leap year factor accounts for the fact that // a leap day is not seen on Jan 1 of the leap year. int gregOffset = (int) Math.floor((double) (year - 1) / 400.) - (int) Math.floor((double) (year - 1) / 100.); return julianDay + gregOffset; } else julianDay -= 2; return julianDay; } }