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

ucar.nc2.time.Calendar Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 1998-2018 John Caron and University Corporation for Atmospheric Research/Unidata
 * See LICENSE for license information.
 */
package ucar.nc2.time;

import org.joda.time.Chronology;
import org.joda.time.chrono.ISOChronology;
import org.joda.time.chrono.JulianChronology;
import uk.ac.rdg.resc.edal.time.AllLeapChronology;
import uk.ac.rdg.resc.edal.time.NoLeapChronology;
import uk.ac.rdg.resc.edal.time.ThreeSixtyDayChronology;
import java.util.HashMap;
import java.util.Map;

/**
 * Implements CF calendar attribute.
 * Uses joda-time, will switch to java.time at a later date.
 * So joda-time classes are not exposed.
 *
 * @author caron
 * @since 3/22/11
 */
public enum Calendar {
  gregorian, proleptic_gregorian, noleap, all_leap, uniform30day, julian, none; // do not change order, used in protobuf

  public static Calendar get(String s) {
    if (s == null)
      return null;
    if (s.equalsIgnoreCase("gregorian") || s.equalsIgnoreCase("standard"))
      return Calendar.gregorian; // CF default, unfortunately
    if (s.equalsIgnoreCase("proleptic_gregorian") || s.equalsIgnoreCase("ISO8601"))
      return Calendar.proleptic_gregorian;
    if (s.equalsIgnoreCase("noleap") || s.equalsIgnoreCase("365_day"))
      return Calendar.noleap;
    if (s.equalsIgnoreCase("all_leap") || s.equalsIgnoreCase("366_day"))
      return Calendar.all_leap;
    if (s.equalsIgnoreCase("uniform30day") || s.equalsIgnoreCase("360_day"))
      return Calendar.uniform30day;
    if (s.equalsIgnoreCase("julian"))
      return Calendar.julian;
    if (s.equalsIgnoreCase("none"))
      return Calendar.none;
    return null;
  }

  public static Calendar getDefault() {
    return proleptic_gregorian;
  }

  public static boolean isDefaultChronology(Calendar cal) {
    return cal == null || cal == getDefault() || cal == Calendar.none;
  }

  /**
   * Map of CF identifiers for calendar systems to joda-time Chronologies
   */
  private static final Map CHRONOLOGIES = new HashMap<>();
  private static final Map CALENDARS = new HashMap<>();

  static {
    // Implements the Gregorian/Julian calendar system which is the calendar system used in most of the world. Wherever
    // possible,
    // it is recommended to use the ISOChronology instead.
    // The Gregorian calendar replaced the Julian calendar, and the point in time when this chronology switches can be
    // controlled
    // using the second parameter of the getInstance method. By default this cutover is set to the date the Gregorian
    // calendar was first instituted, October 15, 1582.
    // Before this date, this chronology uses the proleptic Julian calendar (proleptic means extending indefinitely).
    // The Julian calendar has leap years every four years, whereas the Gregorian has special rules for 100 and 400
    // years.
    // A meaningful result will thus be obtained for all input values. However before 8 CE, Julian leap years were
    // irregular,
    // and before 45 BCE there was no Julian calendar.
    // This chronology differs from GregorianCalendar in that years in BCE are returned correctly. Thus year 1 BCE is
    // returned as -1
    // instead of 1. The yearOfEra field produces results compatible with GregorianCalendar.
    // The Julian calendar does not have a year zero, and so year -1 is followed by year 1. If the Gregorian cutover
    // date is
    // specified at or before year -1 (Julian), year zero is defined. In other words, the proleptic Gregorian chronology
    // used by this class has a year zero.
    associate(Calendar.gregorian, org.joda.time.chrono.GJChronology.getInstanceUTC());

    // Implements a pure proleptic Gregorian calendar system, which defines every fourth year as leap, unless the year
    // is divisible by 100 and not by 400. This improves upon the Julian calendar leap year rule.
    // Although the Gregorian calendar did not exist before 1582 CE, this chronology assumes it did, thus it is
    // proleptic.
    // This implementation also fixes the start of the year at January 1, and defines the year zero.
    associate(Calendar.none, ISOChronology.getInstanceUTC());
    associate(Calendar.proleptic_gregorian, ISOChronology.getInstanceUTC());

    // Implements a pure proleptic Julian calendar system, which defines every fourth year as leap. This implementation
    // follows
    // the leap year rule strictly, even for dates before 8 CE, where leap years were actually irregular. In the Julian
    // calendar,
    // year zero does not exist: 1 BCE is followed by 1 CE.
    // Although the Julian calendar did not exist before 45 BCE, this chronology assumes it did, thus it is proleptic.
    // This implementation also fixes the start of the year at January 1.
    associate(Calendar.julian, JulianChronology.getInstanceUTC());

    associate(Calendar.all_leap, AllLeapChronology.getInstanceUTC());
    associate(Calendar.noleap, NoLeapChronology.getInstanceUTC());
    associate(Calendar.uniform30day, ThreeSixtyDayChronology.getInstanceUTC());
  }

  private static void associate(Calendar cal, Chronology cron) {
    CHRONOLOGIES.put(cal, cron);
    CALENDARS.put(cron, cal);
  }

  /**
   * Return joda Chronology corresponding to this Calendar, using UTC time zone.
   * 
   * @param cal want this Calendar, or null to use the default.
   * @return hronology corresponding to this Calendar
   */
  static Chronology getChronology(Calendar cal) {
    if (cal == null)
      cal = getDefault();
    return CHRONOLOGIES.get(cal);
  }

