All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.squarespace.cldrengine.api.GregorianDate Maven / Gradle / Ivy

The newest version!
package com.squarespace.cldrengine.api;

import static com.squarespace.cldrengine.internal.MathFix.floorDiv;

import com.squarespace.cldrengine.internal.MathFix;
import com.squarespace.cldrengine.utils.MathUtil;

/**
 * Construct a date using the rules of the Gregorian calendar.
 *
 * type: gregory
 */
public class GregorianDate extends CalendarDate {

  protected GregorianDate(CalendarType type, int firstDay, int minDays) {
    super(type, firstDay, minDays);
  }

  public static GregorianDate fromUnixEpoch(long epoch, String zoneId, int firstDay, int minDays) {
    return new GregorianDate(CalendarType.GREGORY, firstDay, minDays)._initFromUnixEpoch(epoch, zoneId);
  }

  @Override
  public GregorianDate add(TimePeriod fields) {
    Pair result = this._add(fields);
    return new GregorianDate(CalendarType.GREGORY, this.firstDay, this.minDays)
        ._initFromJD(result._1, Math.round(result._2), this.timeZoneId());
  }

  @Override
  public GregorianDate subtract(TimePeriod fields) {
    return add(invertPeriod(fields));
  }

  @Override
  public GregorianDate withZone(String zoneId) {
    GregorianDate d = new GregorianDate(CalendarType.GREGORY, this.firstDay, this.minDays);
    return d._initFromUnixEpoch(this.unixEpoch(), zoneId);
  }

  @Override
  protected int daysInMonth(long year, int month) {
    return MONTH_COUNT[month][leapGregorian(year) ? 1 : 0];
  }

  @Override
  protected int daysInYear(long year) {
    return leapGregorian(year) ? 366 : 365;
  }

  @Override
  protected int monthCount() {
    return 12;
  }

  @Override
  public String toString() {
    return this._toString("Gregorian");
  }

  protected GregorianDate _initFromUnixEpoch(long epoch, String zoneId) {
    super.initFromUnixEpoch(epoch, zoneId);
    this.initFields(this.fields);
    return this;
  }

  protected GregorianDate _initFromJD(long jd, long msDay, String zoneId) {
    super.initFromJD(jd, msDay, zoneId);
    this.initFields(this.fields);
    return this;
  }

  @Override
  protected void initFields(long[] f) {
    if (f[DateField.JULIAN_DAY] >= CalendarConstants.JD_GREGORIAN_CUTOVER) {
      computeGregorianFields(f);
    } else {
      computeJulianFields(f);
    }
    long year = f[DateField.EXTENDED_YEAR];
    long era = 1; // AD;
    if (year < 1) {
      era = 0;
      year = 1 - year;
    }
    f[DateField.ERA] = era;
    f[DateField.YEAR] = year;
  }

  @Override
  protected long monthStart(long eyear, double month, boolean useMonth) {
    boolean isLeap = eyear % 4 == 0;
    long y = eyear - 1;
    long jd = 365 * y + MathFix.floorDiv(y, 4) + (CalendarConstants.JD_GREGORIAN_EPOCH - 3);
    if (eyear >= CalendarConstants.JD_GREGORIAN_CUTOVER_YEAR) {
      isLeap = isLeap && ((eyear % 100 != 0) || (eyear % 400 == 0));
      jd += MathFix.floorDiv(y, 400) - MathFix.floorDiv(y, 100) + 2;
    }
    if (month != 0) {
      int m = (int)Math.floor(month);
      double d = month - m;
      jd += MONTH_COUNT[m][isLeap ? 3 : 2];

      // Check if there is a fractional month part, and if so add the number
      // of the days in the next month multiplied by the fraction
      if (d != 0) {
        // note: the 'month' parameter must always be <= # months in the calendar
        // year, so <= 12 in this case.
        jd += d * MONTH_COUNT[m + 1][isLeap ? 1 : 0];
      }
    }
    return jd;
  }

