* io.c (rb_open_file): encoding in mode string was ignored if perm is
[ruby-svn.git] / lib / date.rb
blobd2fc8613b2bebe72a2080eddb2acb7893f2f3314
2 # date.rb - date and time library
4 # Author: Tadayoshi Funaba 1998-2008
6 # Documentation: William Webber <william@williamwebber.com>
8 #--
9 # $Id: date.rb,v 2.37 2008-01-17 20:16:31+09 tadf Exp $
10 #++
12 # == Overview
14 # This file provides two classes for working with
15 # dates and times.
17 # The first class, Date, represents dates.
18 # It works with years, months, weeks, and days.
19 # See the Date class documentation for more details.
21 # The second, DateTime, extends Date to include hours,
22 # minutes, seconds, and fractions of a second.  It
23 # provides basic support for time zones.  See the
24 # DateTime class documentation for more details.
26 # === Ways of calculating the date.
28 # In common usage, the date is reckoned in years since or
29 # before the Common Era (CE/BCE, also known as AD/BC), then
30 # as a month and day-of-the-month within the current year.
31 # This is known as the *Civil* *Date*, and abbreviated
32 # as +civil+ in the Date class.
34 # Instead of year, month-of-the-year,  and day-of-the-month,
35 # the date can also be reckoned in terms of year and
36 # day-of-the-year.  This is known as the *Ordinal* *Date*,
37 # and is abbreviated as +ordinal+ in the Date class.  (Note
38 # that referring to this as the Julian date is incorrect.)
40 # The date can also be reckoned in terms of year, week-of-the-year,
41 # and day-of-the-week.  This is known as the *Commercial*
42 # *Date*, and is abbreviated as +commercial+ in the
43 # Date class.  The commercial week runs Monday (day-of-the-week
44 # 1) to Sunday (day-of-the-week 7), in contrast to the civil
45 # week which runs Sunday (day-of-the-week 0) to Saturday
46 # (day-of-the-week 6).  The first week of the commercial year
47 # starts on the Monday on or before January 1, and the commercial
48 # year itself starts on this Monday, not January 1.
50 # For scientific purposes, it is convenient to refer to a date
51 # simply as a day count, counting from an arbitrary initial
52 # day.  The date first chosen for this was January 1, 4713 BCE.
53 # A count of days from this date is the *Julian* *Day* *Number*
54 # or *Julian* *Date*, which is abbreviated as +jd+ in the
55 # Date class.  This is in local time, and counts from midnight
56 # on the initial day.  The stricter usage is in UTC, and counts
57 # from midday on the initial day.  This is referred to in the
58 # Date class as the *Astronomical* *Julian* *Day* *Number*, and
59 # abbreviated as +ajd+.  In the Date class, the Astronomical
60 # Julian Day Number includes fractional days.
62 # Another absolute day count is the *Modified* *Julian* *Day*
63 # *Number*, which takes November 17, 1858 as its initial day.
64 # This is abbreviated as +mjd+ in the Date class.  There
65 # is also an *Astronomical* *Modified* *Julian* *Day* *Number*,
66 # which is in UTC and includes fractional days.  This is
67 # abbreviated as +amjd+ in the Date class.  Like the Modified
68 # Julian Day Number (and unlike the Astronomical Julian
69 # Day Number), it counts from midnight.
71 # Alternative calendars such as the Chinese Lunar Calendar,
72 # the Islamic Calendar, or the French Revolutionary Calendar
73 # are not supported by the Date class; nor are calendars that
74 # are based on an Era different from the Common Era, such as
75 # the Japanese Imperial Calendar or the Republic of China
76 # Calendar.
78 # === Calendar Reform
80 # The standard civil year is 365 days long.  However, the
81 # solar year is fractionally longer than this.  To account
82 # for this, a *leap* *year* is occasionally inserted.  This
83 # is a year with 366 days, the extra day falling on February 29.
84 # In the early days of the civil calendar, every fourth
85 # year without exception was a leap year.  This way of
86 # reckoning leap years is the *Julian* *Calendar*.
88 # However, the solar year is marginally shorter than 365 1/4
89 # days, and so the *Julian* *Calendar* gradually ran slow
90 # over the centuries.  To correct this, every 100th year
91 # (but not every 400th year) was excluded as a leap year.
92 # This way of reckoning leap years, which we use today, is
93 # the *Gregorian* *Calendar*.
95 # The Gregorian Calendar was introduced at different times
96 # in different regions.  The day on which it was introduced
97 # for a particular region is the *Day* *of* *Calendar*
98 # *Reform* for that region.  This is abbreviated as +sg+
99 # (for Start of Gregorian calendar) in the Date class.
101 # Two such days are of particular
102 # significance.  The first is October 15, 1582, which was
103 # the Day of Calendar Reform for Italy and most Catholic
104 # countries.  The second is September 14, 1752, which was
105 # the Day of Calendar Reform for England and its colonies
106 # (including what is now the United States).  These two
107 # dates are available as the constants Date::ITALY and
108 # Date::ENGLAND, respectively.  (By comparison, Germany and
109 # Holland, less Catholic than Italy but less stubborn than
110 # England, changed over in 1698; Sweden in 1753; Russia not
111 # till 1918, after the Revolution; and Greece in 1923.  Many
112 # Orthodox churches still use the Julian Calendar.  A complete
113 # list of Days of Calendar Reform can be found at
114 # http://www.polysyllabic.com/GregConv.html.)
116 # Switching from the Julian to the Gregorian calendar
117 # involved skipping a number of days to make up for the
118 # accumulated lag, and the later the switch was (or is)
119 # done, the more days need to be skipped.  So in 1582 in Italy,
120 # 4th October was followed by 15th October, skipping 10 days; in 1752
121 # in England, 2nd September was followed by 14th September, skipping
122 # 11 days; and if I decided to switch from Julian to Gregorian
123 # Calendar this midnight, I would go from 27th July 2003 (Julian)
124 # today to 10th August 2003 (Gregorian) tomorrow, skipping
125 # 13 days.  The Date class is aware of this gap, and a supposed
126 # date that would fall in the middle of it is regarded as invalid.
128 # The Day of Calendar Reform is relevant to all date representations
129 # involving years.  It is not relevant to the Julian Day Numbers,
130 # except for converting between them and year-based representations.
132 # In the Date and DateTime classes, the Day of Calendar Reform or
133 # +sg+ can be specified a number of ways.  First, it can be as
134 # the Julian Day Number of the Day of Calendar Reform.  Second,
135 # it can be using the constants Date::ITALY or Date::ENGLAND; these
136 # are in fact the Julian Day Numbers of the Day of Calendar Reform
137 # of the respective regions.  Third, it can be as the constant
138 # Date::JULIAN, which means to always use the Julian Calendar.
139 # Finally, it can be as the constant Date::GREGORIAN, which means
140 # to always use the Gregorian Calendar.
142 # Note: in the Julian Calendar, New Years Day was March 25.  The
143 # Date class does not follow this convention.
145 # === Time Zones
147 # DateTime objects support a simple representation
148 # of time zones.  Time zones are represented as an offset
149 # from UTC, as a fraction of a day.  This offset is the
150 # how much local time is later (or earlier) than UTC.
151 # UTC offset 0 is centred on England (also known as GMT).
152 # As you travel east, the offset increases until you
153 # reach the dateline in the middle of the Pacific Ocean;
154 # as you travel west, the offset decreases.  This offset
155 # is abbreviated as +of+ in the Date class.
157 # This simple representation of time zones does not take
158 # into account the common practice of Daylight Savings
159 # Time or Summer Time.
161 # Most DateTime methods return the date and the
162 # time in local time.  The two exceptions are
163 # #ajd() and #amjd(), which return the date and time
164 # in UTC time, including fractional days.
166 # The Date class does not support time zone offsets, in that
167 # there is no way to create a Date object with a time zone.
168 # However, methods of the Date class when used by a
169 # DateTime instance will use the time zone offset of this
170 # instance.
172 # == Examples of use
174 # === Print out the date of every Sunday between two dates.
176 #     def print_sundays(d1, d2)
177 #         d1 +=1 while (d1.wday != 0)
178 #         d1.step(d2, 7) do |date|
179 #             puts "#{Date::MONTHNAMES[date.mon]} #{date.day}"
180 #         end
181 #     end
183 #     print_sundays(Date::civil(2003, 4, 8), Date::civil(2003, 5, 23))
185 # === Calculate how many seconds to go till midnight on New Year's Day.
187 #     def secs_to_new_year(now = DateTime::now())
188 #         new_year = DateTime.new(now.year + 1, 1, 1)
189 #         dif = new_year - now
190 #         hours, mins, secs, ignore_fractions = Date::day_fraction_to_time(dif)
191 #         return hours * 60 * 60 + mins * 60 + secs
192 #     end
194 #     puts secs_to_new_year()
196 require 'rational'
197 require 'date/format'
199 # Class representing a date.
201 # See the documentation to the file date.rb for an overview.
203 # Internally, the date is represented as an Astronomical
204 # Julian Day Number, +ajd+.  The Day of Calendar Reform, +sg+, is
205 # also stored, for conversions to other date formats.  (There
206 # is also an +of+ field for a time zone offset, but this
207 # is only for the use of the DateTime subclass.)
209 # A new Date object is created using one of the object creation
210 # class methods named after the corresponding date format, and the
211 # arguments appropriate to that date format; for instance,
212 # Date::civil() (aliased to Date::new()) with year, month,
213 # and day-of-month, or Date::ordinal() with year and day-of-year.
214 # All of these object creation class methods also take the
215 # Day of Calendar Reform as an optional argument.
217 # Date objects are immutable once created.
219 # Once a Date has been created, date values
220 # can be retrieved for the different date formats supported
221 # using instance methods.  For instance, #mon() gives the
222 # Civil month, #cwday() gives the Commercial day of the week,
223 # and #yday() gives the Ordinal day of the year.  Date values
224 # can be retrieved in any format, regardless of what format
225 # was used to create the Date instance.
227 # The Date class includes the Comparable module, allowing
228 # date objects to be compared and sorted, ranges of dates
229 # to be created, and so forth.
230 class Date
232   include Comparable
234   # Full month names, in English.  Months count from 1 to 12; a
235   # month's numerical representation indexed into this array
236   # gives the name of that month (hence the first element is nil).
237   MONTHNAMES = [nil] + %w(January February March April May June July
238                           August September October November December)
240   # Full names of days of the week, in English.  Days of the week
241   # count from 0 to 6 (except in the commercial week); a day's numerical
242   # representation indexed into this array gives the name of that day.
243   DAYNAMES = %w(Sunday Monday Tuesday Wednesday Thursday Friday Saturday)
245   # Abbreviated month names, in English.
246   ABBR_MONTHNAMES = [nil] + %w(Jan Feb Mar Apr May Jun
247                                Jul Aug Sep Oct Nov Dec)
249   # Abbreviated day names, in English.
250   ABBR_DAYNAMES = %w(Sun Mon Tue Wed Thu Fri Sat)
252   [MONTHNAMES, DAYNAMES, ABBR_MONTHNAMES, ABBR_DAYNAMES].each do |xs|
253     xs.each{|x| x.freeze unless x.nil?}.freeze
254   end
256   class Infinity < Numeric # :nodoc:
258     include Comparable
260     def initialize(d=1) @d = d <=> 0 end
262     def d() @d end
264     protected :d
266     def zero? () false end
267     def finite? () false end
268     def infinite? () d.nonzero? end
269     def nan? () d.zero? end
271     def abs() self.class.new end
273     def -@ () self.class.new(-d) end
274     def +@ () self.class.new(+d) end
276     def <=> (other)
277       case other
278       when Infinity; return d <=> other.d
279       when Numeric; return d
280       else
281         begin
282           l, r = other.coerce(self)
283           return l <=> r
284         rescue NoMethodError
285         end
286       end
287       nil
288     end
290     def coerce(other)
291       case other
292       when Numeric; return -d, d
293       else
294         super
295       end
296     end
298   end
300   # The Julian Day Number of the Day of Calendar Reform for Italy
301   # and the Catholic countries.
302   ITALY     = 2299161 # 1582-10-15
304   # The Julian Day Number of the Day of Calendar Reform for England
305   # and her Colonies.
306   ENGLAND   = 2361222 # 1752-09-14
308   # A constant used to indicate that a Date should always use the
309   # Julian calendar.
310   JULIAN    =  Infinity.new
312   # A constant used to indicate that a Date should always use the
313   # Gregorian calendar.
314   GREGORIAN = -Infinity.new
316   HALF_DAYS_IN_DAY       = Rational(1, 2) # :nodoc:
317   HOURS_IN_DAY           = Rational(1, 24) # :nodoc:
318   MINUTES_IN_DAY         = Rational(1, 1440) # :nodoc:
319   SECONDS_IN_DAY         = Rational(1, 86400) # :nodoc:
320   MILLISECONDS_IN_DAY    = Rational(1, 86400*10**3) # :nodoc:
321   NANOSECONDS_IN_DAY     = Rational(1, 86400*10**9) # :nodoc:
322   MILLISECONDS_IN_SECOND = Rational(1, 10**3) # :nodoc:
323   NANOSECONDS_IN_SECOND  = Rational(1, 10**9) # :nodoc:
325   MJD_EPOCH_IN_AJD       = Rational(4800001, 2) # 1858-11-17 # :nodoc:
326   UNIX_EPOCH_IN_AJD      = Rational(4881175, 2) # 1970-01-01 # :nodoc:
327   MJD_EPOCH_IN_CJD       = 2400001 # :nodoc:
328   UNIX_EPOCH_IN_CJD      = 2440588 # :nodoc:
329   LD_EPOCH_IN_CJD        = 2299160 # :nodoc:
331   t = Module.new do
333     private
335     def find_fdoy(y, sg) # :nodoc:
336       j = nil
337       1.upto(31) do |d|
338         break if j = _valid_civil?(y, 1, d, sg)
339       end
340       j
341     end
343     def find_ldoy(y, sg) # :nodoc:
344       j = nil
345       31.downto(1) do |d|
346         break if j = _valid_civil?(y, 12, d, sg)
347       end
348       j
349     end
351     def find_fdom(y, m, sg) # :nodoc:
352       j = nil
353       1.upto(31) do |d|
354         break if j = _valid_civil?(y, m, d, sg)
355       end
356       j
357     end
359     def find_ldom(y, m, sg) # :nodoc:
360       j = nil
361       31.downto(1) do |d|
362         break if j = _valid_civil?(y, m, d, sg)
363       end
364       j
365     end
367     # Convert an Ordinal Date to a Julian Day Number.
368     #
369     # +y+ and +d+ are the year and day-of-year to convert.
370     # +sg+ specifies the Day of Calendar Reform.
371     #
372     # Returns the corresponding Julian Day Number.
373     def ordinal_to_jd(y, d, sg=GREGORIAN) # :nodoc:
374       find_fdoy(y, sg) + d - 1
375     end
377     # Convert a Julian Day Number to an Ordinal Date.
378     #
379     # +jd+ is the Julian Day Number to convert.
380     # +sg+ specifies the Day of Calendar Reform.
381     #
382     # Returns the corresponding Ordinal Date as
383     # [year, day_of_year]
384     def jd_to_ordinal(jd, sg=GREGORIAN) # :nodoc:
385       y = jd_to_civil(jd, sg)[0]
386       j = find_fdoy(y, sg)
387       doy = jd - j + 1
388       return y, doy
389     end
391     # Convert a Civil Date to a Julian Day Number.
392     # +y+, +m+, and +d+ are the year, month, and day of the
393     # month.  +sg+ specifies the Day of Calendar Reform.
394     #
395     # Returns the corresponding Julian Day Number.
396     def civil_to_jd(y, m, d, sg=GREGORIAN) # :nodoc:
397       if m <= 2
398         y -= 1
399         m += 12
400       end
401       a = (y / 100.0).floor
402       b = 2 - a + (a / 4.0).floor
403       jd = (365.25 * (y + 4716)).floor +
404         (30.6001 * (m + 1)).floor +
405         d + b - 1524
406       if jd < sg
407         jd -= b
408       end
409       jd
410     end
412     # Convert a Julian Day Number to a Civil Date.  +jd+ is
413     # the Julian Day Number. +sg+ specifies the Day of
414     # Calendar Reform.
415     #
416     # Returns the corresponding [year, month, day_of_month]
417     # as a three-element array.
418     def jd_to_civil(jd, sg=GREGORIAN) # :nodoc:
419       if jd < sg
420         a = jd
421       else
422         x = ((jd - 1867216.25) / 36524.25).floor
423         a = jd + 1 + x - (x / 4.0).floor
424       end
425       b = a + 1524
426       c = ((b - 122.1) / 365.25).floor
427       d = (365.25 * c).floor
428       e = ((b - d) / 30.6001).floor
429       dom = b - d - (30.6001 * e).floor
430       if e <= 13
431         m = e - 1
432         y = c - 4716
433       else
434         m = e - 13
435         y = c - 4715
436       end
437       return y, m, dom
438     end
440     # Convert a Commercial Date to a Julian Day Number.
441     #
442     # +y+, +w+, and +d+ are the (commercial) year, week of the year,
443     # and day of the week of the Commercial Date to convert.
444     # +sg+ specifies the Day of Calendar Reform.
445     def commercial_to_jd(y, w, d, sg=GREGORIAN) # :nodoc:
446       j = find_fdoy(y, sg) + 3
447       (j - (((j - 1) + 1) % 7)) +
448         7 * (w - 1) +
449         (d - 1)
450     end
452     # Convert a Julian Day Number to a Commercial Date
453     #
454     # +jd+ is the Julian Day Number to convert.
455     # +sg+ specifies the Day of Calendar Reform.
456     #
457     # Returns the corresponding Commercial Date as
458     # [commercial_year, week_of_year, day_of_week]
459     def jd_to_commercial(jd, sg=GREGORIAN) # :nodoc:
460       a = jd_to_civil(jd - 3, sg)[0]
461       y = if jd >= commercial_to_jd(a + 1, 1, 1, sg) then a + 1 else a end
462       w = 1 + ((jd - commercial_to_jd(y, 1, 1, sg)) / 7).floor
463       d = (jd + 1) % 7
464       d = 7 if d == 0
465       return y, w, d
466     end
468     def weeknum_to_jd(y, w, d, f=0, sg=GREGORIAN) # :nodoc:
469       a = find_fdoy(y, sg) + 6
470       (a - ((a - f) + 1) % 7 - 7) + 7 * w + d
471     end
473     def jd_to_weeknum(jd, f=0, sg=GREGORIAN) # :nodoc:
474       y, m, d = jd_to_civil(jd, sg)
475       a = find_fdoy(y, sg) + 6
476       w, d = (jd - (a - ((a - f) + 1) % 7) + 7).divmod(7)
477       return y, w, d
478     end
480     def nth_kday_to_jd(y, m, n, k, sg=GREGORIAN) # :nodoc:
481       j = if n > 0
482             find_fdom(y, m, sg) - 1
483           else
484             find_ldom(y, m, sg) + 7
485           end
486       (j - (((j - k) + 1) % 7)) + 7 * n
487     end
489     def jd_to_nth_kday(jd, sg=GREGORIAN) # :nodoc:
490       y, m, d = jd_to_civil(jd, sg)
491       j = find_fdom(y, m, sg)
492       return y, m, ((jd - j) / 7).floor + 1, jd_to_wday(jd)
493     end
495     # Convert an Astronomical Julian Day Number to a (civil) Julian
496     # Day Number.
497     #
498     # +ajd+ is the Astronomical Julian Day Number to convert.
499     # +of+ is the offset from UTC as a fraction of a day (defaults to 0).
500     #
501     # Returns the (civil) Julian Day Number as [day_number,
502     # fraction] where +fraction+ is always 1/2.
503     def ajd_to_jd(ajd, of=0) (ajd + of + HALF_DAYS_IN_DAY).divmod(1) end # :nodoc:
505     # Convert a (civil) Julian Day Number to an Astronomical Julian
506     # Day Number.
507     #
508     # +jd+ is the Julian Day Number to convert, and +fr+ is a
509     # fractional day.
510     # +of+ is the offset from UTC as a fraction of a day (defaults to 0).
511     #
512     # Returns the Astronomical Julian Day Number as a single
513     # numeric value.
514     def jd_to_ajd(jd, fr, of=0) jd + fr - of - HALF_DAYS_IN_DAY end # :nodoc:
516     # Convert a fractional day +fr+ to [hours, minutes, seconds,
517     # fraction_of_a_second]
518     def day_fraction_to_time(fr) # :nodoc:
519       ss,  fr = fr.divmod(SECONDS_IN_DAY) # 4p
520       h,   ss = ss.divmod(3600)
521       min, s  = ss.divmod(60)
522       return h, min, s, fr * 86400
523     end
525     # Convert an +h+ hour, +min+ minutes, +s+ seconds period
526     # to a fractional day.
527     begin
528       Rational(Rational(1, 2), 2) # a challenge
530       def time_to_day_fraction(h, min, s)
531         Rational(h * 3600 + min * 60 + s, 86400) # 4p
532       end
533     rescue
534       def time_to_day_fraction(h, min, s)
535         if Integer === h && Integer === min && Integer === s
536           Rational(h * 3600 + min * 60 + s, 86400) # 4p
537         else
538           (h * 3600 + min * 60 + s).to_r/86400 # 4p
539         end
540       end
541     end
543     # Convert an Astronomical Modified Julian Day Number to an
544     # Astronomical Julian Day Number.
545     def amjd_to_ajd(amjd) amjd + MJD_EPOCH_IN_AJD end # :nodoc:
547     # Convert an Astronomical Julian Day Number to an
548     # Astronomical Modified Julian Day Number.
549     def ajd_to_amjd(ajd) ajd - MJD_EPOCH_IN_AJD end # :nodoc:
551     # Convert a Modified Julian Day Number to a Julian
552     # Day Number.
553     def mjd_to_jd(mjd) mjd + MJD_EPOCH_IN_CJD end # :nodoc:
555     # Convert a Julian Day Number to a Modified Julian Day
556     # Number.
557     def jd_to_mjd(jd) jd - MJD_EPOCH_IN_CJD end # :nodoc:
559     # Convert a count of the number of days since the adoption
560     # of the Gregorian Calendar (in Italy) to a Julian Day Number.
561     def ld_to_jd(ld) ld +  LD_EPOCH_IN_CJD end # :nodoc:
563     # Convert a Julian Day Number to the number of days since
564     # the adoption of the Gregorian Calendar (in Italy).
565     def jd_to_ld(jd) jd -  LD_EPOCH_IN_CJD end # :nodoc:
567     # Convert a Julian Day Number to the day of the week.
568     #
569     # Sunday is day-of-week 0; Saturday is day-of-week 6.
570     def jd_to_wday(jd) (jd + 1) % 7 end # :nodoc:
572     # Is +jd+ a valid Julian Day Number?
573     #
574     # If it is, returns it.  In fact, any value is treated as a valid
575     # Julian Day Number.
576     def _valid_jd? (jd, sg=GREGORIAN) jd end # :nodoc:
578     # Do the year +y+ and day-of-year +d+ make a valid Ordinal Date?
579     # Returns the corresponding Julian Day Number if they do, or
580     # nil if they don't.
581     #
582     # +d+ can be a negative number, in which case it counts backwards
583     # from the end of the year (-1 being the last day of the year).
584     # No year wraparound is performed, however, so valid values of
585     # +d+ are -365 .. -1, 1 .. 365 on a non-leap-year,
586     # -366 .. -1, 1 .. 366 on a leap year.
587     # A date falling in the period skipped in the Day of Calendar Reform
588     # adjustment is not valid.
589     #
590     # +sg+ specifies the Day of Calendar Reform.
591     def _valid_ordinal? (y, d, sg=GREGORIAN) # :nodoc:
592       if d < 0
593         j = find_ldoy(y, sg)
594         ny, nd = jd_to_ordinal(j + d + 1, sg)
595         return unless ny == y
596         d = nd
597       end
598       jd = ordinal_to_jd(y, d, sg)
599       return unless [y, d] == jd_to_ordinal(jd, sg)
600       jd
601     end
603     # Do year +y+, month +m+, and day-of-month +d+ make a
604     # valid Civil Date?  Returns the corresponding Julian
605     # Day Number if they do, nil if they don't.
606     #
607     # +m+ and +d+ can be negative, in which case they count
608     # backwards from the end of the year and the end of the
609     # month respectively.  No wraparound is performed, however,
610     # and invalid values cause an ArgumentError to be raised.
611     # A date falling in the period skipped in the Day of Calendar
612     # Reform adjustment is not valid.
613     #
614     # +sg+ specifies the Day of Calendar Reform.
615     def _valid_civil? (y, m, d, sg=GREGORIAN) # :nodoc:
616       if m < 0
617         m += 13
618       end
619       if d < 0
620         j = find_ldom(y, m, sg)
621         ny, nm, nd = jd_to_civil(j + d + 1, sg)
622         return unless [ny, nm] == [y, m]
623         d = nd
624       end
625       jd = civil_to_jd(y, m, d, sg)
626       return unless [y, m, d] == jd_to_civil(jd, sg)
627       jd
628     end
630     # Do year +y+, week-of-year +w+, and day-of-week +d+ make a
631     # valid Commercial Date?  Returns the corresponding Julian
632     # Day Number if they do, nil if they don't.
633     #
634     # Monday is day-of-week 1; Sunday is day-of-week 7.
635     #
636     # +w+ and +d+ can be negative, in which case they count
637     # backwards from the end of the year and the end of the
638     # week respectively.  No wraparound is performed, however,
639     # and invalid values cause an ArgumentError to be raised.
640     # A date falling in the period skipped in the Day of Calendar
641     # Reform adjustment is not valid.
642     #
643     # +sg+ specifies the Day of Calendar Reform.
644     def _valid_commercial? (y, w, d, sg=GREGORIAN) # :nodoc:
645       if d < 0
646         d += 8
647       end
648       if w < 0
649         ny, nw, nd =
650           jd_to_commercial(commercial_to_jd(y + 1, 1, 1, sg) + w * 7, sg)
651         return unless ny == y
652         w = nw
653       end
654       jd = commercial_to_jd(y, w, d, sg)
655       return unless [y, w, d] == jd_to_commercial(jd, sg)
656       jd
657     end
659     def _valid_weeknum? (y, w, d, f, sg=GREGORIAN) # :nodoc:
660       if d < 0
661         d += 7
662       end
663       if w < 0
664         ny, nw, nd, nf =
665           jd_to_weeknum(weeknum_to_jd(y + 1, 1, f, f, sg) + w * 7, f, sg)
666         return unless ny == y
667         w = nw
668       end
669       jd = weeknum_to_jd(y, w, d, f, sg)
670       return unless [y, w, d] == jd_to_weeknum(jd, f, sg)
671       jd
672     end
674     def _valid_nth_kday? (y, m, n, k, sg=GREGORIAN) # :nodoc:
675       if k < 0
676         k += 7
677       end
678       if n < 0
679         ny, nm = (y * 12 + m).divmod(12)
680         nm,    = (nm + 1)    .divmod(1)
681         ny, nm, nn, nk =
682           jd_to_nth_kday(nth_kday_to_jd(ny, nm, 1, k, sg) + n * 7, sg)
683         return unless [ny, nm] == [y, m]
684         n = nn
685       end
686       jd = nth_kday_to_jd(y, m, n, k, sg)
687       return unless [y, m, n, k] == jd_to_nth_kday(jd, sg)
688       jd
689     end
691     # Do hour +h+, minute +min+, and second +s+ constitute a valid time?
692     #
693     # If they do, returns their value as a fraction of a day.  If not,
694     # returns nil.
695     #
696     # The 24-hour clock is used.  Negative values of +h+, +min+, and
697     # +sec+ are treating as counting backwards from the end of the
698     # next larger unit (e.g. a +min+ of -2 is treated as 58).  No
699     # wraparound is performed.
700     def _valid_time? (h, min, s) # :nodoc:
701       h   += 24 if h   < 0
702       min += 60 if min < 0
703       s   += 60 if s   < 0
704       return unless ((0...24) === h &&
705                      (0...60) === min &&
706                      (0...60) === s) ||
707                      (24 == h &&
708                        0 == min &&
709                        0 == s)
710       time_to_day_fraction(h, min, s)
711     end
713   end
715   extend  t
716   include t
718   # Is a year a leap year in the Julian calendar?
719   #
720   # All years divisible by 4 are leap years in the Julian calendar.
721   def self.julian_leap? (y) y % 4 == 0 end
723   # Is a year a leap year in the Gregorian calendar?
724   #
725   # All years divisible by 4 are leap years in the Gregorian calendar,
726   # except for years divisible by 100 and not by 400.
727   def self.gregorian_leap? (y) y % 4 == 0 && y % 100 != 0 || y % 400 == 0 end
729   class << self; alias_method :leap?, :gregorian_leap? end
730   class << self; alias_method :new!, :new end
732   def self.valid_jd? (jd, sg=ITALY)
733     !!_valid_jd?(jd, sg)
734   end
736   def self.valid_ordinal? (y, d, sg=ITALY)
737     !!_valid_ordinal?(y, d, sg)
738   end
740   def self.valid_civil? (y, m, d, sg=ITALY)
741     !!_valid_civil?(y, m, d, sg)
742   end
744   class << self; alias_method :valid_date?, :valid_civil? end
746   def self.valid_commercial? (y, w, d, sg=ITALY)
747     !!_valid_commercial?(y, w, d, sg)
748   end
750   def self.valid_weeknum? (y, w, d, f, sg=ITALY) # :nodoc:
751     !!_valid_weeknum?(y, w, d, f, sg)
752   end
754   private_class_method :valid_weeknum?
756   def self.valid_nth_kday? (y, m, n, k, sg=ITALY) # :nodoc:
757     !!_valid_nth_kday?(y, m, n, k, sg)
758   end
760   private_class_method :valid_nth_kday?
762   def self.valid_time? (h, min, s) # :nodoc:
763     !!_valid_time?(h, min, s)
764   end
766   private_class_method :valid_time?
768   # Create a new Date object from a Julian Day Number.
769   #
770   # +jd+ is the Julian Day Number; if not specified, it defaults to
771   # 0.
772   # +sg+ specifies the Day of Calendar Reform.
773   def self.jd(jd=0, sg=ITALY)
774     jd = _valid_jd?(jd, sg)
775     new!(jd_to_ajd(jd, 0, 0), 0, sg)
776   end
778   # Create a new Date object from an Ordinal Date, specified
779   # by year +y+ and day-of-year +d+. +d+ can be negative,
780   # in which it counts backwards from the end of the year.
781   # No year wraparound is performed, however.  An invalid
782   # value for +d+ results in an ArgumentError being raised.
783   #
784   # +y+ defaults to -4712, and +d+ to 1; this is Julian Day
785   # Number day 0.
786   #
787   # +sg+ specifies the Day of Calendar Reform.
788   def self.ordinal(y=-4712, d=1, sg=ITALY)
789     unless jd = _valid_ordinal?(y, d, sg)
790       raise ArgumentError, 'invalid date'
791     end
792     new!(jd_to_ajd(jd, 0, 0), 0, sg)
793   end
795   # Create a new Date object for the Civil Date specified by
796   # year +y+, month +m+, and day-of-month +d+.
797   #
798   # +m+ and +d+ can be negative, in which case they count
799   # backwards from the end of the year and the end of the
800   # month respectively.  No wraparound is performed, however,
801   # and invalid values cause an ArgumentError to be raised.
802   # can be negative
803   #
804   # +y+ defaults to -4712, +m+ to 1, and +d+ to 1; this is
805   # Julian Day Number day 0.
806   #
807   # +sg+ specifies the Day of Calendar Reform.
808   def self.civil(y=-4712, m=1, d=1, sg=ITALY)
809     unless jd = _valid_civil?(y, m, d, sg)
810       raise ArgumentError, 'invalid date'
811     end
812     new!(jd_to_ajd(jd, 0, 0), 0, sg)
813   end
815   class << self; alias_method :new, :civil end
817   # Create a new Date object for the Commercial Date specified by
818   # year +y+, week-of-year +w+, and day-of-week +d+.
819   #
820   # Monday is day-of-week 1; Sunday is day-of-week 7.
821   #
822   # +w+ and +d+ can be negative, in which case they count
823   # backwards from the end of the year and the end of the
824   # week respectively.  No wraparound is performed, however,
825   # and invalid values cause an ArgumentError to be raised.
826   #
827   # +y+ defaults to -4712, +w+ to 1, and +d+ to 1; this is
828   # Julian Day Number day 0.
829   #
830   # +sg+ specifies the Day of Calendar Reform.
831   def self.commercial(y=-4712, w=1, d=1, sg=ITALY)
832     unless jd = _valid_commercial?(y, w, d, sg)
833       raise ArgumentError, 'invalid date'
834     end
835     new!(jd_to_ajd(jd, 0, 0), 0, sg)
836   end
838   def self.weeknum(y=-4712, w=0, d=1, f=0, sg=ITALY)
839     unless jd = _valid_weeknum?(y, w, d, f, sg)
840       raise ArgumentError, 'invalid date'
841     end
842     new!(jd_to_ajd(jd, 0, 0), 0, sg)
843   end
845   private_class_method :weeknum
847   def self.nth_kday(y=-4712, m=1, n=1, k=1, sg=ITALY)
848     unless jd = _valid_nth_kday?(y, m, n, k, sg)
849       raise ArgumentError, 'invalid date'
850     end
851     new!(jd_to_ajd(jd, 0, 0), 0, sg)
852   end
854   private_class_method :nth_kday
856   def self.rewrite_frags(elem) # :nodoc:
857     elem ||= {}
858     if seconds = elem[:seconds]
859       d,   fr = seconds.divmod(86400)
860       h,   fr = fr.divmod(3600)
861       min, fr = fr.divmod(60)
862       s,   fr = fr.divmod(1)
863       elem[:jd] = UNIX_EPOCH_IN_CJD + d
864       elem[:hour] = h
865       elem[:min] = min
866       elem[:sec] = s
867       elem[:sec_fraction] = fr
868       elem.delete(:seconds)
869       elem.delete(:offset)
870     end
871     elem
872   end
874   private_class_method :rewrite_frags
876   def self.complete_frags(elem) # :nodoc:
877     i = 0
878     g = [[:time, [:hour, :min, :sec]],
879          [nil, [:jd]],
880          [:ordinal, [:year, :yday, :hour, :min, :sec]],
881          [:civil, [:year, :mon, :mday, :hour, :min, :sec]],
882          [:commercial, [:cwyear, :cweek, :cwday, :hour, :min, :sec]],
883          [:wday, [:wday, :hour, :min, :sec]],
884          [:wnum0, [:year, :wnum0, :wday, :hour, :min, :sec]],
885          [:wnum1, [:year, :wnum1, :wday, :hour, :min, :sec]],
886          [nil, [:cwyear, :cweek, :wday, :hour, :min, :sec]],
887          [nil, [:year, :wnum0, :cwday, :hour, :min, :sec]],
888          [nil, [:year, :wnum1, :cwday, :hour, :min, :sec]]].
889       collect{|k, a| e = elem.values_at(*a).compact; [k, a, e]}.
890       select{|k, a, e| e.size > 0}.
891       sort_by{|k, a, e| [e.size, i -= 1]}.last
893     d = nil
895     if g && g[0] && (g[1].size - g[2].size) != 0
896       d ||= Date.today
898       case g[0]
899       when :ordinal
900         elem[:year] ||= d.year
901         elem[:yday] ||= 1
902       when :civil
903         g[1].each do |e|
904           break if elem[e]
905           elem[e] = d.__send__(e)
906         end
907         elem[:mon]  ||= 1
908         elem[:mday] ||= 1
909       when :commercial
910         g[1].each do |e|
911           break if elem[e]
912           elem[e] = d.__send__(e)
913         end
914         elem[:cweek] ||= 1
915         elem[:cwday] ||= 1
916       when :wday
917         elem[:jd] ||= (d - d.wday + elem[:wday]).jd
918       when :wnum0
919         g[1].each do |e|
920           break if elem[e]
921           elem[e] = d.__send__(e)
922         end
923         elem[:wnum0] ||= 0
924         elem[:wday]  ||= 0
925       when :wnum1
926         g[1].each do |e|
927           break if elem[e]
928           elem[e] = d.__send__(e)
929         end
930         elem[:wnum1] ||= 0
931         elem[:wday]  ||= 0
932       end
933     end
935     if g && g[0] == :time
936       if self <= DateTime
937         d ||= Date.today
938         elem[:jd] ||= d.jd
939       end
940     end
942     elem[:hour] ||= 0
943     elem[:min]  ||= 0
944     elem[:sec]  ||= 0
945     elem[:sec] = [elem[:sec], 59].min
947     elem
948   end
950   private_class_method :complete_frags
952   def self.valid_date_frags?(elem, sg) # :nodoc:
953     catch :jd do
954       a = elem.values_at(:jd)
955       if a.all?
956         if jd = _valid_jd?(*(a << sg))
957           throw :jd, jd
958         end
959       end
961       a = elem.values_at(:year, :yday)
962       if a.all?
963         if jd = _valid_ordinal?(*(a << sg))
964           throw :jd, jd
965         end
966       end
968       a = elem.values_at(:year, :mon, :mday)
969       if a.all?
970         if jd = _valid_civil?(*(a << sg))
971           throw :jd, jd
972         end
973       end
975       a = elem.values_at(:cwyear, :cweek, :cwday)
976       if a[2].nil? && elem[:wday]
977         a[2] = elem[:wday].nonzero? || 7
978       end
979       if a.all?
980         if jd = _valid_commercial?(*(a << sg))
981           throw :jd, jd
982         end
983       end
985       a = elem.values_at(:year, :wnum0, :wday)
986       if a[2].nil? && elem[:cwday]
987         a[2] = elem[:cwday] % 7
988       end
989       if a.all?
990         if jd = _valid_weeknum?(*(a << 0 << sg))
991           throw :jd, jd
992         end
993       end
995       a = elem.values_at(:year, :wnum1, :wday)
996       if a[2]
997         a[2] = (a[2] - 1) % 7
998       end
999       if a[2].nil? && elem[:cwday]
1000         a[2] = (elem[:cwday] - 1) % 7
1001       end
1002       if a.all?
1003         if jd = _valid_weeknum?(*(a << 1 << sg))
1004           throw :jd, jd
1005         end
1006       end
1007     end
1008   end
1010   private_class_method :valid_date_frags?
1012   def self.valid_time_frags? (elem) # :nodoc:
1013     h, min, s = elem.values_at(:hour, :min, :sec)
1014     _valid_time?(h, min, s)
1015   end
1017   private_class_method :valid_time_frags?
1019   def self.new_by_frags(elem, sg) # :nodoc:
1020     elem = rewrite_frags(elem)
1021     elem = complete_frags(elem)
1022     unless jd = valid_date_frags?(elem, sg)
1023       raise ArgumentError, 'invalid date'
1024     end
1025     new!(jd_to_ajd(jd, 0, 0), 0, sg)
1026   end
1028   private_class_method :new_by_frags
1030   # Create a new Date object by parsing from a String
1031   # according to a specified format.
1032   #
1033   # +str+ is a String holding a date representation.
1034   # +fmt+ is the format that the date is in.  See
1035   # date/format.rb for details on supported formats.
1036   #
1037   # The default +str+ is '-4712-01-01', and the default
1038   # +fmt+ is '%F', which means Year-Month-Day_of_Month.
1039   # This gives Julian Day Number day 0.
1040   #
1041   # +sg+ specifies the Day of Calendar Reform.
1042   #
1043   # An ArgumentError will be raised if +str+ cannot be
1044   # parsed.
1045   def self.strptime(str='-4712-01-01', fmt='%F', sg=ITALY)
1046     elem = _strptime(str, fmt)
1047     new_by_frags(elem, sg)
1048   end
1050   # Create a new Date object by parsing from a String,
1051   # without specifying the format.
1052   #
1053   # +str+ is a String holding a date representation.
1054   # +comp+ specifies whether to interpret 2-digit years
1055   # as 19XX (>= 69) or 20XX (< 69); the default is not to.
1056   # The method will attempt to parse a date from the String
1057   # using various heuristics; see #_parse in date/format.rb
1058   # for more details.  If parsing fails, an ArgumentError
1059   # will be raised.
1060   #
1061   # The default +str+ is '-4712-01-01'; this is Julian
1062   # Day Number day 0.
1063   #
1064   # +sg+ specifies the Day of Calendar Reform.
1065   def self.parse(str='-4712-01-01', comp=true, sg=ITALY)
1066     elem = _parse(str, comp)
1067     new_by_frags(elem, sg)
1068   end
1070   def self.iso8601(str='-4712-01-01', sg=ITALY) # :nodoc:
1071     elem = _iso8601(str)
1072     new_by_frags(elem, sg)
1073   end
1075   def self.rfc3339(str='-4712-01-01T00:00:00+00:00', sg=ITALY) # :nodoc:
1076     elem = _rfc3339(str)
1077     new_by_frags(elem, sg)
1078   end
1080   def self.xmlschema(str='-4712-01-01', sg=ITALY) # :nodoc:
1081     elem = _xmlschema(str)
1082     new_by_frags(elem, sg)
1083   end
1085   def self.rfc2822(str='Mon, 1 Jan -4712 00:00:00 +0000', sg=ITALY) # :nodoc:
1086     elem = _rfc2822(str)
1087     new_by_frags(elem, sg)
1088   end
1090   class << self; alias_method :rfc822, :rfc2822 end
1092   def self.httpdate(str='Mon, 01 Jan -4712 00:00:00 GMT', sg=ITALY) # :nodoc:
1093     elem = _httpdate(str)
1094     new_by_frags(elem, sg)
1095   end
1097   def self.jisx0301(str='-4712-01-01', sg=ITALY) # :nodoc:
1098     elem = _jisx0301(str)
1099     new_by_frags(elem, sg)
1100   end
1102   class << self
1104     def once(*ids) # :nodoc: -- restricted
1105       for id in ids
1106         module_eval <<-"end;"
1107           alias_method :__#{id.object_id}__, :#{id.to_s}
1108           private :__#{id.object_id}__
1109           def #{id.to_s}(*args)
1110             @__ca__[#{id.object_id}] ||= __#{id.object_id}__(*args)
1111           end
1112         end;
1113       end
1114     end
1116     private :once
1118   end
1120   # *NOTE* this is the documentation for the method new!().  If
1121   # you are reading this as the documentation for new(), that is
1122   # because rdoc doesn't fully support the aliasing of the
1123   # initialize() method.
1124   # new() is in
1125   # fact an alias for #civil(): read the documentation for that
1126   # method instead.
1127   #
1128   # Create a new Date object.
1129   #
1130   # +ajd+ is the Astronomical Julian Day Number.
1131   # +of+ is the offset from UTC as a fraction of a day.
1132   # Both default to 0.
1133   #
1134   # +sg+ specifies the Day of Calendar Reform to use for this
1135   # Date object.
1136   #
1137   # Using one of the factory methods such as Date::civil is
1138   # generally easier and safer.
1139   def initialize(ajd=0, of=0, sg=ITALY)
1140     @ajd, @of, @sg = ajd, of, sg
1141     @__ca__ = {}
1142   end
1144   # Get the date as an Astronomical Julian Day Number.
1145   def ajd() @ajd end
1147   # Get the date as an Astronomical Modified Julian Day Number.
1148   def amjd() ajd_to_amjd(@ajd) end
1150   once :amjd
1152   # Get the date as a Julian Day Number.
1153   def jd() ajd_to_jd(@ajd, @of)[0] end
1155   # Get any fractional day part of the date.
1156   def day_fraction() ajd_to_jd(@ajd, @of)[1] end
1158   # Get the date as a Modified Julian Day Number.
1159   def mjd() jd_to_mjd(jd) end
1161   # Get the date as the number of days since the Day of Calendar
1162   # Reform (in Italy and the Catholic countries).
1163   def ld() jd_to_ld(jd) end
1165   once :jd, :day_fraction, :mjd, :ld
1167   # Get the date as a Civil Date, [year, month, day_of_month]
1168   def civil() jd_to_civil(jd, @sg) end # :nodoc:
1170   # Get the date as an Ordinal Date, [year, day_of_year]
1171   def ordinal() jd_to_ordinal(jd, @sg) end # :nodoc:
1173   # Get the date as a Commercial Date, [year, week_of_year, day_of_week]
1174   def commercial() jd_to_commercial(jd, @sg) end # :nodoc:
1176   def weeknum0() jd_to_weeknum(jd, 0, @sg) end # :nodoc:
1177   def weeknum1() jd_to_weeknum(jd, 1, @sg) end # :nodoc:
1179   once :civil, :ordinal, :commercial, :weeknum0, :weeknum1
1180   private :civil, :ordinal, :commercial, :weeknum0, :weeknum1
1182   # Get the year of this date.
1183   def year() civil[0] end
1185   # Get the day-of-the-year of this date.
1186   #
1187   # January 1 is day-of-the-year 1
1188   def yday() ordinal[1] end
1190   # Get the month of this date.
1191   #
1192   # January is month 1.
1193   def mon() civil[1] end
1195   # Get the day-of-the-month of this date.
1196   def mday() civil[2] end
1198   alias_method :month, :mon
1199   alias_method :day, :mday
1201   def wnum0() weeknum0[1] end # :nodoc:
1202   def wnum1() weeknum1[1] end # :nodoc:
1204   private :wnum0, :wnum1
1206   # Get the time of this date as [hours, minutes, seconds,
1207   # fraction_of_a_second]
1208   def time() day_fraction_to_time(day_fraction) end # :nodoc:
1210   once :time
1211   private :time
1213   # Get the hour of this date.
1214   def hour() time[0] end
1216   # Get the minute of this date.
1217   def min() time[1] end
1219   # Get the second of this date.
1220   def sec() time[2] end
1222   # Get the fraction-of-a-second of this date.
1223   def sec_fraction() time[3] end
1225   alias_method :minute, :min
1226   alias_method :second, :sec
1227   alias_method :second_fraction, :sec_fraction
1229   private :hour, :min, :sec, :sec_fraction,
1230           :minute, :second, :second_fraction
1232   def zone() strftime('%:z') end
1234   private :zone
1236   # Get the commercial year of this date.  See *Commercial* *Date*
1237   # in the introduction for how this differs from the normal year.
1238   def cwyear() commercial[0] end
1240   # Get the commercial week of the year of this date.
1241   def cweek() commercial[1] end
1243   # Get the commercial day of the week of this date.  Monday is
1244   # commercial day-of-week 1; Sunday is commercial day-of-week 7.
1245   def cwday() commercial[2] end
1247   # Get the week day of this date.  Sunday is day-of-week 0;
1248   # Saturday is day-of-week 6.
1249   def wday() jd_to_wday(jd) end
1251   once :wday
1253 =begin
1254   MONTHNAMES.each_with_index do |n, i|
1255     if n
1256       define_method(n.downcase + '?'){mon == i}
1257     end
1258   end
1259 =end
1261   DAYNAMES.each_with_index do |n, i|
1262     define_method(n.downcase + '?'){wday == i}
1263   end
1265   def nth_kday? (n, k)
1266     k == wday && jd === nth_kday_to_jd(year, mon, n, k, start)
1267   end
1269   private :nth_kday?
1271   # Is the current date old-style (Julian Calendar)?
1272   def julian? () jd < @sg end
1274   # Is the current date new-style (Gregorian Calendar)?
1275   def gregorian? () !julian? end
1277   once :julian?, :gregorian?
1279   def fix_style # :nodoc:
1280     if julian?
1281     then self.class::JULIAN
1282     else self.class::GREGORIAN end
1283   end
1285   private :fix_style
1287   # Is this a leap year?
1288   def leap?
1289     jd_to_civil(civil_to_jd(year, 3, 1, fix_style) - 1,
1290                 fix_style)[-1] == 29
1291   end
1293   once :leap?
1295   # When is the Day of Calendar Reform for this Date object?
1296   def start() @sg end
1298   # Create a copy of this Date object using a new Day of Calendar Reform.
1299   def new_start(sg=self.class::ITALY) self.class.new!(@ajd, @of, sg) end
1301   # Create a copy of this Date object that uses the Italian/Catholic
1302   # Day of Calendar Reform.
1303   def italy() new_start(self.class::ITALY) end
1305   # Create a copy of this Date object that uses the English/Colonial
1306   # Day of Calendar Reform.
1307   def england() new_start(self.class::ENGLAND) end
1309   # Create a copy of this Date object that always uses the Julian
1310   # Calendar.
1311   def julian() new_start(self.class::JULIAN) end
1313   # Create a copy of this Date object that always uses the Gregorian
1314   # Calendar.
1315   def gregorian() new_start(self.class::GREGORIAN) end
1317   def offset() @of end
1319   def new_offset(of=0)
1320     if String === of
1321       of = Rational(zone_to_diff(of) || 0, 86400)
1322     end
1323     self.class.new!(@ajd, of, @sg)
1324   end
1326   private :offset, :new_offset
1328   # Return a new Date object that is +n+ days later than the
1329   # current one.
1330   #
1331   # +n+ may be a negative value, in which case the new Date
1332   # is earlier than the current one; however, #-() might be
1333   # more intuitive.
1334   #
1335   # If +n+ is not a Numeric, a TypeError will be thrown.  In
1336   # particular, two Dates cannot be added to each other.
1337   def + (n)
1338     case n
1339     when Numeric; return self.class.new!(@ajd + n, @of, @sg)
1340     end
1341     raise TypeError, 'expected numeric'
1342   end
1344   # If +x+ is a Numeric value, create a new Date object that is
1345   # +x+ days earlier than the current one.
1346   #
1347   # If +x+ is a Date, return the number of days between the
1348   # two dates; or, more precisely, how many days later the current
1349   # date is than +x+.
1350   #
1351   # If +x+ is neither Numeric nor a Date, a TypeError is raised.
1352   def - (x)
1353     case x
1354     when Numeric; return self.class.new!(@ajd - x, @of, @sg)
1355     when Date;    return @ajd - x.ajd
1356     end
1357     raise TypeError, 'expected numeric or date'
1358   end
1360   # Compare this date with another date.
1361   #
1362   # +other+ can also be a Numeric value, in which case it is
1363   # interpreted as an Astronomical Julian Day Number.
1364   #
1365   # Comparison is by Astronomical Julian Day Number, including
1366   # fractional days.  This means that both the time and the
1367   # timezone offset are taken into account when comparing
1368   # two DateTime instances.  When comparing a DateTime instance
1369   # with a Date instance, the time of the latter will be
1370   # considered as falling on midnight UTC.
1371   def <=> (other)
1372     case other
1373     when Numeric; return @ajd <=> other
1374     when Date;    return @ajd <=> other.ajd
1375     end
1376     nil
1377   end
1379   # The relationship operator for Date.
1380   #
1381   # Compares dates by Julian Day Number.  When comparing
1382   # two DateTime instances, or a DateTime with a Date,
1383   # the instances will be regarded as equivalent if they
1384   # fall on the same date in local time.
1385   def === (other)
1386     case other
1387     when Numeric; return jd == other
1388     when Date;    return jd == other.jd
1389     end
1390     false
1391   end
1393   def next_day(n=1) self + n end
1394   def prev_day(n=1) self - n end
1396   # Return a new Date one day after this one.
1397   def next() next_day end
1399   alias_method :succ, :next
1401   # Return a new Date object that is +n+ months later than
1402   # the current one.
1403   #
1404   # If the day-of-the-month of the current Date is greater
1405   # than the last day of the target month, the day-of-the-month
1406   # of the returned Date will be the last day of the target month.
1407   def >> (n)
1408     y, m = (year * 12 + (mon - 1) + n).divmod(12)
1409     m,   = (m + 1)                    .divmod(1)
1410     d = mday
1411     d -= 1 until jd2 = _valid_civil?(y, m, d, @sg)
1412     self + (jd2 - jd)
1413   end
1415   # Return a new Date object that is +n+ months earlier than
1416   # the current one.
1417   #
1418   # If the day-of-the-month of the current Date is greater
1419   # than the last day of the target month, the day-of-the-month
1420   # of the returned Date will be the last day of the target month.
1421   def << (n) self >> -n end
1423   def next_month(n=1) self >> n end
1424   def prev_month(n=1) self << n end
1426   def next_year(n=1) self >> n * 12 end
1427   def prev_year(n=1) self << n * 12 end
1429   require 'enumerator'
1431   # Step the current date forward +step+ days at a
1432   # time (or backward, if +step+ is negative) until
1433   # we reach +limit+ (inclusive), yielding the resultant
1434   # date at each step.
1435   def step(limit, step=1) # :yield: date
1436 =begin
1437     if step.zero?
1438       raise ArgumentError, "step can't be 0"
1439     end
1440 =end
1441     unless block_given?
1442       return to_enum(:step, limit, step)
1443     end
1444     da = self
1445     op = %w(- <= >=)[step <=> 0]
1446     while da.__send__(op, limit)
1447       yield da
1448       da += step
1449     end
1450     self
1451   end
1453   # Step forward one day at a time until we reach +max+
1454   # (inclusive), yielding each date as we go.
1455   def upto(max, &block) # :yield: date
1456     step(max, +1, &block)
1457   end
1459   # Step backward one day at a time until we reach +min+
1460   # (inclusive), yielding each date as we go.
1461   def downto(min, &block) # :yield: date
1462     step(min, -1, &block)
1463   end
1465   # Is this Date equal to +other+?
1466   #
1467   # +other+ must both be a Date object, and represent the same date.
1468   def eql? (other) Date === other && self == other end
1470   # Calculate a hash value for this date.
1471   def hash() @ajd.hash end
1473   # Return internal object state as a programmer-readable string.
1474   def inspect() format('#<%s: %s,%s,%s>', self.class, @ajd, @of, @sg) end
1476   # Return the date as a human-readable string.
1477   #
1478   # The format used is YYYY-MM-DD.
1479   def to_s() format('%.4d-%02d-%02d', year, mon, mday) end # 4p
1481   # Dump to Marshal format.
1482   def marshal_dump() [@ajd, @of, @sg] end
1484   # Load from Marshal format.
1485   def marshal_load(a)
1486     @ajd, @of, @sg, = a
1487     @__ca__ = {}
1488   end
1492 # Class representing a date and time.
1494 # See the documentation to the file date.rb for an overview.
1496 # DateTime objects are immutable once created.
1498 # == Other methods.
1500 # The following methods are defined in Date, but declared private
1501 # there.  They are made public in DateTime.  They are documented
1502 # here.
1504 # === hour()
1506 # Get the hour-of-the-day of the time.  This is given
1507 # using the 24-hour clock, counting from midnight.  The first
1508 # hour after midnight is hour 0; the last hour of the day is
1509 # hour 23.
1511 # === min()
1513 # Get the minute-of-the-hour of the time.
1515 # === sec()
1517 # Get the second-of-the-minute of the time.
1519 # === sec_fraction()
1521 # Get the fraction of a second of the time.  This is returned as
1522 # a +Rational+.
1524 # === zone()
1526 # Get the time zone as a String.  This is representation of the
1527 # time offset such as "+1000", not the true time-zone name.
1529 # === offset()
1531 # Get the time zone offset as a fraction of a day.  This is returned
1532 # as a +Rational+.
1534 # === new_offset(of=0)
1536 # Create a new DateTime object, identical to the current one, except
1537 # with a new time zone offset of +of+.  +of+ is the new offset from
1538 # UTC as a fraction of a day.
1540 class DateTime < Date
1542   # Create a new DateTime object corresponding to the specified
1543   # Julian Day Number +jd+ and hour +h+, minute +min+, second +s+.
1544   #
1545   # The 24-hour clock is used.  Negative values of +h+, +min+, and
1546   # +sec+ are treating as counting backwards from the end of the
1547   # next larger unit (e.g. a +min+ of -2 is treated as 58).  No
1548   # wraparound is performed.  If an invalid time portion is specified,
1549   # an ArgumentError is raised.
1550   #
1551   # +of+ is the offset from UTC as a fraction of a day (defaults to 0).
1552   # +sg+ specifies the Day of Calendar Reform.
1553   #
1554   # All day/time values default to 0.
1555   def self.jd(jd=0, h=0, min=0, s=0, of=0, sg=ITALY)
1556     unless (jd = _valid_jd?(jd, sg)) &&
1557            (fr = _valid_time?(h, min, s))
1558       raise ArgumentError, 'invalid date'
1559     end
1560     if String === of
1561       of = Rational(zone_to_diff(of) || 0, 86400)
1562     end
1563     new!(jd_to_ajd(jd, fr, of), of, sg)
1564   end
1566   # Create a new DateTime object corresponding to the specified
1567   # Ordinal Date and hour +h+, minute +min+, second +s+.
1568   #
1569   # The 24-hour clock is used.  Negative values of +h+, +min+, and
1570   # +sec+ are treating as counting backwards from the end of the
1571   # next larger unit (e.g. a +min+ of -2 is treated as 58).  No
1572   # wraparound is performed.  If an invalid time portion is specified,
1573   # an ArgumentError is raised.
1574   #
1575   # +of+ is the offset from UTC as a fraction of a day (defaults to 0).
1576   # +sg+ specifies the Day of Calendar Reform.
1577   #
1578   # +y+ defaults to -4712, and +d+ to 1; this is Julian Day Number
1579   # day 0.  The time values default to 0.
1580   def self.ordinal(y=-4712, d=1, h=0, min=0, s=0, of=0, sg=ITALY)
1581     unless (jd = _valid_ordinal?(y, d, sg)) &&
1582            (fr = _valid_time?(h, min, s))
1583       raise ArgumentError, 'invalid date'
1584     end
1585     if String === of
1586       of = Rational(zone_to_diff(of) || 0, 86400)
1587     end
1588     new!(jd_to_ajd(jd, fr, of), of, sg)
1589   end
1591   # Create a new DateTime object corresponding to the specified
1592   # Civil Date and hour +h+, minute +min+, second +s+.
1593   #
1594   # The 24-hour clock is used.  Negative values of +h+, +min+, and
1595   # +sec+ are treating as counting backwards from the end of the
1596   # next larger unit (e.g. a +min+ of -2 is treated as 58).  No
1597   # wraparound is performed.  If an invalid time portion is specified,
1598   # an ArgumentError is raised.
1599   #
1600   # +of+ is the offset from UTC as a fraction of a day (defaults to 0).
1601   # +sg+ specifies the Day of Calendar Reform.
1602   #
1603   # +y+ defaults to -4712, +m+ to 1, and +d+ to 1; this is Julian Day
1604   # Number day 0.  The time values default to 0.
1605   def self.civil(y=-4712, m=1, d=1, h=0, min=0, s=0, of=0, sg=ITALY)
1606     unless (jd = _valid_civil?(y, m, d, sg)) &&
1607            (fr = _valid_time?(h, min, s))
1608       raise ArgumentError, 'invalid date'
1609     end
1610     if String === of
1611       of = Rational(zone_to_diff(of) || 0, 86400)
1612     end
1613     new!(jd_to_ajd(jd, fr, of), of, sg)
1614   end
1616   class << self; alias_method :new, :civil end
1618   # Create a new DateTime object corresponding to the specified
1619   # Commercial Date and hour +h+, minute +min+, second +s+.
1620   #
1621   # The 24-hour clock is used.  Negative values of +h+, +min+, and
1622   # +sec+ are treating as counting backwards from the end of the
1623   # next larger unit (e.g. a +min+ of -2 is treated as 58).  No
1624   # wraparound is performed.  If an invalid time portion is specified,
1625   # an ArgumentError is raised.
1626   #
1627   # +of+ is the offset from UTC as a fraction of a day (defaults to 0).
1628   # +sg+ specifies the Day of Calendar Reform.
1629   #
1630   # +y+ defaults to -4712, +w+ to 1, and +d+ to 1; this is
1631   # Julian Day Number day 0.
1632   # The time values default to 0.
1633   def self.commercial(y=-4712, w=1, d=1, h=0, min=0, s=0, of=0, sg=ITALY)
1634     unless (jd = _valid_commercial?(y, w, d, sg)) &&
1635            (fr = _valid_time?(h, min, s))
1636       raise ArgumentError, 'invalid date'
1637     end
1638     if String === of
1639       of = Rational(zone_to_diff(of) || 0, 86400)
1640     end
1641     new!(jd_to_ajd(jd, fr, of), of, sg)
1642   end
1644   def self.weeknum(y=-4712, w=0, d=1, f=0, h=0, min=0, s=0, of=0, sg=ITALY) # :nodoc:
1645     unless (jd = _valid_weeknum?(y, w, d, f, sg)) &&
1646            (fr = _valid_time?(h, min, s))
1647       raise ArgumentError, 'invalid date'
1648     end
1649     if String === of
1650       of = Rational(zone_to_diff(of) || 0, 86400)
1651     end
1652     new!(jd_to_ajd(jd, fr, of), of, sg)
1653   end
1655   private_class_method :weeknum
1657   def self.nth_kday(y=-4712, m=1, n=1, k=1, h=0, min=0, s=0, of=0, sg=ITALY) # :nodoc:
1658     unless (jd = _valid_nth_kday?(y, m, n, k, sg)) &&
1659            (fr = _valid_time?(h, min, s))
1660       raise ArgumentError, 'invalid date'
1661     end
1662     if String === of
1663       of = Rational(zone_to_diff(of) || 0, 86400)
1664     end
1665     new!(jd_to_ajd(jd, fr, of), of, sg)
1666   end
1668   private_class_method :nth_kday
1670   def self.new_by_frags(elem, sg) # :nodoc:
1671     elem = rewrite_frags(elem)
1672     elem = complete_frags(elem)
1673     unless (jd = valid_date_frags?(elem, sg)) &&
1674            (fr = valid_time_frags?(elem))
1675       raise ArgumentError, 'invalid date'
1676     end
1677     fr += (elem[:sec_fraction] || 0) / 86400
1678     of = Rational(elem[:offset] || 0, 86400)
1679     new!(jd_to_ajd(jd, fr, of), of, sg)
1680   end
1682   private_class_method :new_by_frags
1684   # Create a new DateTime object by parsing from a String
1685   # according to a specified format.
1686   #
1687   # +str+ is a String holding a date-time representation.
1688   # +fmt+ is the format that the date-time is in.  See
1689   # date/format.rb for details on supported formats.
1690   #
1691   # The default +str+ is '-4712-01-01T00:00:00+00:00', and the default
1692   # +fmt+ is '%FT%T%z'.  This gives midnight on Julian Day Number day 0.
1693   #
1694   # +sg+ specifies the Day of Calendar Reform.
1695   #
1696   # An ArgumentError will be raised if +str+ cannot be
1697   # parsed.
1698   def self.strptime(str='-4712-01-01T00:00:00+00:00', fmt='%FT%T%z', sg=ITALY)
1699     elem = _strptime(str, fmt)
1700     new_by_frags(elem, sg)
1701   end
1703   # Create a new DateTime object by parsing from a String,
1704   # without specifying the format.
1705   #
1706   # +str+ is a String holding a date-time representation.
1707   # +comp+ specifies whether to interpret 2-digit years
1708   # as 19XX (>= 69) or 20XX (< 69); the default is not to.
1709   # The method will attempt to parse a date-time from the String
1710   # using various heuristics; see #_parse in date/format.rb
1711   # for more details.  If parsing fails, an ArgumentError
1712   # will be raised.
1713   #
1714   # The default +str+ is '-4712-01-01T00:00:00+00:00'; this is Julian
1715   # Day Number day 0.
1716   #
1717   # +sg+ specifies the Day of Calendar Reform.
1718   def self.parse(str='-4712-01-01T00:00:00+00:00', comp=true, sg=ITALY)
1719     elem = _parse(str, comp)
1720     new_by_frags(elem, sg)
1721   end
1723   def self.iso8601(str='-4712-01-01T00:00:00+00:00', sg=ITALY) # :nodoc:
1724     elem = _iso8601(str)
1725     new_by_frags(elem, sg)
1726   end
1728   def self.rfc3339(str='-4712-01-01T00:00:00+00:00', sg=ITALY) # :nodoc:
1729     elem = _rfc3339(str)
1730     new_by_frags(elem, sg)
1731   end
1733   def self.xmlschema(str='-4712-01-01T00:00:00+00:00', sg=ITALY) # :nodoc:
1734     elem = _xmlschema(str)
1735     new_by_frags(elem, sg)
1736   end
1738   def self.rfc2822(str='Mon, 1 Jan -4712 00:00:00 +0000', sg=ITALY) # :nodoc:
1739     elem = _rfc2822(str)
1740     new_by_frags(elem, sg)
1741   end
1743   class << self; alias_method :rfc822, :rfc2822 end
1745   def self.httpdate(str='Mon, 01 Jan -4712 00:00:00 GMT', sg=ITALY) # :nodoc:
1746     elem = _httpdate(str)
1747     new_by_frags(elem, sg)
1748   end
1750   def self.jisx0301(str='-4712-01-01T00:00:00+00:00', sg=ITALY) # :nodoc:
1751     elem = _jisx0301(str)
1752     new_by_frags(elem, sg)
1753   end
1755   public :hour, :min, :sec, :sec_fraction, :zone, :offset, :new_offset,
1756          :minute, :second, :second_fraction
1758   def to_s # 4p
1759     format('%.4d-%02d-%02dT%02d:%02d:%02d%s',
1760            year, mon, mday, hour, min, sec, zone)
1761   end
1765 class Time
1767   def to_time() getlocal end
1769   def to_date
1770     jd = Date.__send__(:civil_to_jd, year, mon, mday, Date::ITALY)
1771     Date.new!(Date.__send__(:jd_to_ajd, jd, 0, 0), 0, Date::ITALY)
1772   end
1774   def to_datetime
1775     jd = DateTime.__send__(:civil_to_jd, year, mon, mday, DateTime::ITALY)
1776     fr = DateTime.__send__(:time_to_day_fraction, hour, min, [sec, 59].min) +
1777       Rational(nsec, 86400_000_000_000)
1778     of = Rational(utc_offset, 86400)
1779     DateTime.new!(DateTime.__send__(:jd_to_ajd, jd, fr, of),
1780                   of, DateTime::ITALY)
1781   end
1785 class Date
1787   def to_time() Time.local(year, mon, mday) end
1788   def to_date() self end
1789   def to_datetime() DateTime.new!(jd_to_ajd(jd, 0, 0), @of, @sg) end
1791   # Create a new Date object representing today.
1792   #
1793   # +sg+ specifies the Day of Calendar Reform.
1794   def self.today(sg=ITALY) Time.now.to_date    .new_start(sg) end
1796   # Create a new DateTime object representing the current time.
1797   #
1798   # +sg+ specifies the Day of Calendar Reform.
1799   def self.now  (sg=ITALY) Time.now.to_datetime.new_start(sg) end
1801   private_class_method :now
1805 class DateTime < Date
1807   def to_time
1808     d = new_offset(0)
1809     d.instance_eval do
1810       Time.utc(year, mon, mday, hour, min, sec +
1811                sec_fraction)
1812     end.
1813         getlocal
1814   end
1816   def to_date() Date.new!(jd_to_ajd(jd, 0, 0), 0, @sg) end
1817   def to_datetime() self end
1819   private_class_method :today
1820   public_class_method  :now