The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Date::Gregorian - Gregorian calendar

VERSION

This documentation refers to version 0.13 of Date::Gregorian.

SYNOPSIS

  use Date::Gregorian;
  use Date::Gregorian qw(:weekdays :months);

  $date = Date::Gregorian->new->set_ymd(1999, 12, 31);
  $date2 = $date->new;

  if ($date->check_ymd($year, $month, $day)) {
    # valid, $date has new value
  }
  else {
    # not valid, $date remains unchanged
  }

  ($year, $month, $day) = $date->get_ymd;

  $wd = (qw(Mon Tue Wed Thu Fri Sat Sun))[$date->get_weekday];
  $date->set_yd(2000, 366);           # Dec 31, 2000
  ($year, $day_in_year) = $date->get_yd;
  $date->set_ywd(1998, 53, 6);        # Sun Jan 3, 1999
  ($year, $week_in_year, $weekday) = $date->get_ywd;

  if ($date->check_ywd($year, $week, $weekday)) {
    # valid, $date has new value
  }
  else {
    # not valid, $date remains unchanged
  }

  $date->add_days(-100);
  $delta = $date->get_days_since($date2);
  $delta = $date2->get_days_until($date);
  $date->set_easter($y);
  $date->set_today;
  $date->set_localtime($time);
  $date->set_gmtime($time);
  $time = $date->get_gmtime;

  # compare two dates
  $cmp = $date->compare($date2);
  # sort dates
  @sorted = sort {$a->compare($b)} @dates;

  $iterator = $date->iterate_days_upto($date2, '<');
  $iterator = $date->iterate_days_upto($date2, '<', $step);
  $iterator = $date->iterate_days_upto($date2, '<=');
  $iterator = $date->iterate_days_upto($date2, '<=', $step);
  $iterator = $date->iterate_days_downto($date2, '>');
  $iterator = $date->iterate_days_downto($date2, '>', $step);
  $iterator = $date->iterate_days_downto($date2, '>=');
  $iterator = $date->iterate_days_downto($date2, '>=', $step);
  while ($iterator->()) {
    printf "%04d-%02d-%02d\n", $date->get_ymd;
  }

  $date->configure(1752, 9, 14);
  $date->configure(1752, 9, 14, 1753);        # United Kingdom
  $date2->configure(1918, 2, 14);             # Russia

  $date2->set_ymd(1917, 10, 25);      # pre-Gregorian Oct 25, 1917
  $date->set_date($date2);            # Gregorian Nov 7, 1917 (same day)

  if ($date->is_gregorian) {
    # date is past configured calendar reformation,
    # thus in Gregorian notation
  }
  else {
    # date is before configured calendar reformation,
    # thus in Julian notation
  }

  # get the first sunday in October:
  $date->set_ymd($year, 10,  1)->set_weekday(6, '>=');
  # get the last sunday in October:
  $date->set_ymd($year, 11,  1)->set_weekday(6, '<');

  # calculate number of days in 2000:
  $days = $date->get_days_in_year(2000);

  # plaintext representation of dates
  $str = $date->get_string;
  $date->set_string($str) or warn "syntax error";

  # DateTime interface
  use DateTime;
  $dt = DateTime->now(time_zone => 'Europe/Berlin');
  $date->set_datetime($dt);
  $dt = DateTime->from_object(object => $date);
  $date = Date::Gregorian->from_object($dt);
  ($rata_die, $sec, $nanosec) = $date->utc_rd_values();
  $date->truncate_to_day;

DESCRIPTION

Calendars define some notation to identify days in history. The Gregorian calendar, now in wide use, was established by Pope Gregory XIII in AD 1582 as an improvement over the less accurate Julian calendar that had been in use before. Both of these calendars also determine certain holidays. Unfortunately, the new one was not adopted everywhere at the same time. Thus, the correct date for a given historic event can depend on its location. Astronomers usually expand the official Julian/Gregorian calendar backwards beyond AD 1 using zero and negative numbers, so that year 0 is 1 BC, year -1 is 2 BC, etc.

This module provides an object class representing days in history. You can get earlier or later dates by way of adding days to them, determine the difference in days between two of them, and read out the day, month and year number as the (astronomic) Gregorian calendar defines them (numbers 1 through 12 representing January through December). Moreover, you can find out weekdays, easter sundays, week in year and day in year numbers.

For convenience, it is also possible to define the switching day from Julian to Gregorian dates and the switching year from pre-Gregorian to Gregorian easter schedule. Use configure with the first valid date of the new calendar and optionally the first year the new easter schedule was used (default 1583).

The module is based on an algorithm devised by C. F. Gauss (1777-1855). It is completely written in Perl for maximum portability.

All methods except get_* and iterate_* return their object. This allows for shortcuts like:

  $pentecost = Date::Gregorian->new->set_easter(2000)->add_days(49);

Numbers 0 through 6 represent weekdays Monday through Sunday. Day in month ranges from 1 to 31, day in year from 1 to 366, week in year from 1 to 53. Weeks are supposed to start on Monday. The first week of a year is the one containing January 4th. These definitions are slightly closer to ISO 8601 than to Perl's builtin time conversion functions. Weekday numbers, however, are zero-based for ease of use as array indices.

