Fix up Rubinius specific library specs.
[rbx.git] / lib / date.rb
blob39f45146955a595a79777b32c696381303110ac1
2 # date.rb - date and time library
4 # Author: Tadayoshi Funaba 1998-2006
6 # Documentation: William Webber <william@williamwebber.com>
8 #--
9 # $Id: date.rb,v 2.30 2006-12-30 21:43:41+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}.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   # Does a given Julian Day Number fall inside the old-style (Julian)
332   # calendar?
333   #
334   # +jd+ is the Julian Day Number in question. +sg+ may be Date::GREGORIAN,
335   # in which case the answer is false; it may be Date::JULIAN, in which case
336   # the answer is true; or it may a number representing the Day of
337   # Calendar Reform. Date::ENGLAND and Date::ITALY are two possible such
338   # days.
340   def self.julian? (jd, sg)
341     case sg
342     when Numeric
343       jd < sg
344     else
345       if $VERBOSE
346         warn("#{caller.shift.sub(/:in .*/, '')}: " \
347 "warning: do not use non-numerical object as julian day number anymore")
348       end
349       not sg
350     end
351   end
353   # Does a given Julian Day Number fall inside the new-style (Gregorian)
354   # calendar?
355   #
356   # The reverse of self.os?  See the documentation for that method for
357   # more details.
358   def self.gregorian? (jd, sg) !julian?(jd, sg) end
360   def self.fix_style(jd, sg) # :nodoc:
361     if julian?(jd, sg)
362     then JULIAN
363     else GREGORIAN end
364   end
366   private_class_method :fix_style
368   # Convert an Ordinal Date to a Julian Day Number.
369   #
370   # +y+ and +d+ are the year and day-of-year to convert.
371   # +sg+ specifies the Day of Calendar Reform.
372   #
373   # Returns the corresponding Julian Day Number.
374   def self.ordinal_to_jd(y, d, sg=GREGORIAN)
375     civil_to_jd(y, 1, d, sg)
376   end
378   # Convert a Julian Day Number to an Ordinal Date.
379   #
380   # +jd+ is the Julian Day Number to convert.
381   # +sg+ specifies the Day of Calendar Reform.
382   #
383   # Returns the corresponding Ordinal Date as
384   # [year, day_of_year]
385   def self.jd_to_ordinal(jd, sg=GREGORIAN)
386     y = jd_to_civil(jd, sg)[0]
387     doy = jd - civil_to_jd(y - 1, 12, 31, fix_style(jd, sg))
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 self.civil_to_jd(y, m, d, sg=GREGORIAN)
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 julian?(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 self.jd_to_civil(jd, sg=GREGORIAN)
419     if julian?(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 self.commercial_to_jd(y, w, d, ns=GREGORIAN)
446     jd = civil_to_jd(y, 1, 4, ns)
447     (jd - (((jd - 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 self.jd_to_commercial(jd, sg=GREGORIAN)
460     ns = fix_style(jd, sg)
461     a = jd_to_civil(jd - 3, ns)[0]
462     y = if jd >= commercial_to_jd(a + 1, 1, 1, ns) then a + 1 else a end
463     w = 1 + ((jd - commercial_to_jd(y, 1, 1, ns)) / 7).floor
464     d = (jd + 1) % 7
465     d = 7 if d == 0
466     return y, w, d
467   end
469   def self.weeknum_to_jd(y, w, d, f=0, ns=GREGORIAN) # :nodoc:
470     a = civil_to_jd(y, 1, 1, ns) + 6
471     (a - ((a - f) + 1) % 7 - 7) + 7 * w + d
472   end
474   def self.jd_to_weeknum(jd, f=0, sg=GREGORIAN) # :nodoc:
475     ns = fix_style(jd, sg)
476     y, m, d = jd_to_civil(jd, ns)
477     a = civil_to_jd(y, 1, 1, ns) + 6
478     w, d = (jd - (a - ((a - f) + 1) % 7) + 7).divmod(7)
479     return y, w, d
480   end
482   private_class_method :weeknum_to_jd, :jd_to_weeknum
484   # Convert an Astronomical Julian Day Number to a (civil) Julian
485   # Day Number.
486   #
487   # +ajd+ is the Astronomical Julian Day Number to convert.
488   # +of+ is the offset from UTC as a fraction of a day (defaults to 0).
489   #
490   # Returns the (civil) Julian Day Number as [day_number,
491   # fraction] where +fraction+ is always 1/2.
492   def self.ajd_to_jd(ajd, of=0) (ajd + of + 1.to_r/2).divmod(1) end
494   # Convert a (civil) Julian Day Number to an Astronomical Julian
495   # Day Number.
496   #
497   # +jd+ is the Julian Day Number to convert, and +fr+ is a
498   # fractional day.
499   # +of+ is the offset from UTC as a fraction of a day (defaults to 0).
500   #
501   # Returns the Astronomical Julian Day Number as a single
502   # numeric value.
503   def self.jd_to_ajd(jd, fr, of=0) jd + fr - of - 1.to_r/2 end
505   # Convert a fractional day +fr+ to [hours, minutes, seconds,
506   # fraction_of_a_second]
507   def self.day_fraction_to_time(fr)
508     h,   fr = fr.divmod(1.to_r/24)
509     min, fr = fr.divmod(1.to_r/1440)
510     s,   fr = fr.divmod(1.to_r/86400)
511     return h, min, s, fr
512   end
514   # Convert an +h+ hour, +min+ minutes, +s+ seconds period
515   # to a fractional day.
516   def self.time_to_day_fraction(h, min, s)
517     h.to_r/24 + min.to_r/1440 + s.to_r/86400
518   end
520   # Convert an Astronomical Modified Julian Day Number to an
521   # Astronomical Julian Day Number.
522   def self.amjd_to_ajd(amjd) amjd + 4800001.to_r/2 end
524   # Convert an Astronomical Julian Day Number to an
525   # Astronomical Modified Julian Day Number.
526   def self.ajd_to_amjd(ajd) ajd - 4800001.to_r/2 end
528   # Convert a Modified Julian Day Number to a Julian
529   # Day Number.
530   def self.mjd_to_jd(mjd) mjd + 2400001 end
532   # Convert a Julian Day Number to a Modified Julian Day
533   # Number.
534   def self.jd_to_mjd(jd) jd - 2400001 end
536   # Convert a count of the number of days since the adoption
537   # of the Gregorian Calendar (in Italy) to a Julian Day Number.
538   def self.ld_to_jd(ld) ld + 2299160 end
540   # Convert a Julian Day Number to the number of days since
541   # the adoption of the Gregorian Calendar (in Italy).
542   def self.jd_to_ld(jd) jd - 2299160 end
544   # Convert a Julian Day Number to the day of the week.
545   #
546   # Sunday is day-of-week 0; Saturday is day-of-week 6.
547   def self.jd_to_wday(jd) (jd + 1) % 7 end
549   # Is a year a leap year in the Julian calendar?
550   #
551   # All years divisible by 4 are leap years in the Julian calendar.
552   def self.julian_leap? (y) y % 4 == 0 end
554   # Is a year a leap year in the Gregorian calendar?
555   #
556   # All years divisible by 4 are leap years in the Gregorian calendar,
557   # except for years divisible by 100 and not by 400.
558   def self.gregorian_leap? (y) y % 4 == 0 && y % 100 != 0 || y % 400 == 0 end
560   class << self; alias_method :leap?, :gregorian_leap? end
561   class << self; alias_method :new!, :new end
563   # Is +jd+ a valid Julian Day Number?
564   #
565   # If it is, returns it.  In fact, any value is treated as a valid
566   # Julian Day Number.
567   def self.valid_jd? (jd, sg=ITALY) jd end
569   # Do the year +y+ and day-of-year +d+ make a valid Ordinal Date?
570   # Returns the corresponding Julian Day Number if they do, or
571   # nil if they don't.
572   #
573   # +d+ can be a negative number, in which case it counts backwards
574   # from the end of the year (-1 being the last day of the year).
575   # No year wraparound is performed, however, so valid values of
576   # +d+ are -365 .. -1, 1 .. 365 on a non-leap-year,
577   # -366 .. -1, 1 .. 366 on a leap year.
578   # A date falling in the period skipped in the Day of Calendar Reform
579   # adjustment is not valid.
580   #
581   # +sg+ specifies the Day of Calendar Reform.
582   def self.valid_ordinal? (y, d, sg=ITALY)
583     if d < 0
584       ny, = (y + 1).divmod(1)
585       jd = ordinal_to_jd(ny, d + 1, sg)
586       ns = fix_style(jd, sg)
587       return unless [y] == jd_to_ordinal(jd, sg)[0..0]
588       return unless [ny, 1] == jd_to_ordinal(jd - d, ns)
589     else
590       jd = ordinal_to_jd(y, d, sg)
591       return unless [y, d] == jd_to_ordinal(jd, sg)
592     end
593     jd
594   end
596   # Do year +y+, month +m+, and day-of-month +d+ make a
597   # valid Civil Date?  Returns the corresponding Julian
598   # Day Number if they do, nil if they don't.
599   #
600   # +m+ and +d+ can be negative, in which case they count
601   # backwards from the end of the year and the end of the
602   # month respectively.  No wraparound is performed, however,
603   # and invalid values cause an ArgumentError to be raised.
604   # A date falling in the period skipped in the Day of Calendar
605   # Reform adjustment is not valid.
606   #
607   # +sg+ specifies the Day of Calendar Reform.
608   def self.valid_civil? (y, m, d, sg=ITALY)
609     if m < 0
610       m += 13
611     end
612     if d < 0
613       ny, nm = (y * 12 + m).divmod(12)
614       nm,    = (nm + 1).divmod(1)
615       jd = civil_to_jd(ny, nm, d + 1, sg)
616       ns = fix_style(jd, sg)
617       return unless [y, m] == jd_to_civil(jd, sg)[0..1]
618       return unless [ny, nm, 1] == jd_to_civil(jd - d, ns)
619     else
620       jd = civil_to_jd(y, m, d, sg)
621       return unless [y, m, d] == jd_to_civil(jd, sg)
622     end
623     jd
624   end
626   class << self; alias_method :valid_date?, :valid_civil? end
628   # Do year +y+, week-of-year +w+, and day-of-week +d+ make a
629   # valid Commercial Date?  Returns the corresponding Julian
630   # Day Number if they do, nil if they don't.
631   #
632   # Monday is day-of-week 1; Sunday is day-of-week 7.
633   #
634   # +w+ and +d+ can be negative, in which case they count
635   # backwards from the end of the year and the end of the
636   # week respectively.  No wraparound is performed, however,
637   # and invalid values cause an ArgumentError to be raised.
638   # A date falling in the period skipped in the Day of Calendar
639   # Reform adjustment is not valid.
640   #
641   # +sg+ specifies the Day of Calendar Reform.
642   def self.valid_commercial? (y, w, d, sg=ITALY)
643     if d < 0
644       d += 8
645     end
646     if w < 0
647       ny, nw, nd =
648         jd_to_commercial(commercial_to_jd(y + 1, 1, 1) + w * 7)
649       return unless ny == y
650       w = nw
651     end
652     jd = commercial_to_jd(y, w, d)
653     return unless gregorian?(jd, sg)
654     return unless [y, w, d] == jd_to_commercial(jd)
655     jd
656   end
658   def self.valid_weeknum? (y, w, d, f, sg=ITALY) # :nodoc:
659     if d < 0
660       d += 7
661     end
662     if w < 0
663       ny, nw, nd, nf =
664         jd_to_weeknum(weeknum_to_jd(y + 1, 1, f, f) + w * 7, f)
665       return unless ny == y
666       w = nw
667     end
668     jd = weeknum_to_jd(y, w, d, f)
669     return unless gregorian?(jd, sg)
670     return unless [y, w, d] == jd_to_weeknum(jd, f)
671     jd
672   end
674   private_class_method :valid_weeknum?
676   # Do hour +h+, minute +min+, and second +s+ constitute a valid time?
677   #
678   # If they do, returns their value as a fraction of a day.  If not,
679   # returns nil.
680   #
681   # The 24-hour clock is used.  Negative values of +h+, +min+, and
682   # +sec+ are treating as counting backwards from the end of the
683   # next larger unit (e.g. a +min+ of -2 is treated as 58).  No
684   # wraparound is performed.
685   def self.valid_time? (h, min, s)
686     h   += 24 if h   < 0
687     min += 60 if min < 0
688     s   += 60 if s   < 0
689     return unless ((0..23) === h &&
690                    (0..59) === min &&
691                    (0..59) === s) ||
692                   (24 == h &&
693                     0 == min &&
694                     0 == s)
695     time_to_day_fraction(h, min, s)
696   end
698   # Create a new Date object from a Julian Day Number.
699   #
700   # +jd+ is the Julian Day Number; if not specified, it defaults to
701   # 0.
702   # +sg+ specifies the Day of Calendar Reform.
703   def self.jd(jd=0, sg=ITALY)
704     jd = valid_jd?(jd, sg)
705     new!(jd_to_ajd(jd, 0, 0), 0, sg)
706   end
708   # Create a new Date object from an Ordinal Date, specified
709   # by year +y+ and day-of-year +d+. +d+ can be negative,
710   # in which it counts backwards from the end of the year.
711   # No year wraparound is performed, however.  An invalid
712   # value for +d+ results in an ArgumentError being raised.
713   #
714   # +y+ defaults to -4712, and +d+ to 1; this is Julian Day
715   # Number day 0.
716   #
717   # +sg+ specifies the Day of Calendar Reform.
718   def self.ordinal(y=-4712, d=1, sg=ITALY)
719     unless jd = valid_ordinal?(y, d, sg)
720       raise ArgumentError, 'invalid date'
721     end
722     new!(jd_to_ajd(jd, 0, 0), 0, sg)
723   end
725   # Create a new Date object for the Civil Date specified by
726   # year +y+, month +m+, and day-of-month +d+.
727   #
728   # +m+ and +d+ can be negative, in which case they count
729   # backwards from the end of the year and the end of the
730   # month respectively.  No wraparound is performed, however,
731   # and invalid values cause an ArgumentError to be raised.
732   # can be negative
733   #
734   # +y+ defaults to -4712, +m+ to 1, and +d+ to 1; this is
735   # Julian Day Number day 0.
736   #
737   # +sg+ specifies the Day of Calendar Reform.
738   def self.civil(y=-4712, m=1, d=1, sg=ITALY)
739     unless jd = valid_civil?(y, m, d, sg)
740       raise ArgumentError, 'invalid date'
741     end
742     new!(jd_to_ajd(jd, 0, 0), 0, sg)
743   end
745   class << self; alias_method :new, :civil end
747   # Create a new Date object for the Commercial Date specified by
748   # year +y+, week-of-year +w+, and day-of-week +d+.
749   #
750   # Monday is day-of-week 1; Sunday is day-of-week 7.
751   #
752   # +w+ and +d+ can be negative, in which case they count
753   # backwards from the end of the year and the end of the
754   # week respectively.  No wraparound is performed, however,
755   # and invalid values cause an ArgumentError to be raised.
756   #
757   # +y+ defaults to 1582, +w+ to 41, and +d+ to 5, the Day of
758   # Calendar Reform for Italy and the Catholic countries.
759   #
760   # +sg+ specifies the Day of Calendar Reform.
761   def self.commercial(y=1582, w=41, d=5, sg=ITALY)
762     unless jd = valid_commercial?(y, w, d, sg)
763       raise ArgumentError, 'invalid date'
764     end
765     new!(jd_to_ajd(jd, 0, 0), 0, sg)
766   end
768   def self.weeknum(y=1582, w=41, d=5, f=0, sg=ITALY) # :nodoc:
769     unless jd = valid_weeknum?(y, w, d, f, sg)
770       raise ArgumentError, 'invalid date'
771     end
772     new!(jd_to_ajd(jd, 0, 0), 0, sg)
773   end
775   private_class_method :weeknum
777   def self.rewrite_frags(elem) # :nodoc:
778     elem ||= {}
779     if seconds = elem[:seconds]
780       d,   fr = seconds.divmod(86400)
781       h,   fr = fr.divmod(3600)
782       min, fr = fr.divmod(60)
783       s,   fr = fr.divmod(1)
784       elem[:jd] = UNIX_EPOCH_IN_CJD + d
785       elem[:hour] = h
786       elem[:min] = min
787       elem[:sec] = s
788       elem[:sec_fraction] = fr
789       elem.delete(:seconds)
790       elem.delete(:offset)
791     end
792     elem
793   end
795   private_class_method :rewrite_frags
797   def self.complete_frags(elem) # :nodoc:
798     i = 0
799     g = [[:time, [:hour, :min, :sec]],
800          [nil, [:jd]],
801          [:ordinal, [:year, :yday, :hour, :min, :sec]],
802          [:civil, [:year, :mon, :mday, :hour, :min, :sec]],
803          [:commercial, [:cwyear, :cweek, :cwday, :hour, :min, :sec]],
804          [:wday, [:wday, :hour, :min, :sec]],
805          [:wnum0, [:year, :wnum0, :wday, :hour, :min, :sec]],
806          [:wnum1, [:year, :wnum1, :wday, :hour, :min, :sec]],
807          [nil, [:cwyear, :cweek, :wday, :hour, :min, :sec]],
808          [nil, [:year, :wnum0, :cwday, :hour, :min, :sec]],
809          [nil, [:year, :wnum1, :cwday, :hour, :min, :sec]]].
810       collect{|k, a| e = elem.values_at(*a).compact; [k, a, e]}.
811       select{|k, a, e| e.size > 0}.
812       sort_by{|k, a, e| [e.size, i -= 1]}.last
814     d = nil
816     if g && g[0] && (g[1].size - g[2].size) != 0
817       d ||= Date.today
819       case g[0]
820       when :ordinal
821         elem[:year] ||= d.year
822         elem[:yday] ||= 1
823       when :civil
824         g[1].each do |e|
825           break if elem[e]
826           elem[e] = d.__send__(e)
827         end
828         elem[:mon]  ||= 1
829         elem[:mday] ||= 1
830       when :commercial
831         g[1].each do |e|
832           break if elem[e]
833           elem[e] = d.__send__(e)
834         end
835         elem[:cweek] ||= 1
836         elem[:cwday] ||= 1
837       when :wday
838         elem[:jd] ||= (d - d.wday + elem[:wday]).jd
839       when :wnum0
840         g[1].each do |e|
841           break if elem[e]
842           elem[e] = d.__send__(e)
843         end
844         elem[:wnum0] ||= 0
845         elem[:wday]  ||= 0
846       when :wnum1
847         g[1].each do |e|
848           break if elem[e]
849           elem[e] = d.__send__(e)
850         end
851         elem[:wnum1] ||= 0
852         elem[:wday]  ||= 0
853       end
854     end
856     if g && g[0] == :time
857       if self <= DateTime
858         d ||= Date.today
859         elem[:jd] ||= d.jd
860       end
861     end
863     elem[:hour] ||= 0
864     elem[:min]  ||= 0
865     elem[:sec]  ||= 0
866     elem[:sec] = [elem[:sec], 59].min
868     elem
869   end
871   private_class_method :complete_frags
873   def self.valid_date_frags?(elem, sg) # :nodoc:
874     catch :jd do
875       a = elem.values_at(:jd)
876       if a.all?
877         if jd = valid_jd?(*(a << sg))
878           throw :jd, jd
879         end
880       end
882       a = elem.values_at(:year, :yday)
883       if a.all?
884         if jd = valid_ordinal?(*(a << sg))
885           throw :jd, jd
886         end
887       end
889       a = elem.values_at(:year, :mon, :mday)
890       if a.all?
891         if jd = valid_civil?(*(a << sg))
892           throw :jd, jd
893         end
894       end
896       a = elem.values_at(:cwyear, :cweek, :cwday)
897       if a[2].nil? && elem[:wday]
898         a[2] = elem[:wday].nonzero? || 7
899       end
900       if a.all?
901         if jd = valid_commercial?(*(a << sg))
902           throw :jd, jd
903         end
904       end
906       a = elem.values_at(:year, :wnum0, :wday)
907       if a[2].nil? && elem[:cwday]
908         a[2] = elem[:cwday] % 7
909       end
910       if a.all?
911         if jd = valid_weeknum?(*(a << 0 << sg))
912           throw :jd, jd
913         end
914       end
916       a = elem.values_at(:year, :wnum1, :wday)
917       if a[2]
918         a[2] = (a[2] - 1) % 7
919       end
920       if a[2].nil? && elem[:cwday]
921         a[2] = (elem[:cwday] - 1) % 7
922       end
923       if a.all?
924         if jd = valid_weeknum?(*(a << 1 << sg))
925           throw :jd, jd
926         end
927       end
928     end
929   end
931   private_class_method :valid_date_frags?
933   def self.valid_time_frags? (elem) # :nodoc:
934     h, min, s = elem.values_at(:hour, :min, :sec)
935     valid_time?(h, min, s)
936   end
938   private_class_method :valid_time_frags?
940   def self.new_by_frags(elem, sg) # :nodoc:
941     elem = rewrite_frags(elem)
942     elem = complete_frags(elem)
943     unless jd = valid_date_frags?(elem, sg)
944       raise ArgumentError, 'invalid date'
945     end
946     new!(jd_to_ajd(jd, 0, 0), 0, sg)
947   end
949   private_class_method :new_by_frags
951   # Create a new Date object by parsing from a String
952   # according to a specified format.
953   #
954   # +str+ is a String holding a date representation.
955   # +fmt+ is the format that the date is in.  See
956   # date/format.rb for details on supported formats.
957   #
958   # The default +str+ is '-4712-01-01', and the default
959   # +fmt+ is '%F', which means Year-Month-Day_of_Month.
960   # This gives Julian Day Number day 0.
961   #
962   # +sg+ specifies the Day of Calendar Reform.
963   #
964   # An ArgumentError will be raised if +str+ cannot be
965   # parsed.
966   def self.strptime(str='-4712-01-01', fmt='%F', sg=ITALY)
967     elem = _strptime(str, fmt)
968     new_by_frags(elem, sg)
969   end
971   # Create a new Date object by parsing from a String,
972   # without specifying the format.
973   #
974   # +str+ is a String holding a date representation.
975   # +comp+ specifies whether to interpret 2-digit years
976   # as 19XX (>= 69) or 20XX (< 69); the default is not to.
977   # The method will attempt to parse a date from the String
978   # using various heuristics; see #_parse in date/format.rb
979   # for more details.  If parsing fails, an ArgumentError
980   # will be raised.
981   #
982   # The default +str+ is '-4712-01-01'; this is Julian
983   # Day Number day 0.
984   #
985   # +sg+ specifies the Day of Calendar Reform.
986   def self.parse(str='-4712-01-01', comp=false, sg=ITALY)
987     elem = _parse(str, comp)
988     new_by_frags(elem, sg)
989   end
991   class << self
993     def once(*ids) # :nodoc:
994       for id in ids
995         module_eval <<-"end;"
996           alias_method :__#{id.object_id}__, :#{id.to_s}
997           private :__#{id.object_id}__
998           def #{id.to_s}(*args, &block)
999             (@__#{id.object_id}__ ||= [__#{id.object_id}__(*args, &block)])[0]
1000           end
1001         end;
1002       end
1003     end
1005     private :once
1007   end
1009   # *NOTE* this is the documentation for the method new!().  If
1010   # you are reading this as the documentation for new(), that is
1011   # because rdoc doesn't fully support the aliasing of the
1012   # initialize() method.
1013   # new() is in
1014   # fact an alias for #civil(): read the documentation for that
1015   # method instead.
1016   #
1017   # Create a new Date object.
1018   #
1019   # +ajd+ is the Astronomical Julian Day Number.
1020   # +of+ is the offset from UTC as a fraction of a day.
1021   # Both default to 0.
1022   #
1023   # +sg+ specifies the Day of Calendar Reform to use for this
1024   # Date object.
1025   #
1026   # Using one of the factory methods such as Date::civil is
1027   # generally easier and safer.
1028   def initialize(ajd=0, of=0, sg=ITALY) @ajd, @of, @sg = ajd, of, sg end
1030   # Get the date as an Astronomical Julian Day Number.
1031   def ajd() @ajd end
1033   # Get the date as an Astronomical Modified Julian Day Number.
1034   def amjd() self.class.ajd_to_amjd(@ajd) end
1036   once :amjd
1038   # Get the date as a Julian Day Number.
1039   def jd() self.class.ajd_to_jd(@ajd, @of)[0] end
1041   # Get any fractional day part of the date.
1042   def day_fraction() self.class.ajd_to_jd(@ajd, @of)[1] end
1044   # Get the date as a Modified Julian Day Number.
1045   def mjd() self.class.jd_to_mjd(jd) end
1047   # Get the date as the number of days since the Day of Calendar
1048   # Reform (in Italy and the Catholic countries).
1049   def ld() self.class.jd_to_ld(jd) end
1051   once :jd, :day_fraction, :mjd, :ld
1053   # Get the date as a Civil Date, [year, month, day_of_month]
1054   def civil() self.class.jd_to_civil(jd, @sg) end # :nodoc:
1056   # Get the date as an Ordinal Date, [year, day_of_year]
1057   def ordinal() self.class.jd_to_ordinal(jd, @sg) end # :nodoc:
1059   # Get the date as a Commercial Date, [year, week_of_year, day_of_week]
1060   def commercial() self.class.jd_to_commercial(jd, @sg) end # :nodoc:
1062   def weeknum0() self.class.__send__(:jd_to_weeknum, jd, 0, @sg) end # :nodoc:
1063   def weeknum1() self.class.__send__(:jd_to_weeknum, jd, 1, @sg) end # :nodoc:
1065   once :civil, :ordinal, :commercial, :weeknum0, :weeknum1
1066   private :civil, :ordinal, :commercial, :weeknum0, :weeknum1
1068   # Get the year of this date.
1069   def year() civil[0] end
1071   # Get the day-of-the-year of this date.
1072   #
1073   # January 1 is day-of-the-year 1
1074   def yday() ordinal[1] end
1076   # Get the month of this date.
1077   #
1078   # January is month 1.
1079   def mon() civil[1] end
1081   # Get the day-of-the-month of this date.
1082   def mday() civil[2] end
1084   alias_method :month, :mon
1085   alias_method :day, :mday
1087   def wnum0() weeknum0[1] end # :nodoc:
1088   def wnum1() weeknum1[1] end # :nodoc:
1090   private :wnum0, :wnum1
1092   # Get the time of this date as [hours, minutes, seconds,
1093   # fraction_of_a_second]
1094   def time() self.class.day_fraction_to_time(day_fraction) end # :nodoc:
1096   once :time
1097   private :time
1099   # Get the hour of this date.
1100   def hour() time[0] end
1102   # Get the minute of this date.
1103   def min() time[1] end
1105   # Get the second of this date.
1106   def sec() time[2] end
1108   # Get the fraction-of-a-second of this date.  The unit is in days.
1109   # I do NOT recommend you to use this method.
1110   def sec_fraction() time[3] end
1112   private :hour, :min, :sec, :sec_fraction
1114   def zone() strftime('%:z') end
1116   private :zone
1118   # Get the commercial year of this date.  See *Commercial* *Date*
1119   # in the introduction for how this differs from the normal year.
1120   def cwyear() commercial[0] end
1122   # Get the commercial week of the year of this date.
1123   def cweek() commercial[1] end
1125   # Get the commercial day of the week of this date.  Monday is
1126   # commercial day-of-week 1; Sunday is commercial day-of-week 7.
1127   def cwday() commercial[2] end
1129   # Get the week day of this date.  Sunday is day-of-week 0;
1130   # Saturday is day-of-week 6.
1131   def wday() self.class.jd_to_wday(jd) end
1133   once :wday
1135 =begin
1136   MONTHNAMES.each_with_index do |n, i|
1137     if n
1138       define_method(n.downcase + '?'){mon == i}
1139     end
1140   end
1142   DAYNAMES.each_with_index do |n, i|
1143     define_method(n.downcase + '?'){wday == i}
1144   end
1145 =end
1147   # Is the current date old-style (Julian Calendar)?
1148   def julian? () self.class.julian?(jd, @sg) end
1150   # Is the current date new-style (Gregorian Calendar)?
1151   def gregorian? () self.class.gregorian?(jd, @sg) end
1153   once :julian?, :gregorian?
1155   def fix_style # :nodoc:
1156     if julian?
1157     then self.class::JULIAN
1158     else self.class::GREGORIAN end
1159   end
1161   private :fix_style
1163   # Is this a leap year?
1164   def leap?
1165     self.class.jd_to_civil(self.class.civil_to_jd(year, 3, 1, fix_style) - 1,
1166                      fix_style)[-1] == 29
1167   end
1169   once :leap?
1171   # When is the Day of Calendar Reform for this Date object?
1172   def start() @sg end
1174   # Create a copy of this Date object using a new Day of Calendar Reform.
1175   def new_start(sg=self.class::ITALY) self.class.new!(@ajd, @of, sg) end
1177   # Create a copy of this Date object that uses the Italian/Catholic
1178   # Day of Calendar Reform.
1179   def italy() new_start(self.class::ITALY) end
1181   # Create a copy of this Date object that uses the English/Colonial
1182   # Day of Calendar Reform.
1183   def england() new_start(self.class::ENGLAND) end
1185   # Create a copy of this Date object that always uses the Julian
1186   # Calendar.
1187   def julian() new_start(self.class::JULIAN) end
1189   # Create a copy of this Date object that always uses the Gregorian
1190   # Calendar.
1191   def gregorian() new_start(self.class::GREGORIAN) end
1193   def offset() @of end
1195   def new_offset(of=0)
1196     if String === of
1197       of = (self.class.zone_to_diff(of) || 0).to_r/86400
1198     end
1199     self.class.new!(@ajd, of, @sg)
1200   end
1202   private :offset, :new_offset
1204   # Return a new Date object that is +n+ days later than the
1205   # current one.
1206   #
1207   # +n+ may be a negative value, in which case the new Date
1208   # is earlier than the current one; however, #-() might be
1209   # more intuitive.
1210   #
1211   # If +n+ is not a Numeric, a TypeError will be thrown.  In
1212   # particular, two Dates cannot be added to each other.
1213   def + (n)
1214     case n
1215     when Numeric; return self.class.new!(@ajd + n, @of, @sg)
1216     end
1217     raise TypeError, 'expected numeric'
1218   end
1220   # If +x+ is a Numeric value, create a new Date object that is
1221   # +x+ days earlier than the current one.
1222   #
1223   # If +x+ is a Date, return the number of days between the
1224   # two dates; or, more precisely, how many days later the current
1225   # date is than +x+.
1226   #
1227   # If +x+ is neither Numeric nor a Date, a TypeError is raised.
1228   def - (x)
1229     case x
1230     when Numeric; return self.class.new!(@ajd - x, @of, @sg)
1231     when Date;    return @ajd - x.ajd
1232     end
1233     raise TypeError, 'expected numeric or date'
1234   end
1236   # Compare this date with another date.
1237   #
1238   # +other+ can also be a Numeric value, in which case it is
1239   # interpreted as an Astronomical Julian Day Number.
1240   #
1241   # Comparison is by Astronomical Julian Day Number, including
1242   # fractional days.  This means that both the time and the
1243   # timezone offset are taken into account when comparing
1244   # two DateTime instances.  When comparing a DateTime instance
1245   # with a Date instance, the time of the latter will be
1246   # considered as falling on midnight UTC.
1247   def <=> (other)
1248     case other
1249     when Numeric; return @ajd <=> other
1250     when Date;    return @ajd <=> other.ajd
1251     end
1252     nil
1253   end
1255   # The relationship operator for Date.
1256   #
1257   # Compares dates by Julian Day Number.  When comparing
1258   # two DateTime instances, or a DateTime with a Date,
1259   # the instances will be regarded as equivalent if they
1260   # fall on the same date in local time.
1261   def === (other)
1262     case other
1263     when Numeric; return jd == other
1264     when Date;    return jd == other.jd
1265     end
1266     false
1267   end
1269   def next_day(n=1) self + n end
1270 # def prev_day(n=1) self - n end
1272   private :next_day
1274   # Return a new Date one day after this one.
1275   def next() next_day end
1277   alias_method :succ, :next
1279   # Return a new Date object that is +n+ months later than
1280   # the current one.
1281   #
1282   # If the day-of-the-month of the current Date is greater
1283   # than the last day of the target month, the day-of-the-month
1284   # of the returned Date will be the last day of the target month.
1285   def >> (n)
1286     y, m = (year * 12 + (mon - 1) + n).divmod(12)
1287     m,   = (m + 1)                    .divmod(1)
1288     d = mday
1289     d -= 1 until jd2 = self.class.valid_civil?(y, m, d, fix_style)
1290     self + (jd2 - jd)
1291   end
1293   # Return a new Date object that is +n+ months earlier than
1294   # the current one.
1295   #
1296   # If the day-of-the-month of the current Date is greater
1297   # than the last day of the target month, the day-of-the-month
1298   # of the returned Date will be the last day of the target month.
1299   def << (n) self >> -n end
1301 =begin
1302   def next_month(n=1) self >> n end
1303   def prev_month(n=1) self << n end
1305   def next_year(n=1) self >> n * 12 end
1306   def prev_year(n=1) self << n * 12 end
1307 =end
1309 #  require 'enumerator'
1311   # Step the current date forward +step+ days at a
1312   # time (or backward, if +step+ is negative) until
1313   # we reach +limit+ (inclusive), yielding the resultant
1314   # date at each step.
1315   def step(limit, step=1) # :yield: date
1316 =begin
1317     unless block_given?
1318       return to_enum(:step, limit, step)
1319     end
1320 =end
1321     da = self
1322     op = %w(- <= >=)[step <=> 0]
1323     while da.__send__(op, limit)
1324       yield da
1325       da += step
1326     end
1327     self
1328   end
1330   # Step forward one day at a time until we reach +max+
1331   # (inclusive), yielding each date as we go.
1332   def upto(max, &block) # :yield: date
1333     step(max, +1, &block)
1334   end
1336   # Step backward one day at a time until we reach +min+
1337   # (inclusive), yielding each date as we go.
1338   def downto(min, &block) # :yield: date
1339     step(min, -1, &block)
1340   end
1342   # Is this Date equal to +other+?
1343   #
1344   # +other+ must both be a Date object, and represent the same date.
1345   def eql? (other) Date === other && self == other end
1347   # Calculate a hash value for this date.
1348   def hash() @ajd.hash end
1350   # Return internal object state as a programmer-readable string.
1351   def inspect() format('#<%s: %s,%s,%s>', self.class, @ajd, @of, @sg) end
1353   # Return the date as a human-readable string.
1354   #
1355   # The format used is YYYY-MM-DD.
1356   def to_s() strftime end
1358   # Dump to Marshal format.
1359   def _dump(limit) Marshal.dump([@ajd, @of, @sg], -1) end
1361 # def self._load(str) new!(*Marshal.load(str)) end
1363   # Load from Marshall format.
1364   def self._load(str)
1365     a = Marshal.load(str)
1366     if a.size == 2
1367       ajd,     sg = a
1368            of = 0
1369       ajd -= 1.to_r/2
1370     else
1371       ajd, of, sg = a
1372     end
1373     new!(ajd, of, sg)
1374   end
1378 # Class representing a date and time.
1380 # See the documentation to the file date.rb for an overview.
1382 # DateTime objects are immutable once created.
1384 # == Other methods.
1386 # The following methods are defined in Date, but declared private
1387 # there.  They are made public in DateTime.  They are documented
1388 # here.
1390 # === hour()
1392 # Get the hour-of-the-day of the time.  This is given
1393 # using the 24-hour clock, counting from midnight.  The first
1394 # hour after midnight is hour 0; the last hour of the day is
1395 # hour 23.
1397 # === min()
1399 # Get the minute-of-the-hour of the time.
1401 # === sec()
1403 # Get the second-of-the-minute of the time.
1405 # === sec_fraction()
1407 # Get the fraction of a second of the time.  This is returned as
1408 # a +Rational+.  The unit is in days.
1409 # I do NOT recommend you to use this method.
1411 # === zone()
1413 # Get the time zone as a String.  This is representation of the
1414 # time offset such as "+1000", not the true time-zone name.
1416 # === offset()
1418 # Get the time zone offset as a fraction of a day.  This is returned
1419 # as a +Rational+.
1421 # === new_offset(of=0)
1423 # Create a new DateTime object, identical to the current one, except
1424 # with a new time zone offset of +of+.  +of+ is the new offset from
1425 # UTC as a fraction of a day.
1427 class DateTime < Date
1429   # Create a new DateTime object corresponding to the specified
1430   # Julian Day Number +jd+ and hour +h+, minute +min+, second +s+.
1431   #
1432   # The 24-hour clock is used.  Negative values of +h+, +min+, and
1433   # +sec+ are treating as counting backwards from the end of the
1434   # next larger unit (e.g. a +min+ of -2 is treated as 58).  No
1435   # wraparound is performed.  If an invalid time portion is specified,
1436   # an ArgumentError is raised.
1437   #
1438   # +of+ is the offset from UTC as a fraction of a day (defaults to 0).
1439   # +sg+ specifies the Day of Calendar Reform.
1440   #
1441   # All day/time values default to 0.
1442   def self.jd(jd=0, h=0, min=0, s=0, of=0, sg=ITALY)
1443     unless (jd = valid_jd?(jd, sg)) &&
1444            (fr = valid_time?(h, min, s))
1445       raise ArgumentError, 'invalid date'
1446     end
1447     if String === of
1448       of = (zone_to_diff(of) || 0).to_r/86400
1449     end
1450     new!(jd_to_ajd(jd, fr, of), of, sg)
1451   end
1453   # Create a new DateTime object corresponding to the specified
1454   # Ordinal Date and hour +h+, minute +min+, second +s+.
1455   #
1456   # The 24-hour clock is used.  Negative values of +h+, +min+, and
1457   # +sec+ are treating as counting backwards from the end of the
1458   # next larger unit (e.g. a +min+ of -2 is treated as 58).  No
1459   # wraparound is performed.  If an invalid time portion is specified,
1460   # an ArgumentError is raised.
1461   #
1462   # +of+ is the offset from UTC as a fraction of a day (defaults to 0).
1463   # +sg+ specifies the Day of Calendar Reform.
1464   #
1465   # +y+ defaults to -4712, and +d+ to 1; this is Julian Day Number
1466   # day 0.  The time values default to 0.
1467   def self.ordinal(y=-4712, d=1, h=0, min=0, s=0, of=0, sg=ITALY)
1468     unless (jd = valid_ordinal?(y, d, sg)) &&
1469            (fr = valid_time?(h, min, s))
1470       raise ArgumentError, 'invalid date'
1471     end
1472     if String === of
1473       of = (zone_to_diff(of) || 0).to_r/86400
1474     end
1475     new!(jd_to_ajd(jd, fr, of), of, sg)
1476   end
1478   # Create a new DateTime object corresponding to the specified
1479   # Civil Date and hour +h+, minute +min+, second +s+.
1480   #
1481   # The 24-hour clock is used.  Negative values of +h+, +min+, and
1482   # +sec+ are treating as counting backwards from the end of the
1483   # next larger unit (e.g. a +min+ of -2 is treated as 58).  No
1484   # wraparound is performed.  If an invalid time portion is specified,
1485   # an ArgumentError is raised.
1486   #
1487   # +of+ is the offset from UTC as a fraction of a day (defaults to 0).
1488   # +sg+ specifies the Day of Calendar Reform.
1489   #
1490   # +y+ defaults to -4712, +m+ to 1, and +d+ to 1; this is Julian Day
1491   # Number day 0.  The time values default to 0.
1492   def self.civil(y=-4712, m=1, d=1, h=0, min=0, s=0, of=0, sg=ITALY)
1493     unless (jd = valid_civil?(y, m, d, sg)) &&
1494            (fr = valid_time?(h, min, s))
1495       raise ArgumentError, 'invalid date'
1496     end
1497     if String === of
1498       of = (zone_to_diff(of) || 0).to_r/86400
1499     end
1500     new!(jd_to_ajd(jd, fr, of), of, sg)
1501   end
1503   class << self; alias_method :new, :civil end
1505   # Create a new DateTime object corresponding to the specified
1506   # Commercial Date and hour +h+, minute +min+, second +s+.
1507   #
1508   # The 24-hour clock is used.  Negative values of +h+, +min+, and
1509   # +sec+ are treating as counting backwards from the end of the
1510   # next larger unit (e.g. a +min+ of -2 is treated as 58).  No
1511   # wraparound is performed.  If an invalid time portion is specified,
1512   # an ArgumentError is raised.
1513   #
1514   # +of+ is the offset from UTC as a fraction of a day (defaults to 0).
1515   # +sg+ specifies the Day of Calendar Reform.
1516   #
1517   # +y+ defaults to 1582, +w+ to 41, and +d+ to 5; this is the Day of
1518   # Calendar Reform for Italy and the Catholic countries.
1519   # The time values default to 0.
1520   def self.commercial(y=1582, w=41, d=5, h=0, min=0, s=0, of=0, sg=ITALY)
1521     unless (jd = valid_commercial?(y, w, d, sg)) &&
1522            (fr = valid_time?(h, min, s))
1523       raise ArgumentError, 'invalid date'
1524     end
1525     if String === of
1526       of = (zone_to_diff(of) || 0).to_r/86400
1527     end
1528     new!(jd_to_ajd(jd, fr, of), of, sg)
1529   end
1531   def self.weeknum(y=1582, w=41, d=5, f=0, h=0, min=0, s=0, of=0, sg=ITALY) # :nodoc:
1532     unless (jd = valid_weeknum?(y, w, d, f, sg)) &&
1533            (fr = valid_time?(h, min, s))
1534       raise ArgumentError, 'invalid date'
1535     end
1536     if String === of
1537       of = (zone_to_diff(of) || 0).to_r/86400
1538     end
1539     new!(jd_to_ajd(jd, fr, of), of, sg)
1540   end
1542   private_class_method :weeknum
1544   def self.new_by_frags(elem, sg) # :nodoc:
1545     elem = rewrite_frags(elem)
1546     elem = complete_frags(elem)
1547     unless (jd = valid_date_frags?(elem, sg)) &&
1548            (fr = valid_time_frags?(elem))
1549       raise ArgumentError, 'invalid date'
1550     end
1551     sf = (elem[:sec_fraction] || 0)
1552     fr += sf/86400
1553     of = (elem[:offset] || 0)
1554     of = of.to_r/86400
1555     new!(jd_to_ajd(jd, fr, of), of, sg)
1556   end
1558   private_class_method :new_by_frags
1560   # Create a new DateTime object by parsing from a String
1561   # according to a specified format.
1562   #
1563   # +str+ is a String holding a date-time representation.
1564   # +fmt+ is the format that the date-time is in.  See
1565   # date/format.rb for details on supported formats.
1566   #
1567   # The default +str+ is '-4712-01-01T00:00:00+00:00', and the default
1568   # +fmt+ is '%FT%T%z'.  This gives midnight on Julian Day Number day 0.
1569   #
1570   # +sg+ specifies the Day of Calendar Reform.
1571   #
1572   # An ArgumentError will be raised if +str+ cannot be
1573   # parsed.
1574   def self.strptime(str='-4712-01-01T00:00:00+00:00', fmt='%FT%T%z', sg=ITALY)
1575     elem = _strptime(str, fmt)
1576     new_by_frags(elem, sg)
1577   end
1579   # Create a new DateTime object by parsing from a String,
1580   # without specifying the format.
1581   #
1582   # +str+ is a String holding a date-time representation.
1583   # +comp+ specifies whether to interpret 2-digit years
1584   # as 19XX (>= 69) or 20XX (< 69); the default is not to.
1585   # The method will attempt to parse a date-time from the String
1586   # using various heuristics; see #_parse in date/format.rb
1587   # for more details.  If parsing fails, an ArgumentError
1588   # will be raised.
1589   #
1590   # The default +str+ is '-4712-01-01T00:00:00+00:00'; this is Julian
1591   # Day Number day 0.
1592   #
1593   # +sg+ specifies the Day of Calendar Reform.
1594   def self.parse(str='-4712-01-01T00:00:00+00:00', comp=false, sg=ITALY)
1595     elem = _parse(str, comp)
1596     new_by_frags(elem, sg)
1597   end
1599   public :hour, :min, :sec, :sec_fraction, :zone, :offset, :new_offset
1603 class Time
1605 #  def to_time() getlocal end
1607   def to_date
1608     jd = Date.civil_to_jd(year, mon, mday, Date::ITALY)
1609     Date.new!(Date.jd_to_ajd(jd, 0, 0), 0, Date::ITALY)
1610   end
1612   def to_datetime
1613     jd = DateTime.civil_to_jd(year, mon, mday, DateTime::ITALY)
1614     fr = DateTime.time_to_day_fraction(hour, min, [sec, 59].min) +
1615          usec.to_r/86400000000
1616     of = utc_offset.to_r/86400
1617     DateTime.new!(DateTime.jd_to_ajd(jd, fr, of), of, DateTime::ITALY)
1618   end
1620   private :to_date, :to_datetime
1624 class Date
1626 =begin
1627   def to_time() Time.local(year, mon, mday) end
1628   def to_date() self end
1629   def to_datetime() DateTime.new!(self.class.jd_to_ajd(jd, 0, 0), @of, @sg) end
1630 =end
1632   # Create a new Date object representing today.
1633   #
1634   # +sg+ specifies the Day of Calendar Reform.
1635   def self.today(sg=ITALY) Time.now.__send__(:to_date)    .new_start(sg) end
1637   # Create a new DateTime object representing the current time.
1638   #
1639   # +sg+ specifies the Day of Calendar Reform.
1640   def self.now  (sg=ITALY) Time.now.__send__(:to_datetime).new_start(sg) end
1642   private_class_method :now
1646 class DateTime < Date
1648 =begin
1649   def to_time
1650     d = new_offset(0)
1651     d.instance_eval do
1652       Time.utc(year, mon, mday, hour, min, sec,
1653                (sec_fraction * 86400000000).to_i)
1654     end.
1655         getlocal
1656   end
1658   def to_date() Date.new!(self.class.jd_to_ajd(jd, 0, 0), 0, @sg) end
1659   def to_datetime() self end
1660 =end
1662   private_class_method :today
1663   public_class_method  :now
1667 class Date
1669   [ %w(os?      julian?),
1670     %w(ns?      gregorian?),
1671     %w(exist1?  valid_jd?),
1672     %w(exist2?  valid_ordinal?),
1673     %w(exist3?  valid_date?),
1674     %w(exist?   valid_date?),
1675     %w(existw?  valid_commercial?),
1676     %w(new0     new!),
1677     %w(new1     jd),
1678     %w(new2     ordinal),
1679     %w(new3     new),
1680     %w(neww     commercial)
1681   ].each do |old, new|
1682     module_eval <<-"end;"
1683       def self.#{old}(*args, &block)
1684         if $VERBOSE
1685           warn("\#{caller.shift.sub(/:in .*/, '')}: " \
1686                "warning: \#{self}::#{old} is deprecated; " \
1687                "use \#{self}::#{new}")
1688         end
1689         #{new}(*args, &block)
1690       end
1691     end;
1692   end
1694   [ %w(os?      julian?),
1695     %w(ns?      gregorian?),
1696     %w(sg       start),
1697     %w(newsg    new_start),
1698     %w(of       offset),
1699     %w(newof    new_offset)
1700   ].each do |old, new|
1701     module_eval <<-"end;"
1702       def #{old}(*args, &block)
1703         if $VERBOSE
1704           warn("\#{caller.shift.sub(/:in .*/, '')}: " \
1705                "warning: \#{self.class}\##{old} is deprecated; " \
1706                "use \#{self.class}\##{new}")
1707         end
1708         #{new}(*args, &block)
1709       end
1710     end;
1711   end
1713   private :of, :newof
1717 class DateTime < Date
1719   public :of, :newof