  /**
   * Return Calendar corresponding to this joda Chronology corresponding to this Calendar.
   * 
   * @param cron want this Calendar, or null to use the default.
   * @return Calendar corresponding to this joda Chronology
   */
  static Calendar of(Chronology cron) {
    if (cron == null)
      return getDefault();
    return CALENDARS.get(cron);
  }

}

/*
 * 
 * https://www.unidata.ucar.edu/software/udunits/udunits-2/udunits2lib.html#Time
 * 
 * You should use a true calendar package rather than the UDUNITS-2 package to handle time. Having said that, many
 * people use
 * the time-handling capabilities of the UDUNITS-2 package because it supports CDM.UNITS like
 * "seconds since 1970-01-01".
 * You should be aware, however, that the hybrid Gregorian/Julian calendar used by the UDUNITS-2 package cannot be
 * changed.
 * Dates on or after 1582-10-15 are assumed to be Gregorian dates; dates before that are assumed to be Julian dates.
 * In particular, the year 1 BCE is immediately followed by the year 1 CE.
 * 
 */

/*
 * 
 * CF
 * 
 * 4.4.1. Calendar
 * In order to calculate a new date and time given a base date, base time and a time increment one must know what
 * calendar to use.
 * For this purpose we recommend that the calendar be specified by the attribute calendar which is assigned to the time
 * coordinate variable.
 * The values currently defined for calendar are:
 * 
 * gregorian or standard
 * Mixed Gregorian/Julian calendar as defined by Udunits. This is the default.
 * 
 * proleptic_gregorian
 * A Gregorian calendar extended to dates before 1582-10-15. That is, a year is a leap year if either (i) it is
 * divisible by 4 but not by 100 or (ii) it is divisible by 400.
 * 
 * noleap or 365_day
 * Gregorian calendar without leap years, i.e., all years are 365 days long.
 * 
 * all_leap or 366_day
 * Gregorian calendar with every year being a leap year, i.e., all years are 366 days long.
 * 
 * 360_day
 * All years are 360 days divided into 30 day months.
 * 
 * julian
 * Julian calendar.
 * 
 * none
 * No calendar.
 * 
 * The calendar attribute may be set to none in climate experiments that simulate a fixed time of year.
 * The time of year is indicated by the date in the reference time of the units attribute.
 * The time coordinate that might apply in a perpetual July experiment are given in the following example.
 * 
 * Example 4.5. Perpetual time axis
 * 
 * variables:
 * double time(time) ;
 * time:long_name = "time" ;
 * time:units = "days since 1-7-15 0:0:0" ;
 * time:calendar = "none" ;
 * data:
 * time = 0., 1., 2., ...;
 * 
 * 
 * Here, all days simulate the conditions of 15th July, so it does not make sense to give them different dates.
 * The time coordinates are interpreted as 0, 1, 2, etc. days since the start of the experiment.
 * 
 * If none of the calendars defined above applies (e.g., calendars appropriate to a different paleoclimate era),
 * a non-standard calendar can be defined. The lengths of each month are explicitly defined with the month_lengths
 * attribute of the time axis:
 * 
 * month_lengths
 * A vector of size 12, specifying the number of days in the months from January to December (in a non-leap year).
 * 
 * If leap years are included, then two other attributes of the time axis should also be defined:
 * 
 * leap_year
 * An example of a leap year. It is assumed that all years that differ from this year by a multiple of four are also
 * leap years.
 * If this attribute is absent, it is assumed there are no leap years.
 * 
 * leap_month
 * A value in the range 1-12, specifying which month is lengthened by a day in leap years (1=January). If this attribute
 * is not present, February (2) is assumed. This attribute is ignored if leap_year is not specified.
 * 
 * The calendar attribute is not required when a non-standard calendar is being used. It is sufficient to define the c
 * alendar using the month_lengths attribute, along with leap_year, and leap_month as appropriate. However, the calendar
 * attribute is allowed to take non-standard values and in that case defining the non-standard calendar using the
 * appropriate attributes is required.
 * 
 * Example 4.6. Paleoclimate time axis
 * 
 * double time(time) ;
 * time:long_name = "time" ;
 * time:units = "days since 1-1-1 0:0:0" ;
 * time:calendar = "126 kyr B.P." ;
 * time:month_lengths = 34, 31, 32, 30, 29, 27, 28, 28, 28, 32, 32, 34 ;
 * 
 * 
 * The mixed Gregorian/Julian calendar used by Udunits is explained in the following excerpt from the udunits(3) man
 * page:
 * 
 * The udunits(3) package uses a mixed Gregorian/Julian calen-
 * dar system. Dates prior to 1582-10-15 are assumed to use
 * the Julian calendar, which was introduced by Julius Caesar
 * in 46 BCE and is based on a year that is exactly 365.25 days
 * long. Dates on and after 1582-10-15 are assumed to use the
 * Gregorian calendar, which was introduced on that date and is
 * based on a year that is exactly 365.2425 days long. (A year
 * is actually approximately 365.242198781 days long.) Seem-
 * ingly strange behavior of the udunits(3) package can result
 * if a user-given time interval includes the changeover date.
 * For example, utCalendar() and utInvCalendar() can be used to
 * show that 1582-10-15 *preceded* 1582-10-14 by 9 days.
 * 
 * Due to problems caused by the discontinuity in the default mixed Gregorian/Julian calendar, we strongly recommend
 * that this calendar
 * should only be used when the time coordinate does not cross the discontinuity. For time coordinates that do cross the
 * discontinuity
 * the proleptic_gregorian calendar should be used instead.
 */




© 2015 - 2025 Weber Informatics LLC | Privacy Policy