(Author's note: I wish now I had defined 1-based weekdays when the module was young, to make things nice and consistent, but backwards compatibility suggests not to revise that decision. If you prefer consistent code, subtract JANUARY from a month value and MONDAY from a weekday value to get a 0-based array index in any case.)

Numeric parameters must be integer numbers throughout the module.

For convenience, weekdays and months can be imported as constants MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY, and JANUARY, FEBRUARY, MARCH, APRIL, MAY, JUNE, JULY, AUGUST, SEPTEMBER, OCTOBER, NOVEMBER, DECEMBER. The tag :weekdays provides all weekdays, as :months does all month names. By default, nothing is exported.

new

new creates a Date::Gregorian object from scratch (if called as a class method) or as a copy of an existing object. The latter is more efficient than the former. new does not take any arguments.

set_date

set_date sets one Date::Gregorian object to the same day another object represents. The objects do not need to share a common calendar configuration.

set_ymd

set_ymd sets year, month and day to new absolute values. Days and months out of range are silently folded to standard dates, in a way that is intended to preserve continuity: Month 13 is treated as month 1 of the next year, month 14 as month 2 of the next year, month 0 as month 12 of the previous year, day 0 as the last day of the previous month, etc. Thus, e.g., the date 10000 days before February 22, 2002 can be defined like this:

  $date->set_ymd(2002, 2, 22-10000)

check_ymd

check_ymd, on the other hand, checks a given combination of year, month and day for validity. Given a valid date, the object is updated and the object itself is returned, evaluating to true in boolean context. Otherwise, the object remains untouched and undef is returned.

get_ymd

get_ymd returns year, month and day as a three-item list.

get_weekday

get_weekday returns the weekday as a number in the range of 0 to 6, with 0 representing Monday, 1 Tuesday, 2 Wednesday, 3 Thursday, 4 Friday, 5 Saturday and 6 representing Sunday.

set_yd get_yd

set_yd and get_yd set or get dates as a pair of year and day in year numbers, day 1 representing January 1, day 32 February 1 etc.

set_ywd get_ywd

set_ywd and get_ywd set or get dates as a tuple of year, week in year and day in week numbers. As noted above, weeks are supposed to start on Mondays. Weeks containing days of both December and January belong to the year containing more days of them. Because of this, get_ywd and get_ymd may return different year numbers. Week numbers range from 1 to 53 (max).

check_ywd

check_ywd checks a given combination of year, week in year and weekday for validity. Given a valid date, the object is updated and the object itself is returned, evaluating to true in boolean context. Otherwise, the object remains untouched and undef is returned.

Note that year 1582 (or whatever year was configured to have the Gregorian calendar reformation) was considerably shorter than a normal year. Such a year has some invalid dates that otherwise might seem utterly inconspicuos.

add_days

add_days increases, or, given a negative argument, decreases, a date by a number of days. Its new value represents a day that many days later in history if a positive number of days was added. Adding a negative number of days consequently shifts a date back towards the past.

get_days_since

get_days_since computes the difference of two dates as a number of days. The result is positive if the given date is an earlier date than the one whose method is called, negative if it is a later one. Look at it as a subtraction operation, yielding a positive result if something smaller is subtracted from something larger, "smaller" meaning "earlier" in this context.

get_days_until

get_days_until computes the same value as get_days_since, only with opposite sign.

compare

compare compares two dates chronologically. Result is zero if the dates refer to the same day, -1 if the method invocant refers to an earlier day than the parameter and 1 otherwise.

iterate_days_upto iterate_days_downto

iterate_days_upto and iterate_days_downto provide convenient methods to iterate over a range of dates. They return a reference to a subroutine that can be called without argument in a while condition to set the given date iteratively to each one of a sequence of dates. The current date is always the first one to be visited (unless the sequence is all empty). The limit parameter determines the end of the sequence, together with the relation parameter: '<' excludes the upper limit from the sequence, '<=' includes the upper limit, '>=' includes the lower limit and '>' excludes the lower limit. The step parameter is optional. It must be greater than zero and defines how many days the dates in the sequence lie apart. It defaults to one.

Each iterator maintains its own state; therefore it is legal to run more than one iterator in parallel or even create new iterators within iterations.

set_easter

set_easter computes the date of Easter sunday of a given year, taking into account how the date object was configured.

set_weekday

set_weekday computes a date matching a given weekday that is close to the date it is applied to. The optional relation parameter may be one of '>=', '>', '<=' or '<', and determines if the resulting date should be "equal or later", later, "equal or earlier", or earlier, respectively, than the initial date. Default is '>='.

set_today

set_today computes a date value equivalent to the system's notion of the current date in the local timezone. System time is assumed to run in Gregorian mode.

set_localtime

set_localtime likewise computes a date value equivalent to a given arbitrary time value in the local timezone.

set_gmtime

set_gmtime computes a date value equivalent to a given time value in the "GMT" system timezone. This timezone represents a time scale counting a constant number of seconds per day since an OS- and implementation dependent starting point -- the epoch -- and not counting leap seconds. This makes arithmetic on timestamps easy but forces clocks to be frequently adjusted to the earth rotation. On POSIX-like systems this timezone is used for timestamps. On systems using the gmtime call in any other fashion set_gmtime and get_gmtime are not guaranteed to comply with it.

get_gmtime

get_gmtime converts a date value back to a timestamp in the "GMT" timezone explained above. This method may return undef if the date seems to be out of range. Note that the precision of timestamps represented by Date::Gregorian objects is normally limited to days. Thus, converting a timestamp to a date and back again usually truncates the timestamp to midnight. Extension classes may behave differently, however.

get_localtime

get_localtime converts a date value back to a system timestamp in the current local timezone. Undef is returned if the date seems to be out of range. As with get_gmtime, the precision is normally limited to days. However, values returned by get_localtime for successive days need not follow a simple arithmetic progression, as they interpolate actual localtime calls, and the local timezone may incorporate oddities like daylight saving time changes.

Note also that timestamps are not portable. While the conversion functions described here make an effort to cover the local clock behaviour, mostly in order to make set_today work, they depend on the Perl builtin functions localtime and gmtime, which in turn are OS- and implementation-specific. localtime may also depend on the environment and other dynamic configuration settings.

get_days_in_year

get_days_in_year computes the number of days in a given year (independent of the year stored in the date object, but taking into account its configuration).

configure

configure defines the way the Gregorian calendar reformation should be handled in calculations with the date object and any new ones later cloned with new from this one. The first three arguments specify the year, month and day of the first day the new calendar was in use. The optional fourth argument defines the first year the new easter schedule has to be used in easter calculations. Re-configuring a date object is legal and does not change the day in history it represents while possibly changing the year, month and day values related to it.

is_gregorian

is_gregorian returns a boolean value telling whether a date is past the configured calendar reformation and thus will yield year, month and day values in Gregorian mode.

get_string

get_string returns a plaintext representation of the date represented by an object.

set_string

set_string restores a date value from a string returned by get_string. Strings of the form "YYYY-MM-DD" are also accepted. The return value is undef if the syntax could not be recognized, otherwise the object. set_string handles values out of range the same way set_ymd does.

DateTime interoperability

Date::Gregorian objects can be converted to DateTime objects and vice versa. From the view of DateTime, Date::Gregorian implements a calendar operating in the floating timezone. From the view of Date::Gregorian, DateTime objects represent days in history in a way suitable for object initialization. Higher precision components of DateTime objects, i.e. seconds and nanoseconds, are preserved for reverse conversion but otherwise ignored.

set_datetime

set_datetime sets a Date::Gregorian object to the day represented by a given DateTime object. It returns the updated object.

from_object

from_object is a DateTime compatible constructor. Arguments are mapped to a hash. The value in the 'object' slot is taken to be a DateTime object. The result is a Date::Gregorian object. Note that Date::Gregorian is not a subclass of DateTime.

utc_rd_values

utc_rd_values returns a list of rata die, seconds and nanoseconds values corresponding to the date currently represented by the object. Seconds and nanoseconds will default to zero if not initialized from some DateTime object, and will be ignored by all other Date::Gregorian methods. In particular, date objects differing only in their hidden seconds or nanoseconds values are considered equivalent by compare.

truncate_to_day

truncate_to_day drops seconds and nanoseconds from a date. This will have an effect on DateTime objects subsequently initialized from that object. Return value is the object.

EXPORTS

By default, nothing is exported into the caller's namespace. Optionally, uppercase English weekday and month names may be imported individually or using the :weekdays and :months tags. These constants should be preferred over their numerical values as documented above for readability and in order not to depend on zero or one being the smallest value.

BUGS AND LIMITATIONS

This library works with integer arithmetic only. Do not call methods expecting days, months, years, etc. with non-integer values.

Bug reports and suggestions are always welcome — please submit them through the CPAN RT, http://rt.cpan.org/NoAuth/Bugs.html?Dist=Date-Gregorian.

ROADMAP

The author intends to re-factor this library and combine its algorithms with a better API, addressing these issues:

  • Make date objects immutable.

  • Add time arguments to gmtime and localtime conversions.

  • Add more business calendars.

  • Name days and holidays.

  • Unify simple date arithmetic and business day arithmetic.

  • Comply more strictly with ISO 8601. Notably, use 1-based weekday numbers.

The new API will live in the Date::Gregorian namespace but use different module names. That way, old and new APIs can co-exist while downstream applications prepare for the transition.

SEE ALSO

The sci.astro Calendar FAQ, Date::Calc, Date::Gregorian::Business, DateTime.

AUTHOR

Martin Becker <becker-cpan-mp (at) cozap.com>

LICENSE AND COPYRIGHT

Copyright (c) 1999-2019 by Martin Becker, Blaubeuren.

This library is free software; you can distribute it and/or modify it under the terms of the Artistic License 2.0 (see the LICENSE file).

DISCLAIMER OF WARRANTY

This library 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.