2 # date.rb - date and time library
4 # Author: Tadayoshi Funaba 1998-2006
6 # Documentation: William Webber <william@williamwebber.com>
9 # $Id: date.rb,v 2.30 2006-12-30 21:43:41+09 tadf Exp $
14 # This file provides two classes for working with
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
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.
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
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}"
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
194 # puts secs_to_new_year()
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.
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
256 class Infinity < Numeric # :nodoc:
260 def initialize(d=1) @d = d <=> 0 end
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
278 when Infinity; return d <=> other.d
279 when Numeric; return d
282 l, r = other.coerce(self)
292 when Numeric; return -d, d
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
306 ENGLAND = 2361222 # 1752-09-14
308 # A constant used to indicate that a Date should always use the
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)
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
340 def self.julian? (jd, sg)
346 warn("#{caller.shift.sub(/:in .*/, '')}: " \
347 "warning: do not use non-numerical object as julian day number anymore")
353 # Does a given Julian Day Number fall inside the new-style (Gregorian)
356 # The reverse of self.os? See the documentation for that method for
358 def self.gregorian? (jd, sg) !julian?(jd, sg) end
360 def self.fix_style(jd, sg) # :nodoc:
366 private_class_method :fix_style
368 # Convert an Ordinal Date to a Julian Day Number.
370 # +y+ and +d+ are the year and day-of-year to convert.
371 # +sg+ specifies the Day of Calendar Reform.
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)
378 # Convert a Julian Day Number to an Ordinal Date.
380 # +jd+ is the Julian Day Number to convert.
381 # +sg+ specifies the Day of Calendar Reform.
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))
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.
395 # Returns the corresponding Julian Day Number.
396 def self.civil_to_jd(y, m, d, sg=GREGORIAN)
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 +
412 # Convert a Julian Day Number to a Civil Date. +jd+ is
413 # the Julian Day Number. +sg+ specifies the Day of
416 # Returns the corresponding [year, month, day_of_month]
417 # as a three-element array.
418 def self.jd_to_civil(jd, sg=GREGORIAN)
422 x = ((jd - 1867216.25) / 36524.25).floor
423 a = jd + 1 + x - (x / 4.0).floor
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
440 # Convert a Commercial Date to a Julian Day Number.
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)) +
452 # Convert a Julian Day Number to a Commercial Date
454 # +jd+ is the Julian Day Number to convert.
455 # +sg+ specifies the Day of Calendar Reform.
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
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
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)
482 private_class_method :weeknum_to_jd, :jd_to_weeknum
484 # Convert an Astronomical Julian Day Number to a (civil) Julian
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).
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
497 # +jd+ is the Julian Day Number to convert, and +fr+ is a
499 # +of+ is the offset from UTC as a fraction of a day (defaults to 0).
501 # Returns the Astronomical Julian Day Number as a single
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)
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
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
530 def self.mjd_to_jd(mjd) mjd + 2400001 end
532 # Convert a Julian Day Number to a Modified Julian Day
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.
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?
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?
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?
565 # If it is, returns it. In fact, any value is treated as a valid
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
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.
581 # +sg+ specifies the Day of Calendar Reform.
582 def self.valid_ordinal? (y, d, sg=ITALY)
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)
590 jd = ordinal_to_jd(y, d, sg)
591 return unless [y, d] == jd_to_ordinal(jd, sg)
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.
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.
607 # +sg+ specifies the Day of Calendar Reform.
608 def self.valid_civil? (y, m, d, sg=ITALY)
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)
620 jd = civil_to_jd(y, m, d, sg)
621 return unless [y, m, d] == jd_to_civil(jd, sg)
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.
632 # Monday is day-of-week 1; Sunday is day-of-week 7.
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.
641 # +sg+ specifies the Day of Calendar Reform.
642 def self.valid_commercial? (y, w, d, sg=ITALY)
648 jd_to_commercial(commercial_to_jd(y + 1, 1, 1) + w * 7)
649 return unless ny == y
652 jd = commercial_to_jd(y, w, d)
653 return unless gregorian?(jd, sg)
654 return unless [y, w, d] == jd_to_commercial(jd)
658 def self.valid_weeknum? (y, w, d, f, sg=ITALY) # :nodoc:
664 jd_to_weeknum(weeknum_to_jd(y + 1, 1, f, f) + w * 7, f)
665 return unless ny == y
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)
674 private_class_method :valid_weeknum?
676 # Do hour +h+, minute +min+, and second +s+ constitute a valid time?
678 # If they do, returns their value as a fraction of a day. If not,
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)
689 return unless ((0..23) === h &&
695 time_to_day_fraction(h, min, s)
698 # Create a new Date object from a Julian Day Number.
700 # +jd+ is the Julian Day Number; if not specified, it defaults to
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)
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.
714 # +y+ defaults to -4712, and +d+ to 1; this is Julian Day
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'
722 new!(jd_to_ajd(jd, 0, 0), 0, sg)
725 # Create a new Date object for the Civil Date specified by
726 # year +y+, month +m+, and day-of-month +d+.
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.
734 # +y+ defaults to -4712, +m+ to 1, and +d+ to 1; this is
735 # Julian Day Number day 0.
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'
742 new!(jd_to_ajd(jd, 0, 0), 0, sg)
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+.
750 # Monday is day-of-week 1; Sunday is day-of-week 7.
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.
757 # +y+ defaults to 1582, +w+ to 41, and +d+ to 5, the Day of
758 # Calendar Reform for Italy and the Catholic countries.
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'
765 new!(jd_to_ajd(jd, 0, 0), 0, sg)
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'
772 new!(jd_to_ajd(jd, 0, 0), 0, sg)
775 private_class_method :weeknum
777 def self.rewrite_frags(elem) # :nodoc:
779 if seconds = elem[:seconds]
780 d, fr = seconds.divmod(86400)
781 h, fr = fr.divmod(3600)
782 min, fr = fr.divmod(60)
784 elem[:jd] = UNIX_EPOCH_IN_CJD + d
788 elem[:sec_fraction] = fr
789 elem.delete(:seconds)
795 private_class_method :rewrite_frags
797 def self.complete_frags(elem) # :nodoc:
799 g = [[:time, [:hour, :min, :sec]],
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
816 if g && g[0] && (g[1].size - g[2].size) != 0
821 elem[:year] ||= d.year
826 elem[e] = d.__send__(e)
833 elem[e] = d.__send__(e)
838 elem[:jd] ||= (d - d.wday + elem[:wday]).jd
842 elem[e] = d.__send__(e)
849 elem[e] = d.__send__(e)
856 if g && g[0] == :time
866 elem[:sec] = [elem[:sec], 59].min
871 private_class_method :complete_frags
873 def self.valid_date_frags?(elem, sg) # :nodoc:
875 a = elem.values_at(:jd)
877 if jd = valid_jd?(*(a << sg))
882 a = elem.values_at(:year, :yday)
884 if jd = valid_ordinal?(*(a << sg))
889 a = elem.values_at(:year, :mon, :mday)
891 if jd = valid_civil?(*(a << sg))
896 a = elem.values_at(:cwyear, :cweek, :cwday)
897 if a[2].nil? && elem[:wday]
898 a[2] = elem[:wday].nonzero? || 7
901 if jd = valid_commercial?(*(a << sg))
906 a = elem.values_at(:year, :wnum0, :wday)
907 if a[2].nil? && elem[:cwday]
908 a[2] = elem[:cwday] % 7
911 if jd = valid_weeknum?(*(a << 0 << sg))
916 a = elem.values_at(:year, :wnum1, :wday)
918 a[2] = (a[2] - 1) % 7
920 if a[2].nil? && elem[:cwday]
921 a[2] = (elem[:cwday] - 1) % 7
924 if jd = valid_weeknum?(*(a << 1 << sg))
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)
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'
946 new!(jd_to_ajd(jd, 0, 0), 0, sg)
949 private_class_method :new_by_frags
951 # Create a new Date object by parsing from a String
952 # according to a specified format.
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.
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.
962 # +sg+ specifies the Day of Calendar Reform.
964 # An ArgumentError will be raised if +str+ cannot be
966 def self.strptime(str='-4712-01-01', fmt='%F', sg=ITALY)
967 elem = _strptime(str, fmt)
968 new_by_frags(elem, sg)
971 # Create a new Date object by parsing from a String,
972 # without specifying the format.
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
982 # The default +str+ is '-4712-01-01'; this is Julian
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)
993 def once(*ids) # :nodoc:
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]
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.
1014 # fact an alias for #civil(): read the documentation for that
1017 # Create a new Date object.
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.
1023 # +sg+ specifies the Day of Calendar Reform to use for this
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.
1033 # Get the date as an Astronomical Modified Julian Day Number.
1034 def amjd() self.class.ajd_to_amjd(@ajd) end
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.
1073 # January 1 is day-of-the-year 1
1074 def yday() ordinal[1] end
1076 # Get the month of this date.
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:
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
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
1136 MONTHNAMES.each_with_index do |n, i|
1138 define_method(n.downcase + '?'){mon == i}
1142 DAYNAMES.each_with_index do |n, i|
1143 define_method(n.downcase + '?'){wday == i}
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:
1157 then self.class::JULIAN
1158 else self.class::GREGORIAN end
1163 # Is this a leap year?
1165 self.class.jd_to_civil(self.class.civil_to_jd(year, 3, 1, fix_style) - 1,
1166 fix_style)[-1] == 29
1171 # When is the Day of Calendar Reform for this Date object?
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
1187 def julian() new_start(self.class::JULIAN) end
1189 # Create a copy of this Date object that always uses the Gregorian
1191 def gregorian() new_start(self.class::GREGORIAN) end
1193 def offset() @of end
1195 def new_offset(of=0)
1197 of = (self.class.zone_to_diff(of) || 0).to_r/86400
1199 self.class.new!(@ajd, of, @sg)
1202 private :offset, :new_offset
1204 # Return a new Date object that is +n+ days later than the
1207 # +n+ may be a negative value, in which case the new Date
1208 # is earlier than the current one; however, #-() might be
1211 # If +n+ is not a Numeric, a TypeError will be thrown. In
1212 # particular, two Dates cannot be added to each other.
1215 when Numeric; return self.class.new!(@ajd + n, @of, @sg)
1217 raise TypeError, 'expected numeric'
1220 # If +x+ is a Numeric value, create a new Date object that is
1221 # +x+ days earlier than the current one.
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
1227 # If +x+ is neither Numeric nor a Date, a TypeError is raised.
1230 when Numeric; return self.class.new!(@ajd - x, @of, @sg)
1231 when Date; return @ajd - x.ajd
1233 raise TypeError, 'expected numeric or date'
1236 # Compare this date with another date.
1238 # +other+ can also be a Numeric value, in which case it is
1239 # interpreted as an Astronomical Julian Day Number.
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.
1249 when Numeric; return @ajd <=> other
1250 when Date; return @ajd <=> other.ajd
1255 # The relationship operator for Date.
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.
1263 when Numeric; return jd == other
1264 when Date; return jd == other.jd
1269 def next_day(n=1) self + n end
1270 # def prev_day(n=1) self - n end
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
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.
1286 y, m = (year * 12 + (mon - 1) + n).divmod(12)
1287 m, = (m + 1) .divmod(1)
1289 d -= 1 until jd2 = self.class.valid_civil?(y, m, d, fix_style)
1293 # Return a new Date object that is +n+ months earlier than
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
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
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
1318 return to_enum(:step, limit, step)
1322 op = %w(- <= >=)[step <=> 0]
1323 while da.__send__(op, limit)
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)
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)
1342 # Is this Date equal to +other+?
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.
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.
1365 a = Marshal.load(str)
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.
1386 # The following methods are defined in Date, but declared private
1387 # there. They are made public in DateTime. They are documented
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
1399 # Get the minute-of-the-hour of the time.
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.
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.
1418 # Get the time zone offset as a fraction of a day. This is returned
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+.
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.
1438 # +of+ is the offset from UTC as a fraction of a day (defaults to 0).
1439 # +sg+ specifies the Day of Calendar Reform.
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'
1448 of = (zone_to_diff(of) || 0).to_r/86400
1450 new!(jd_to_ajd(jd, fr, of), of, sg)
1453 # Create a new DateTime object corresponding to the specified
1454 # Ordinal Date and hour +h+, minute +min+, second +s+.
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.
1462 # +of+ is the offset from UTC as a fraction of a day (defaults to 0).
1463 # +sg+ specifies the Day of Calendar Reform.
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'
1473 of = (zone_to_diff(of) || 0).to_r/86400
1475 new!(jd_to_ajd(jd, fr, of), of, sg)
1478 # Create a new DateTime object corresponding to the specified
1479 # Civil Date and hour +h+, minute +min+, second +s+.
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.
1487 # +of+ is the offset from UTC as a fraction of a day (defaults to 0).
1488 # +sg+ specifies the Day of Calendar Reform.
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'
1498 of = (zone_to_diff(of) || 0).to_r/86400
1500 new!(jd_to_ajd(jd, fr, of), of, sg)
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+.
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.
1514 # +of+ is the offset from UTC as a fraction of a day (defaults to 0).
1515 # +sg+ specifies the Day of Calendar Reform.
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'
1526 of = (zone_to_diff(of) || 0).to_r/86400
1528 new!(jd_to_ajd(jd, fr, of), of, sg)
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'
1537 of = (zone_to_diff(of) || 0).to_r/86400
1539 new!(jd_to_ajd(jd, fr, of), of, sg)
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'
1551 sf = (elem[:sec_fraction] || 0)
1553 of = (elem[:offset] || 0)
1555 new!(jd_to_ajd(jd, fr, of), of, sg)
1558 private_class_method :new_by_frags
1560 # Create a new DateTime object by parsing from a String
1561 # according to a specified format.
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.
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.
1570 # +sg+ specifies the Day of Calendar Reform.
1572 # An ArgumentError will be raised if +str+ cannot be
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)
1579 # Create a new DateTime object by parsing from a String,
1580 # without specifying the format.
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
1590 # The default +str+ is '-4712-01-01T00:00:00+00:00'; this is Julian
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)
1599 public :hour, :min, :sec, :sec_fraction, :zone, :offset, :new_offset
1605 # def to_time() getlocal end
1608 jd = Date.civil_to_jd(year, mon, mday, Date::ITALY)
1609 Date.new!(Date.jd_to_ajd(jd, 0, 0), 0, Date::ITALY)
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)
1620 private :to_date, :to_datetime
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
1632 # Create a new Date object representing today.
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.
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
1652 Time.utc(year, mon, mday, hour, min, sec,
1653 (sec_fraction * 86400000000).to_i)
1658 def to_date() Date.new!(self.class.jd_to_ajd(jd, 0, 0), 0, @sg) end
1659 def to_datetime() self end
1662 private_class_method :today
1663 public_class_method :now
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?),
1681 ].each do |old, new|
1682 module_eval <<-"end;"
1683 def self.#{old}(*args, &block)
1685 warn("\#{caller.shift.sub(/:in .*/, '')}: " \
1686 "warning: \#{self}::#{old} is deprecated; " \
1687 "use \#{self}::#{new}")
1689 #{new}(*args, &block)
1697 %w(newsg new_start),
1699 %w(newof new_offset)
1700 ].each do |old, new|
1701 module_eval <<-"end;"
1702 def #{old}(*args, &block)
1704 warn("\#{caller.shift.sub(/:in .*/, '')}: " \
1705 "warning: \#{self.class}\##{old} is deprecated; " \
1706 "use \#{self.class}\##{new}")
1708 #{new}(*args, &block)
1717 class DateTime < Date