  /**
   * Compute fields for dates on or after the Gregorian cutover.
   */
  protected void computeGregorianFields(long[] f) {
    long ged = f[DateField.JULIAN_DAY] - CalendarConstants.JD_GREGORIAN_EPOCH;
    long[] rem = new long[1];
    long n400 = MathUtil.floorDiv(ged, 146097, rem);
    long n100 = MathUtil.floorDiv(rem[0], 36524, rem);
    long n4 = MathUtil.floorDiv(rem[0], 1461, rem);
    long n1 = MathUtil.floorDiv(rem[0], 365, rem);

    long year = 400 * n400 + 100 * n100 + 4 * n4 + n1;
    long doy = rem[0]; // 0-based day of year
    if (n100 == 4 || n1 == 4) {
      doy = 365;
    } else {
      ++year;
    }

    boolean isLeap = leapGregorian(year);
    long corr = 0;
    long mar1 = isLeap ? 60 : 59;
    if (doy >= mar1) {
      corr = isLeap ? 1 : 2;
    }
    int month = (int)MathFix.floorDiv(12 * (doy + corr) + 6, 367);
    long dom = doy - MONTH_COUNT[month][isLeap ? 3 : 2] + 1;

    f[DateField.EXTENDED_YEAR] = year;
    f[DateField.MONTH] = month + 1;
    f[DateField.DAY_OF_MONTH] = dom;
    f[DateField.DAY_OF_YEAR] = doy + 1;
    f[DateField.IS_LEAP] = isLeap ? 1 : 0;
  }

  /**
   * Compute fields for dates before the Gregorian cutover using the proleptic
   * Julian calendar. Any Gregorian date before October 15, 1582 is really a
   * date on the proleptic Julian calendar, with leap years every 4 years.
   */
  protected void computeJulianFields(long[] f) {
    long jed = f[DateField.JULIAN_DAY] - (CalendarConstants.JD_GREGORIAN_EPOCH - 2);
    long eyear = floorDiv(4 * jed + 1464, 1461);
    long jan1 = 365 * (eyear - 1) + floorDiv(eyear - 1, 4);
    long doy = jed - jan1;
    boolean isLeap = eyear % 4 == 0;
    long corr = 0;
    long mar1 = isLeap ? 60 : 59;
    if (doy >= mar1) {
      corr = isLeap ? 1 : 2;
    }

    int month = (int)(12 * (doy + corr) + 6) / 367;
    long dom = doy - MONTH_COUNT[month][isLeap ? 3 : 2] + 1;

    f[DateField.EXTENDED_YEAR] = eyear;
    f[DateField.MONTH] = month + 1;
    f[DateField.DAY_OF_MONTH] = dom;
    f[DateField.DAY_OF_YEAR] = doy + 1;
    f[DateField.IS_LEAP] = isLeap ? 1 : 0;
  }
  
  /**
   * Return true if the given year is a leap year in the Gregorian calendar; false otherwise.
   * Note that we switch to the Julian calendar at the Gregorian cutover year.
   */
  protected boolean leapGregorian(long year) {
    boolean r = year % 4 == 0;
    if (year >= CalendarConstants.JD_GREGORIAN_CUTOVER_YEAR) {
      r = r && ((year % 100 != 0) || (year % 400 == 0));
    }
    return r;
  }
  
  private static final int[][] MONTH_COUNT = new int[][] {
    new int[] { 31,  31,   0,   0 }, // Jan
    new int[] { 28,  29,  31,  31 }, // Feb
    new int[] { 31,  31,  59,  60 }, // Mar
    new int[] { 30,  30,  90,  91 }, // Apr
    new int[] { 31,  31, 120, 121 }, // May
    new int[] { 30,  30, 151, 152 }, // Jun
    new int[] { 31,  31, 181, 182 }, // Jul
    new int[] { 31,  31, 212, 213 }, // Aug
    new int[] { 30,  30, 243, 244 }, // Sep
    new int[] { 31,  31, 273, 274 }, // Oct
    new int[] { 30,  30, 304, 305 }, // Nov
    new int[] { 31,  31, 334, 335 }  // Dec
  };

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy