~ubuntu-branches/ubuntu/quantal/ruby1.9.1/quantal

« back to all changes in this revision

Viewing changes to lib/date.rb

  • Committer: Bazaar Package Importer
  • Author(s): Lucas Nussbaum
  • Date: 2011-09-24 19:16:17 UTC
  • mfrom: (1.1.8 upstream) (13.1.7 experimental)
  • Revision ID: james.westby@ubuntu.com-20110924191617-o1qz4rcmqjot8zuy
Tags: 1.9.3~rc1-1
* New upstream release: 1.9.3 RC1.
  + Includes load.c fixes. Closes: #639959.
* Upload to unstable.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#
2
 
# date.rb - date and time library
3
 
#
4
 
# Author: Tadayoshi Funaba 1998-2011
5
 
#
6
 
# Documentation: William Webber <william@williamwebber.com>
7
 
#
8
 
#--
9
 
# $Id: date.rb,v 2.37 2008-01-17 20:16:31+09 tadf Exp $
10
 
#++
11
 
#
12
 
# == Overview
13
 
#
14
 
# This file provides two classes for working with
15
 
# dates and times.
16
 
#
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.
20
 
#
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.
25
 
#
26
 
# === Ways of calculating the date.
27
 
#
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.
33
 
#
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.)
39
 
#
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.
49
 
#
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.
61
 
#
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.
70
 
#
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.
77
 
#
78
 
# === Calendar Reform
79
 
#
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*.
87
 
#
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*.
94
 
#
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.
100
 
#
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.)
115
 
#
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.
127
 
#
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.
131
 
#
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.
141
 
#
142
 
# Note: in the Julian Calendar, New Years Day was March 25.  The
143
 
# Date class does not follow this convention.
144
 
#
145
 
# === Time Zones
146
 
#
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.
156
 
#
157
 
# This simple representation of time zones does not take
158
 
# into account the common practice of Daylight Savings
159
 
# Time or Summer Time.
160
 
#
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.
165
 
#
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.
171
 
#
172
 
# == Examples of use
173
 
#
174
 
# === Print out the date of every Sunday between two dates.
175
 
#
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
182
 
#
183
 
#     print_sundays(Date::civil(2003, 4, 8), Date::civil(2003, 5, 23))
184
 
#
185
 
# === Calculate how many seconds to go till midnight on New Year's Day.
186
 
#
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
193
 
#
194
 
#     puts secs_to_new_year()
195
 
 
196
 
require 'date/format'
197
 
 
198
 
# Class representing a date.
199
 
#
200
 
# See the documentation to the file date.rb for an overview.
201
 
#
202
 
# Internally, the date is represented as an Astronomical
203
 
# Julian Day Number, +ajd+.  The Day of Calendar Reform, +sg+, is
204
 
# also stored, for conversions to other date formats.  (There
205
 
# is also an +of+ field for a time zone offset, but this
206
 
# is only for the use of the DateTime subclass.)
207
 
#
208
 
# A new Date object is created using one of the object creation
209
 
# class methods named after the corresponding date format, and the
210
 
# arguments appropriate to that date format; for instance,
211
 
# Date::civil() (aliased to Date::new()) with year, month,
212
 
# and day-of-month, or Date::ordinal() with year and day-of-year.
213
 
# All of these object creation class methods also take the
214
 
# Day of Calendar Reform as an optional argument.
215
 
#
216
 
# Date objects are immutable once created.
217
 
#
218
 
# Once a Date has been created, date values
219
 
# can be retrieved for the different date formats supported
220
 
# using instance methods.  For instance, #mon() gives the
221
 
# Civil month, #cwday() gives the Commercial day of the week,
222
 
# and #yday() gives the Ordinal day of the year.  Date values
223
 
# can be retrieved in any format, regardless of what format
224
 
# was used to create the Date instance.
225
 
#
226
 
# The Date class includes the Comparable module, allowing
227
 
# date objects to be compared and sorted, ranges of dates
228
 
# to be created, and so forth.
229
 
class Date
230
 
 
231
 
  include Comparable
232
 
 
233
 
  # Full month names, in English.  Months count from 1 to 12; a
234
 
  # month's numerical representation indexed into this array
235
 
  # gives the name of that month (hence the first element is nil).
236
 
  MONTHNAMES = [nil] + %w(January February March April May June July
237
 
                          August September October November December)
238
 
 
239
 
  # Full names of days of the week, in English.  Days of the week
240
 
  # count from 0 to 6 (except in the commercial week); a day's numerical
241
 
  # representation indexed into this array gives the name of that day.
242
 
  DAYNAMES = %w(Sunday Monday Tuesday Wednesday Thursday Friday Saturday)
243
 
 
244
 
  # Abbreviated month names, in English.
245
 
  ABBR_MONTHNAMES = [nil] + %w(Jan Feb Mar Apr May Jun
246
 
                               Jul Aug Sep Oct Nov Dec)
247
 
 
248
 
  # Abbreviated day names, in English.
249
 
  ABBR_DAYNAMES = %w(Sun Mon Tue Wed Thu Fri Sat)
250
 
 
251
 
  [MONTHNAMES, DAYNAMES, ABBR_MONTHNAMES, ABBR_DAYNAMES].each do |xs|
252
 
    xs.each{|x| x.freeze unless x.nil?}.freeze
253
 
  end
254
 
 
255
 
  class Infinity < Numeric # :nodoc:
256
 
 
257
 
    include Comparable
258
 
 
259
 
    def initialize(d=1) @d = d <=> 0 end
260
 
 
261
 
    def d() @d end
262
 
 
263
 
    protected :d
264
 
 
265
 
    def zero? () false end
266
 
    def finite? () false end
267
 
    def infinite? () d.nonzero? end
268
 
    def nan? () d.zero? end
269
 
 
270
 
    def abs() self.class.new end
271
 
 
272
 
    def -@ () self.class.new(-d) end
273
 
    def +@ () self.class.new(+d) end
274
 
 
275
 
    def <=> (other)
276
 
      case other
277
 
      when Infinity; return d <=> other.d
278
 
      when Numeric; return d
279
 
      else
280
 
        begin
281
 
          l, r = other.coerce(self)
282
 
          return l <=> r
283
 
        rescue NoMethodError
284
 
        end
285
 
      end
286
 
      nil
287
 
    end
288
 
 
289
 
    def coerce(other)
290
 
      case other
291
 
      when Numeric; return -d, d
292
 
      else
293
 
        super
294
 
      end
295
 
    end
296
 
 
297
 
  end
298
 
 
299
 
  # The Julian Day Number of the Day of Calendar Reform for Italy
300
 
  # and the Catholic countries.
301
 
  ITALY     = 2299161 # 1582-10-15
302
 
 
303
 
  # The Julian Day Number of the Day of Calendar Reform for England
304
 
  # and her Colonies.
305
 
  ENGLAND   = 2361222 # 1752-09-14
306
 
 
307
 
  # A constant used to indicate that a Date should always use the
308
 
  # Julian calendar.
309
 
  JULIAN    =  Infinity.new
310
 
 
311
 
  # A constant used to indicate that a Date should always use the
312
 
  # Gregorian calendar.
313
 
  GREGORIAN = -Infinity.new
314
 
 
315
 
  HALF_DAYS_IN_DAY       = Rational(1, 2) # :nodoc:
316
 
  HOURS_IN_DAY           = Rational(1, 24) # :nodoc:
317
 
  MINUTES_IN_DAY         = Rational(1, 1440) # :nodoc:
318
 
  SECONDS_IN_DAY         = Rational(1, 86400) # :nodoc:
319
 
  MILLISECONDS_IN_DAY    = Rational(1, 86400*10**3) # :nodoc:
320
 
  NANOSECONDS_IN_DAY     = Rational(1, 86400*10**9) # :nodoc:
321
 
  MILLISECONDS_IN_SECOND = Rational(1, 10**3) # :nodoc:
322
 
  NANOSECONDS_IN_SECOND  = Rational(1, 10**9) # :nodoc:
323
 
 
324
 
  MJD_EPOCH_IN_AJD       = Rational(4800001, 2) # 1858-11-17 # :nodoc:
325
 
  UNIX_EPOCH_IN_AJD      = Rational(4881175, 2) # 1970-01-01 # :nodoc:
326
 
  MJD_EPOCH_IN_CJD       = 2400001 # :nodoc:
327
 
  UNIX_EPOCH_IN_CJD      = 2440588 # :nodoc:
328
 
  LD_EPOCH_IN_CJD        = 2299160 # :nodoc:
329
 
 
330
 
  t = Module.new do
331
 
 
332
 
    private
333
 
 
334
 
    def find_fdoy(y, sg) # :nodoc:
335
 
      j = nil
336
 
      1.upto(31) do |d|
337
 
        break if j = _valid_civil?(y, 1, d, sg)
338
 
      end
339
 
      j
340
 
    end
341
 
 
342
 
    def find_ldoy(y, sg) # :nodoc:
343
 
      j = nil
344
 
      31.downto(1) do |d|
345
 
        break if j = _valid_civil?(y, 12, d, sg)
346
 
      end
347
 
      j
348
 
    end
349
 
 
350
 
    def find_fdom(y, m, sg) # :nodoc:
351
 
      j = nil
352
 
      1.upto(31) do |d|
353
 
        break if j = _valid_civil?(y, m, d, sg)
354
 
      end
355
 
      j
356
 
    end
357
 
 
358
 
    def find_ldom(y, m, sg) # :nodoc:
359
 
      j = nil
360
 
      31.downto(1) do |d|
361
 
        break if j = _valid_civil?(y, m, d, sg)
362
 
      end
363
 
      j
364
 
    end
365
 
 
366
 
    # Convert an Ordinal Date to a Julian Day Number.
367
 
    #
368
 
    # +y+ and +d+ are the year and day-of-year to convert.
369
 
    # +sg+ specifies the Day of Calendar Reform.
370
 
    #
371
 
    # Returns the corresponding Julian Day Number.
372
 
    def ordinal_to_jd(y, d, sg=GREGORIAN) # :nodoc:
373
 
      find_fdoy(y, sg) + d - 1
374
 
    end
375
 
 
376
 
    # Convert a Julian Day Number to an Ordinal Date.
377
 
    #
378
 
    # +jd+ is the Julian Day Number to convert.
379
 
    # +sg+ specifies the Day of Calendar Reform.
380
 
    #
381
 
    # Returns the corresponding Ordinal Date as
382
 
    # [year, day_of_year]
383
 
    def jd_to_ordinal(jd, sg=GREGORIAN) # :nodoc:
384
 
      y = jd_to_civil(jd, sg)[0]
385
 
      j = find_fdoy(y, sg)
386
 
      doy = jd - j + 1
387
 
      return y, doy
388
 
    end
389
 
 
390
 
    # Convert a Civil Date to a Julian Day Number.
391
 
    # +y+, +m+, and +d+ are the year, month, and day of the
392
 
    # month.  +sg+ specifies the Day of Calendar Reform.
393
 
    #
394
 
    # Returns the corresponding Julian Day Number.
395
 
    def civil_to_jd(y, m, d, sg=GREGORIAN) # :nodoc:
396
 
      if m <= 2
397
 
        y -= 1
398
 
        m += 12
399
 
      end
400
 
      a = (y / 100.0).floor
401
 
      b = 2 - a + (a / 4.0).floor
402
 
      jd = (365.25 * (y + 4716)).floor +
403
 
        (30.6001 * (m + 1)).floor +
404
 
        d + b - 1524
405
 
      if jd < sg
406
 
        jd -= b
407
 
      end
408
 
      jd
409
 
    end
410
 
 
411
 
    # Convert a Julian Day Number to a Civil Date.  +jd+ is
412
 
    # the Julian Day Number. +sg+ specifies the Day of
413
 
    # Calendar Reform.
414
 
    #
415
 
    # Returns the corresponding [year, month, day_of_month]
416
 
    # as a three-element array.
417
 
    def jd_to_civil(jd, sg=GREGORIAN) # :nodoc:
418
 
      if jd < sg
419
 
        a = jd
420
 
      else
421
 
        x = ((jd - 1867216.25) / 36524.25).floor
422
 
        a = jd + 1 + x - (x / 4.0).floor
423
 
      end
424
 
      b = a + 1524
425
 
      c = ((b - 122.1) / 365.25).floor
426
 
      d = (365.25 * c).floor
427
 
      e = ((b - d) / 30.6001).floor
428
 
      dom = b - d - (30.6001 * e).floor
429
 
      if e <= 13
430
 
        m = e - 1
431
 
        y = c - 4716
432
 
      else
433
 
        m = e - 13
434
 
        y = c - 4715
435
 
      end
436
 
      return y, m, dom
437
 
    end
438
 
 
439
 
    # Convert a Commercial Date to a Julian Day Number.
440
 
    #
441
 
    # +y+, +w+, and +d+ are the (commercial) year, week of the year,
442
 
    # and day of the week of the Commercial Date to convert.
443
 
    # +sg+ specifies the Day of Calendar Reform.
444
 
    def commercial_to_jd(y, w, d, sg=GREGORIAN) # :nodoc:
445
 
      j = find_fdoy(y, sg) + 3
446
 
      (j - (((j - 1) + 1) % 7)) +
447
 
        7 * (w - 1) +
448
 
        (d - 1)
449
 
    end
450
 
 
451
 
    # Convert a Julian Day Number to a Commercial Date
452
 
    #
453
 
    # +jd+ is the Julian Day Number to convert.
454
 
    # +sg+ specifies the Day of Calendar Reform.
455
 
    #
456
 
    # Returns the corresponding Commercial Date as
457
 
    # [commercial_year, week_of_year, day_of_week]
458
 
    def jd_to_commercial(jd, sg=GREGORIAN) # :nodoc:
459
 
      a = jd_to_civil(jd - 3, sg)[0]
460
 
      y = if jd >= commercial_to_jd(a + 1, 1, 1, sg) then a + 1 else a end
461
 
      w = 1 + ((jd - commercial_to_jd(y, 1, 1, sg)) / 7).floor
462
 
      d = (jd + 1) % 7
463
 
      d = 7 if d == 0
464
 
      return y, w, d
465
 
    end
466
 
 
467
 
    def weeknum_to_jd(y, w, d, f=0, sg=GREGORIAN) # :nodoc:
468
 
      a = find_fdoy(y, sg) + 6
469
 
      (a - ((a - f) + 1) % 7 - 7) + 7 * w + d
470
 
    end
471
 
 
472
 
    def jd_to_weeknum(jd, f=0, sg=GREGORIAN) # :nodoc:
473
 
      y, m, d = jd_to_civil(jd, sg)
474
 
      a = find_fdoy(y, sg) + 6
475
 
      w, d = (jd - (a - ((a - f) + 1) % 7) + 7).divmod(7)
476
 
      return y, w, d
477
 
    end
478
 
 
479
 
    def nth_kday_to_jd(y, m, n, k, sg=GREGORIAN) # :nodoc:
480
 
      j = if n > 0
481
 
            find_fdom(y, m, sg) - 1
482
 
          else
483
 
            find_ldom(y, m, sg) + 7
484
 
          end
485
 
      (j - (((j - k) + 1) % 7)) + 7 * n
486
 
    end
487
 
 
488
 
    def jd_to_nth_kday(jd, sg=GREGORIAN) # :nodoc:
489
 
      y, m, d = jd_to_civil(jd, sg)
490
 
      j = find_fdom(y, m, sg)
491
 
      return y, m, ((jd - j) / 7).floor + 1, jd_to_wday(jd)
492
 
    end
493
 
 
494
 
    # Convert an Astronomical Julian Day Number to a (civil) Julian
495
 
    # Day Number.
496
 
    #
497
 
    # +ajd+ is the Astronomical Julian Day Number to convert.
498
 
    # +of+ is the offset from UTC as a fraction of a day (defaults to 0).
499
 
    #
500
 
    # Returns the (civil) Julian Day Number as [day_number,
501
 
    # fraction] where +fraction+ is always 1/2.
502
 
    def ajd_to_jd(ajd, of=0) (ajd + of + HALF_DAYS_IN_DAY).divmod(1) end # :nodoc:
503
 
 
504
 
    # Convert a (civil) Julian Day Number to an Astronomical Julian
505
 
    # Day Number.
506
 
    #
507
 
    # +jd+ is the Julian Day Number to convert, and +fr+ is a
508
 
    # fractional day.
509
 
    # +of+ is the offset from UTC as a fraction of a day (defaults to 0).
510
 
    #
511
 
    # Returns the Astronomical Julian Day Number as a single
512
 
    # numeric value.
513
 
    def jd_to_ajd(jd, fr, of=0) jd + fr - of - HALF_DAYS_IN_DAY end # :nodoc:
514
 
 
515
 
    # Convert a fractional day +fr+ to [hours, minutes, seconds,
516
 
    # fraction_of_a_second]
517
 
    def day_fraction_to_time(fr) # :nodoc:
518
 
      ss,  fr = fr.divmod(SECONDS_IN_DAY) # 4p
519
 
      h,   ss = ss.divmod(3600)
520
 
      min, s  = ss.divmod(60)
521
 
      return h, min, s, fr * 86400
522
 
    end
523
 
 
524
 
    # Convert an +h+ hour, +min+ minutes, +s+ seconds period
525
 
    # to a fractional day.
526
 
    begin
527
 
      Rational(Rational(1, 2), 2) # a challenge
528
 
 
529
 
      def time_to_day_fraction(h, min, s)
530
 
        Rational(h * 3600 + min * 60 + s, 86400) # 4p
531
 
      end
532
 
    rescue
533
 
      def time_to_day_fraction(h, min, s)
534
 
        if Integer === h && Integer === min && Integer === s
535
 
          Rational(h * 3600 + min * 60 + s, 86400) # 4p
536
 
        else
537
 
          (h * 3600 + min * 60 + s).to_r/86400 # 4p
538
 
        end
539
 
      end
540
 
    end
541
 
 
542
 
    # Convert an Astronomical Modified Julian Day Number to an
543
 
    # Astronomical Julian Day Number.
544
 
    def amjd_to_ajd(amjd) amjd + MJD_EPOCH_IN_AJD end # :nodoc:
545
 
 
546
 
    # Convert an Astronomical Julian Day Number to an
547
 
    # Astronomical Modified Julian Day Number.
548
 
    def ajd_to_amjd(ajd) ajd - MJD_EPOCH_IN_AJD end # :nodoc:
549
 
 
550
 
    # Convert a Modified Julian Day Number to a Julian
551
 
    # Day Number.
552
 
    def mjd_to_jd(mjd) mjd + MJD_EPOCH_IN_CJD end # :nodoc:
553
 
 
554
 
    # Convert a Julian Day Number to a Modified Julian Day
555
 
    # Number.
556
 
    def jd_to_mjd(jd) jd - MJD_EPOCH_IN_CJD end # :nodoc:
557
 
 
558
 
    # Convert a count of the number of days since the adoption
559
 
    # of the Gregorian Calendar (in Italy) to a Julian Day Number.
560
 
    def ld_to_jd(ld) ld +  LD_EPOCH_IN_CJD end # :nodoc:
561
 
 
562
 
    # Convert a Julian Day Number to the number of days since
563
 
    # the adoption of the Gregorian Calendar (in Italy).
564
 
    def jd_to_ld(jd) jd -  LD_EPOCH_IN_CJD end # :nodoc:
565
 
 
566
 
    # Convert a Julian Day Number to the day of the week.
567
 
    #
568
 
    # Sunday is day-of-week 0; Saturday is day-of-week 6.
569
 
    def jd_to_wday(jd) (jd + 1) % 7 end # :nodoc:
570
 
 
571
 
    # Is +jd+ a valid Julian Day Number?
572
 
    #
573
 
    # If it is, returns it.  In fact, any value is treated as a valid
574
 
    # Julian Day Number.
575
 
    def _valid_jd? (jd, sg=GREGORIAN) jd end # :nodoc:
576
 
 
577
 
    # Do the year +y+ and day-of-year +d+ make a valid Ordinal Date?
578
 
    # Returns the corresponding Julian Day Number if they do, or
579
 
    # nil if they don't.
580
 
    #
581
 
    # +d+ can be a negative number, in which case it counts backwards
582
 
    # from the end of the year (-1 being the last day of the year).
583
 
    # No year wraparound is performed, however, so valid values of
584
 
    # +d+ are -365 .. -1, 1 .. 365 on a non-leap-year,
585
 
    # -366 .. -1, 1 .. 366 on a leap year.
586
 
    # A date falling in the period skipped in the Day of Calendar Reform
587
 
    # adjustment is not valid.
588
 
    #
589
 
    # +sg+ specifies the Day of Calendar Reform.
590
 
    def _valid_ordinal? (y, d, sg=GREGORIAN) # :nodoc:
591
 
      if d < 0
592
 
        return unless j = find_ldoy(y, sg)
593
 
        ny, nd = jd_to_ordinal(j + d + 1, sg)
594
 
        return unless ny == y
595
 
        d = nd
596
 
      end
597
 
      jd = ordinal_to_jd(y, d, sg)
598
 
      return unless [y, d] == jd_to_ordinal(jd, sg)
599
 
      jd
600
 
    end
601
 
 
602
 
    # Do year +y+, month +m+, and day-of-month +d+ make a
603
 
    # valid Civil Date?  Returns the corresponding Julian
604
 
    # Day Number if they do, nil if they don't.
605
 
    #
606
 
    # +m+ and +d+ can be negative, in which case they count
607
 
    # backwards from the end of the year and the end of the
608
 
    # month respectively.  No wraparound is performed, however,
609
 
    # and invalid values cause an ArgumentError to be raised.
610
 
    # A date falling in the period skipped in the Day of Calendar
611
 
    # Reform adjustment is not valid.
612
 
    #
613
 
    # +sg+ specifies the Day of Calendar Reform.
614
 
    def _valid_civil? (y, m, d, sg=GREGORIAN) # :nodoc:
615
 
      if m < 0
616
 
        m += 13
617
 
      end
618
 
      if d < 0
619
 
        return unless j = find_ldom(y, m, sg)
620
 
        ny, nm, nd = jd_to_civil(j + d + 1, sg)
621
 
        return unless [ny, nm] == [y, m]
622
 
        d = nd
623
 
      end
624
 
      jd = civil_to_jd(y, m, d, sg)
625
 
      return unless [y, m, d] == jd_to_civil(jd, sg)
626
 
      jd
627
 
    end
628
 
 
629
 
    # Do year +y+, week-of-year +w+, and day-of-week +d+ make a
630
 
    # valid Commercial Date?  Returns the corresponding Julian
631
 
    # Day Number if they do, nil if they don't.
632
 
    #
633
 
    # Monday is day-of-week 1; Sunday is day-of-week 7.
634
 
    #
635
 
    # +w+ and +d+ can be negative, in which case they count
636
 
    # backwards from the end of the year and the end of the
637
 
    # week respectively.  No wraparound is performed, however,
638
 
    # and invalid values cause an ArgumentError to be raised.
639
 
    # A date falling in the period skipped in the Day of Calendar
640
 
    # Reform adjustment is not valid.
641
 
    #
642
 
    # +sg+ specifies the Day of Calendar Reform.
643
 
    def _valid_commercial? (y, w, d, sg=GREGORIAN) # :nodoc:
644
 
      if d < 0
645
 
        d += 8
646
 
      end
647
 
      if w < 0
648
 
        ny, nw, nd =
649
 
          jd_to_commercial(commercial_to_jd(y + 1, 1, 1, sg) + w * 7, sg)
650
 
        return unless ny == y
651
 
        w = nw
652
 
      end
653
 
      jd = commercial_to_jd(y, w, d, sg)
654
 
      return unless [y, w, d] == jd_to_commercial(jd, sg)
655
 
      jd
656
 
    end
657
 
 
658
 
    def _valid_weeknum? (y, w, d, f, sg=GREGORIAN) # :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, sg) + w * 7, f, sg)
665
 
        return unless ny == y
666
 
        w = nw
667
 
      end
668
 
      jd = weeknum_to_jd(y, w, d, f, sg)
669
 
      return unless [y, w, d] == jd_to_weeknum(jd, f, sg)
670
 
      jd
671
 
    end
672
 
 
673
 
    def _valid_nth_kday? (y, m, n, k, sg=GREGORIAN) # :nodoc:
674
 
      if k < 0
675
 
        k += 7
676
 
      end
677
 
      if n < 0
678
 
        ny, nm = (y * 12 + m).divmod(12)
679
 
        nm,    = (nm + 1)    .divmod(1)
680
 
        ny, nm, nn, nk =
681
 
          jd_to_nth_kday(nth_kday_to_jd(ny, nm, 1, k, sg) + n * 7, sg)
682
 
        return unless [ny, nm] == [y, m]
683
 
        n = nn
684
 
      end
685
 
      jd = nth_kday_to_jd(y, m, n, k, sg)
686
 
      return unless [y, m, n, k] == jd_to_nth_kday(jd, sg)
687
 
      jd
688
 
    end
689
 
 
690
 
    # Do hour +h+, minute +min+, and second +s+ constitute a valid time?
691
 
    #
692
 
    # If they do, returns their value as a fraction of a day.  If not,
693
 
    # returns nil.
694
 
    #
695
 
    # The 24-hour clock is used.  Negative values of +h+, +min+, and
696
 
    # +sec+ are treating as counting backwards from the end of the
697
 
    # next larger unit (e.g. a +min+ of -2 is treated as 58).  No
698
 
    # wraparound is performed.
699
 
    def _valid_time? (h, min, s) # :nodoc:
700
 
      h   += 24 if h   < 0
701
 
      min += 60 if min < 0
702
 
      s   += 60 if s   < 0
703
 
      return unless ((0...24) === h &&
704
 
                     (0...60) === min &&
705
 
                     (0...60) === s) ||
706
 
                     (24 == h &&
707
 
                       0 == min &&
708
 
                       0 == s)
709
 
      time_to_day_fraction(h, min, s)
710
 
    end
711
 
 
712
 
  end
713
 
 
714
 
  extend  t
715
 
  include t
716
 
 
717
 
  # Is a year a leap year in the Julian calendar?
718
 
  #
719
 
  # All years divisible by 4 are leap years in the Julian calendar.
720
 
  def self.julian_leap? (y) y % 4 == 0 end
721
 
 
722
 
  # Is a year a leap year in the Gregorian calendar?
723
 
  #
724
 
  # All years divisible by 4 are leap years in the Gregorian calendar,
725
 
  # except for years divisible by 100 and not by 400.
726
 
  def self.gregorian_leap? (y) y % 4 == 0 && y % 100 != 0 || y % 400 == 0 end
727
 
 
728
 
  class << self; alias_method :leap?, :gregorian_leap? end
729
 
  class << self; alias_method :new!, :new end
730
 
 
731
 
  def self.valid_jd? (jd, sg=ITALY)
732
 
    !!_valid_jd?(jd, sg)
733
 
  end
734
 
 
735
 
  def self.valid_ordinal? (y, d, sg=ITALY)
736
 
    !!_valid_ordinal?(y, d, sg)
737
 
  end
738
 
 
739
 
  def self.valid_civil? (y, m, d, sg=ITALY)
740
 
    !!_valid_civil?(y, m, d, sg)
741
 
  end
742
 
 
743
 
  class << self; alias_method :valid_date?, :valid_civil? end
744
 
 
745
 
  def self.valid_commercial? (y, w, d, sg=ITALY)
746
 
    !!_valid_commercial?(y, w, d, sg)
747
 
  end
748
 
 
749
 
  def self.valid_weeknum? (y, w, d, f, sg=ITALY) # :nodoc:
750
 
    !!_valid_weeknum?(y, w, d, f, sg)
751
 
  end
752
 
 
753
 
  private_class_method :valid_weeknum?
754
 
 
755
 
  def self.valid_nth_kday? (y, m, n, k, sg=ITALY) # :nodoc:
756
 
    !!_valid_nth_kday?(y, m, n, k, sg)
757
 
  end
758
 
 
759
 
  private_class_method :valid_nth_kday?
760
 
 
761
 
  def self.valid_time? (h, min, s) # :nodoc:
762
 
    !!_valid_time?(h, min, s)
763
 
  end
764
 
 
765
 
  private_class_method :valid_time?
766
 
 
767
 
  # Create a new Date object from a Julian Day Number.
768
 
  #
769
 
  # +jd+ is the Julian Day Number; if not specified, it defaults to
770
 
  # 0.
771
 
  # +sg+ specifies the Day of Calendar Reform.
772
 
  def self.jd(jd=0, sg=ITALY)
773
 
    jd = _valid_jd?(jd, sg)
774
 
    new!(jd_to_ajd(jd, 0, 0), 0, sg)
775
 
  end
776
 
 
777
 
  # Create a new Date object from an Ordinal Date, specified
778
 
  # by year +y+ and day-of-year +d+. +d+ can be negative,
779
 
  # in which it counts backwards from the end of the year.
780
 
  # No year wraparound is performed, however.  An invalid
781
 
  # value for +d+ results in an ArgumentError being raised.
782
 
  #
783
 
  # +y+ defaults to -4712, and +d+ to 1; this is Julian Day
784
 
  # Number day 0.
785
 
  #
786
 
  # +sg+ specifies the Day of Calendar Reform.
787
 
  def self.ordinal(y=-4712, d=1, sg=ITALY)
788
 
    unless jd = _valid_ordinal?(y, d, sg)
789
 
      raise ArgumentError, 'invalid date'
790
 
    end
791
 
    new!(jd_to_ajd(jd, 0, 0), 0, sg)
792
 
  end
793
 
 
794
 
  # Create a new Date object for the Civil Date specified by
795
 
  # year +y+, month +m+, and day-of-month +d+.
796
 
  #
797
 
  # +m+ and +d+ can be negative, in which case they count
798
 
  # backwards from the end of the year and the end of the
799
 
  # month respectively.  No wraparound is performed, however,
800
 
  # and invalid values cause an ArgumentError to be raised.
801
 
  # can be negative
802
 
  #
803
 
  # +y+ defaults to -4712, +m+ to 1, and +d+ to 1; this is
804
 
  # Julian Day Number day 0.
805
 
  #
806
 
  # +sg+ specifies the Day of Calendar Reform.
807
 
  def self.civil(y=-4712, m=1, d=1, sg=ITALY)
808
 
    unless jd = _valid_civil?(y, m, d, sg)
809
 
      raise ArgumentError, 'invalid date'
810
 
    end
811
 
    new!(jd_to_ajd(jd, 0, 0), 0, sg)
812
 
  end
813
 
 
814
 
  class << self; alias_method :new, :civil end
815
 
 
816
 
  # Create a new Date object for the Commercial Date specified by
817
 
  # year +y+, week-of-year +w+, and day-of-week +d+.
818
 
  #
819
 
  # Monday is day-of-week 1; Sunday is day-of-week 7.
820
 
  #
821
 
  # +w+ and +d+ can be negative, in which case they count
822
 
  # backwards from the end of the year and the end of the
823
 
  # week respectively.  No wraparound is performed, however,
824
 
  # and invalid values cause an ArgumentError to be raised.
825
 
  #
826
 
  # +y+ defaults to -4712, +w+ to 1, and +d+ to 1; this is
827
 
  # Julian Day Number day 0.
828
 
  #
829
 
  # +sg+ specifies the Day of Calendar Reform.
830
 
  def self.commercial(y=-4712, w=1, d=1, sg=ITALY)
831
 
    unless jd = _valid_commercial?(y, w, d, sg)
832
 
      raise ArgumentError, 'invalid date'
833
 
    end
834
 
    new!(jd_to_ajd(jd, 0, 0), 0, sg)
835
 
  end
836
 
 
837
 
  def self.weeknum(y=-4712, w=0, d=1, f=0, sg=ITALY)
838
 
    unless jd = _valid_weeknum?(y, w, d, f, sg)
839
 
      raise ArgumentError, 'invalid date'
840
 
    end
841
 
    new!(jd_to_ajd(jd, 0, 0), 0, sg)
842
 
  end
843
 
 
844
 
  private_class_method :weeknum
845
 
 
846
 
  def self.nth_kday(y=-4712, m=1, n=1, k=1, sg=ITALY)
847
 
    unless jd = _valid_nth_kday?(y, m, n, k, sg)
848
 
      raise ArgumentError, 'invalid date'
849
 
    end
850
 
    new!(jd_to_ajd(jd, 0, 0), 0, sg)
851
 
  end
852
 
 
853
 
  private_class_method :nth_kday
854
 
 
855
 
  def self.rewrite_frags(elem) # :nodoc:
856
 
    elem ||= {}
857
 
    if seconds = elem[:seconds]
858
 
      d,   fr = seconds.divmod(86400)
859
 
      h,   fr = fr.divmod(3600)
860
 
      min, fr = fr.divmod(60)
861
 
      s,   fr = fr.divmod(1)
862
 
      elem[:jd] = UNIX_EPOCH_IN_CJD + d
863
 
      elem[:hour] = h
864
 
      elem[:min] = min
865
 
      elem[:sec] = s
866
 
      elem[:sec_fraction] = fr
867
 
      elem.delete(:seconds)
868
 
      elem.delete(:offset)
869
 
    end
870
 
    elem
871
 
  end
872
 
 
873
 
  private_class_method :rewrite_frags
874
 
 
875
 
  def self.complete_frags(elem) # :nodoc:
876
 
    i = 0
877
 
    g = [[:time, [:hour, :min, :sec]],
878
 
         [nil, [:jd]],
879
 
         [:ordinal, [:year, :yday, :hour, :min, :sec]],
880
 
         [:civil, [:year, :mon, :mday, :hour, :min, :sec]],
881
 
         [:commercial, [:cwyear, :cweek, :cwday, :hour, :min, :sec]],
882
 
         [:wday, [:wday, :hour, :min, :sec]],
883
 
         [:wnum0, [:year, :wnum0, :wday, :hour, :min, :sec]],
884
 
         [:wnum1, [:year, :wnum1, :wday, :hour, :min, :sec]],
885
 
         [nil, [:cwyear, :cweek, :wday, :hour, :min, :sec]],
886
 
         [nil, [:year, :wnum0, :cwday, :hour, :min, :sec]],
887
 
         [nil, [:year, :wnum1, :cwday, :hour, :min, :sec]]].
888
 
      collect{|k, a| e = elem.values_at(*a).compact; [k, a, e]}.
889
 
      select{|k, a, e| e.size > 0}.
890
 
      sort_by{|k, a, e| [e.size, i -= 1]}.last
891
 
 
892
 
    d = nil
893
 
 
894
 
    if g && g[0] && (g[1].size - g[2].size) != 0
895
 
      d ||= Date.today
896
 
 
897
 
      case g[0]
898
 
      when :ordinal
899
 
        elem[:year] ||= d.year
900
 
        elem[:yday] ||= 1
901
 
      when :civil
902
 
        g[1].each do |e|
903
 
          break if elem[e]
904
 
          elem[e] = d.__send__(e)
905
 
        end
906
 
        elem[:mon]  ||= 1
907
 
        elem[:mday] ||= 1
908
 
      when :commercial
909
 
        g[1].each do |e|
910
 
          break if elem[e]
911
 
          elem[e] = d.__send__(e)
912
 
        end
913
 
        elem[:cweek] ||= 1
914
 
        elem[:cwday] ||= 1
915
 
      when :wday
916
 
        elem[:jd] ||= (d - d.wday + elem[:wday]).jd
917
 
      when :wnum0
918
 
        g[1].each do |e|
919
 
          break if elem[e]
920
 
          elem[e] = d.__send__(e)
921
 
        end
922
 
        elem[:wnum0] ||= 0
923
 
        elem[:wday]  ||= 0
924
 
      when :wnum1
925
 
        g[1].each do |e|
926
 
          break if elem[e]
927
 
          elem[e] = d.__send__(e)
928
 
        end
929
 
        elem[:wnum1] ||= 0
930
 
        elem[:wday]  ||= 1
931
 
      end
932
 
    end
933
 
 
934
 
    if g && g[0] == :time
935
 
      if self <= DateTime
936
 
        d ||= Date.today
937
 
        elem[:jd] ||= d.jd
938
 
      end
939
 
    end
940
 
 
941
 
    elem[:hour] ||= 0
942
 
    elem[:min]  ||= 0
943
 
    elem[:sec]  ||= 0
944
 
    elem[:sec] = [elem[:sec], 59].min
945
 
 
946
 
    elem
947
 
  end
948
 
 
949
 
  private_class_method :complete_frags
950
 
 
951
 
  def self.valid_date_frags?(elem, sg) # :nodoc:
952
 
    catch :jd do
953
 
      a = elem.values_at(:jd)
954
 
      if a.all?
955
 
        if jd = _valid_jd?(*(a << sg))
956
 
          throw :jd, jd
957
 
        end
958
 
      end
959
 
 
960
 
      a = elem.values_at(:year, :yday)
961
 
      if a.all?
962
 
        if jd = _valid_ordinal?(*(a << sg))
963
 
          throw :jd, jd
964
 
        end
965
 
      end
966
 
 
967
 
      a = elem.values_at(:year, :mon, :mday)
968
 
      if a.all?
969
 
        if jd = _valid_civil?(*(a << sg))
970
 
          throw :jd, jd
971
 
        end
972
 
      end
973
 
 
974
 
      a = elem.values_at(:cwyear, :cweek, :cwday)
975
 
      if a[2].nil? && elem[:wday]
976
 
        a[2] = elem[:wday].nonzero? || 7
977
 
      end
978
 
      if a.all?
979
 
        if jd = _valid_commercial?(*(a << sg))
980
 
          throw :jd, jd
981
 
        end
982
 
      end
983
 
 
984
 
      a = elem.values_at(:year, :wnum0, :wday)
985
 
      if a[2].nil? && elem[:cwday]
986
 
        a[2] = elem[:cwday] % 7
987
 
      end
988
 
      if a.all?
989
 
        if jd = _valid_weeknum?(*(a << 0 << sg))
990
 
          throw :jd, jd
991
 
        end
992
 
      end
993
 
 
994
 
      a = elem.values_at(:year, :wnum1, :wday)
995
 
      if a[2]
996
 
        a[2] = (a[2] - 1) % 7
997
 
      end
998
 
      if a[2].nil? && elem[:cwday]
999
 
        a[2] = (elem[:cwday] - 1) % 7
1000
 
      end
1001
 
      if a.all?
1002
 
        if jd = _valid_weeknum?(*(a << 1 << sg))
1003
 
          throw :jd, jd
1004
 
        end
1005
 
      end
1006
 
    end
1007
 
  end
1008
 
 
1009
 
  private_class_method :valid_date_frags?
1010
 
 
1011
 
  def self.valid_time_frags? (elem) # :nodoc:
1012
 
    h, min, s = elem.values_at(:hour, :min, :sec)
1013
 
    _valid_time?(h, min, s)
1014
 
  end
1015
 
 
1016
 
  private_class_method :valid_time_frags?
1017
 
 
1018
 
  def self.new_by_frags(elem, sg) # :nodoc:
1019
 
    elem = rewrite_frags(elem)
1020
 
    elem = complete_frags(elem)
1021
 
    unless jd = valid_date_frags?(elem, sg)
1022
 
      raise ArgumentError, 'invalid date'
1023
 
    end
1024
 
    new!(jd_to_ajd(jd, 0, 0), 0, sg)
1025
 
  end
1026
 
 
1027
 
  private_class_method :new_by_frags
1028
 
 
1029
 
  # Create a new Date object by parsing from a String
1030
 
  # according to a specified format.
1031
 
  #
1032
 
  # +str+ is a String holding a date representation.
1033
 
  # +fmt+ is the format that the date is in.  See
1034
 
  # date/format.rb for details on supported formats.
1035
 
  #
1036
 
  # The default +str+ is '-4712-01-01', and the default
1037
 
  # +fmt+ is '%F', which means Year-Month-Day_of_Month.
1038
 
  # This gives Julian Day Number day 0.
1039
 
  #
1040
 
  # +sg+ specifies the Day of Calendar Reform.
1041
 
  #
1042
 
  # An ArgumentError will be raised if +str+ cannot be
1043
 
  # parsed.
1044
 
  def self.strptime(str='-4712-01-01', fmt='%F', sg=ITALY)
1045
 
    elem = _strptime(str, fmt)
1046
 
    new_by_frags(elem, sg)
1047
 
  end
1048
 
 
1049
 
  # Create a new Date object by parsing from a String,
1050
 
  # without specifying the format.
1051
 
  #
1052
 
  # +str+ is a String holding a date representation.
1053
 
  # +comp+ specifies whether to interpret 2-digit years
1054
 
  # as 19XX (>= 69) or 20XX (< 69); the default is not to.
1055
 
  # The method will attempt to parse a date from the String
1056
 
  # using various heuristics; see #_parse in date/format.rb
1057
 
  # for more details.  If parsing fails, an ArgumentError
1058
 
  # will be raised.
1059
 
  #
1060
 
  # The default +str+ is '-4712-01-01'; this is Julian
1061
 
  # Day Number day 0.
1062
 
  #
1063
 
  # +sg+ specifies the Day of Calendar Reform.
1064
 
  def self.parse(str='-4712-01-01', comp=true, sg=ITALY)
1065
 
    elem = _parse(str, comp)
1066
 
    new_by_frags(elem, sg)
1067
 
  end
1068
 
 
1069
 
  def self.iso8601(str='-4712-01-01', sg=ITALY) # :nodoc:
1070
 
    elem = _iso8601(str)
1071
 
    new_by_frags(elem, sg)
1072
 
  end
1073
 
 
1074
 
  def self.rfc3339(str='-4712-01-01T00:00:00+00:00', sg=ITALY) # :nodoc:
1075
 
    elem = _rfc3339(str)
1076
 
    new_by_frags(elem, sg)
1077
 
  end
1078
 
 
1079
 
  def self.xmlschema(str='-4712-01-01', sg=ITALY) # :nodoc:
1080
 
    elem = _xmlschema(str)
1081
 
    new_by_frags(elem, sg)
1082
 
  end
1083
 
 
1084
 
  def self.rfc2822(str='Mon, 1 Jan -4712 00:00:00 +0000', sg=ITALY) # :nodoc:
1085
 
    elem = _rfc2822(str)
1086
 
    new_by_frags(elem, sg)
1087
 
  end
1088
 
 
1089
 
  class << self; alias_method :rfc822, :rfc2822 end
1090
 
 
1091
 
  def self.httpdate(str='Mon, 01 Jan -4712 00:00:00 GMT', sg=ITALY) # :nodoc:
1092
 
    elem = _httpdate(str)
1093
 
    new_by_frags(elem, sg)
1094
 
  end
1095
 
 
1096
 
  def self.jisx0301(str='-4712-01-01', sg=ITALY) # :nodoc:
1097
 
    elem = _jisx0301(str)
1098
 
    new_by_frags(elem, sg)
1099
 
  end
1100
 
 
1101
 
  class << self
1102
 
 
1103
 
    def once(*ids) # :nodoc: -- restricted
1104
 
      for id in ids
1105
 
        module_eval <<-"end;"
1106
 
          alias_method :__#{id.object_id}__, :#{id.to_s}
1107
 
          private :__#{id.object_id}__
1108
 
          def #{id.to_s}(*args)
1109
 
            @__ca__[#{id.object_id}] ||= __#{id.object_id}__(*args)
1110
 
          end
1111
 
        end;
1112
 
      end
1113
 
    end # <<dummy
1114
 
 
1115
 
    private :once
1116
 
 
1117
 
  end
1118
 
 
1119
 
  # *NOTE* this is the documentation for the method new!().  If
1120
 
  # you are reading this as the documentation for new(), that is
1121
 
  # because rdoc doesn't fully support the aliasing of the
1122
 
  # initialize() method.
1123
 
  # new() is in
1124
 
  # fact an alias for #civil(): read the documentation for that
1125
 
  # method instead.
1126
 
  #
1127
 
  # Create a new Date object.
1128
 
  #
1129
 
  # +ajd+ is the Astronomical Julian Day Number.
1130
 
  # +of+ is the offset from UTC as a fraction of a day.
1131
 
  # Both default to 0.
1132
 
  #
1133
 
  # +sg+ specifies the Day of Calendar Reform to use for this
1134
 
  # Date object.
1135
 
  #
1136
 
  # Using one of the factory methods such as Date::civil is
1137
 
  # generally easier and safer.
1138
 
  def initialize(ajd=0, of=0, sg=ITALY)
1139
 
    @ajd, @of, @sg = ajd, of, sg
1140
 
    @__ca__ = {}
1141
 
  end
1142
 
 
1143
 
  # Get the date as an Astronomical Julian Day Number.
1144
 
  def ajd() @ajd end
1145
 
 
1146
 
  # Get the date as an Astronomical Modified Julian Day Number.
1147
 
  def amjd() ajd_to_amjd(@ajd) end
1148
 
 
1149
 
  once :amjd
1150
 
 
1151
 
  # Get the date as a Julian Day Number.
1152
 
  def jd() ajd_to_jd(@ajd, @of)[0] end
1153
 
 
1154
 
  # Get any fractional day part of the date.
1155
 
  def day_fraction() ajd_to_jd(@ajd, @of)[1] end
1156
 
 
1157
 
  # Get the date as a Modified Julian Day Number.
1158
 
  def mjd() jd_to_mjd(jd) end
1159
 
 
1160
 
  # Get the date as the number of days since the Day of Calendar
1161
 
  # Reform (in Italy and the Catholic countries).
1162
 
  def ld() jd_to_ld(jd) end
1163
 
 
1164
 
  once :jd, :day_fraction, :mjd, :ld
1165
 
 
1166
 
  # Get the date as a Civil Date, [year, month, day_of_month]
1167
 
  def civil() jd_to_civil(jd, @sg) end # :nodoc:
1168
 
 
1169
 
  # Get the date as an Ordinal Date, [year, day_of_year]
1170
 
  def ordinal() jd_to_ordinal(jd, @sg) end # :nodoc:
1171
 
 
1172
 
  # Get the date as a Commercial Date, [year, week_of_year, day_of_week]
1173
 
  def commercial() jd_to_commercial(jd, @sg) end # :nodoc:
1174
 
 
1175
 
  def weeknum0() jd_to_weeknum(jd, 0, @sg) end # :nodoc:
1176
 
  def weeknum1() jd_to_weeknum(jd, 1, @sg) end # :nodoc:
1177
 
 
1178
 
  once :civil, :ordinal, :commercial, :weeknum0, :weeknum1
1179
 
  private :civil, :ordinal, :commercial, :weeknum0, :weeknum1
1180
 
 
1181
 
  # Get the year of this date.
1182
 
  def year() civil[0] end
1183
 
 
1184
 
  # Get the day-of-the-year of this date.
1185
 
  #
1186
 
  # January 1 is day-of-the-year 1
1187
 
  def yday() ordinal[1] end
1188
 
 
1189
 
  # Get the month of this date.
1190
 
  #
1191
 
  # January is month 1.
1192
 
  def mon() civil[1] end
1193
 
 
1194
 
  # Get the day-of-the-month of this date.
1195
 
  def mday() civil[2] end
1196
 
 
1197
 
  alias_method :month, :mon
1198
 
  alias_method :day, :mday
1199
 
 
1200
 
  def wnum0() weeknum0[1] end # :nodoc:
1201
 
  def wnum1() weeknum1[1] end # :nodoc:
1202
 
 
1203
 
  private :wnum0, :wnum1
1204
 
 
1205
 
  # Get the time of this date as [hours, minutes, seconds,
1206
 
  # fraction_of_a_second]
1207
 
  def time() day_fraction_to_time(day_fraction) end # :nodoc:
1208
 
 
1209
 
  once :time
1210
 
  private :time
1211
 
 
1212
 
  # Get the hour of this date.
1213
 
  def hour() time[0] end
1214
 
 
1215
 
  # Get the minute of this date.
1216
 
  def min() time[1] end
1217
 
 
1218
 
  # Get the second of this date.
1219
 
  def sec() time[2] end
1220
 
 
1221
 
  # Get the fraction-of-a-second of this date.
1222
 
  def sec_fraction() time[3] end
1223
 
 
1224
 
  alias_method :minute, :min
1225
 
  alias_method :second, :sec
1226
 
  alias_method :second_fraction, :sec_fraction
1227
 
 
1228
 
  private :hour, :min, :sec, :sec_fraction,
1229
 
          :minute, :second, :second_fraction
1230
 
 
1231
 
  def zone() strftime('%:z') end
1232
 
 
1233
 
  private :zone
1234
 
 
1235
 
  # Get the commercial year of this date.  See *Commercial* *Date*
1236
 
  # in the introduction for how this differs from the normal year.
1237
 
  def cwyear() commercial[0] end
1238
 
 
1239
 
  # Get the commercial week of the year of this date.
1240
 
  def cweek() commercial[1] end
1241
 
 
1242
 
  # Get the commercial day of the week of this date.  Monday is
1243
 
  # commercial day-of-week 1; Sunday is commercial day-of-week 7.
1244
 
  def cwday() commercial[2] end
1245
 
 
1246
 
  # Get the week day of this date.  Sunday is day-of-week 0;
1247
 
  # Saturday is day-of-week 6.
1248
 
  def wday() jd_to_wday(jd) end
1249
 
 
1250
 
  once :wday
1251
 
 
1252
 
=begin
1253
 
  MONTHNAMES.each_with_index do |n, i|
1254
 
    if n
1255
 
      define_method(n.downcase + '?'){mon == i}
1256
 
    end
1257
 
  end
1258
 
=end
1259
 
 
1260
 
  DAYNAMES.each_with_index do |n, i|
1261
 
    define_method(n.downcase + '?'){wday == i}
1262
 
  end
1263
 
 
1264
 
  def nth_kday? (n, k)
1265
 
    k == wday && jd === nth_kday_to_jd(year, mon, n, k, start)
1266
 
  end
1267
 
 
1268
 
  private :nth_kday?
1269
 
 
1270
 
  # Is the current date old-style (Julian Calendar)?
1271
 
  def julian? () jd < @sg end
1272
 
 
1273
 
  # Is the current date new-style (Gregorian Calendar)?
1274
 
  def gregorian? () !julian? end
1275
 
 
1276
 
  once :julian?, :gregorian?
1277
 
 
1278
 
  def fix_style # :nodoc:
1279
 
    if julian?
1280
 
    then self.class::JULIAN
1281
 
    else self.class::GREGORIAN end
1282
 
  end
1283
 
 
1284
 
  private :fix_style
1285
 
 
1286
 
  # Is this a leap year?
1287
 
  def leap?
1288
 
    jd_to_civil(civil_to_jd(year, 3, 1, fix_style) - 1,
1289
 
                fix_style)[-1] == 29
1290
 
  end
1291
 
 
1292
 
  once :leap?
1293
 
 
1294
 
  # When is the Day of Calendar Reform for this Date object?
1295
 
  def start() @sg end
1296
 
 
1297
 
  # Create a copy of this Date object using a new Day of Calendar Reform.
1298
 
  def new_start(sg=self.class::ITALY) self.class.new!(@ajd, @of, sg) end
1299
 
 
1300
 
  # Create a copy of this Date object that uses the Italian/Catholic
1301
 
  # Day of Calendar Reform.
1302
 
  def italy() new_start(self.class::ITALY) end
1303
 
 
1304
 
  # Create a copy of this Date object that uses the English/Colonial
1305
 
  # Day of Calendar Reform.
1306
 
  def england() new_start(self.class::ENGLAND) end
1307
 
 
1308
 
  # Create a copy of this Date object that always uses the Julian
1309
 
  # Calendar.
1310
 
  def julian() new_start(self.class::JULIAN) end
1311
 
 
1312
 
  # Create a copy of this Date object that always uses the Gregorian
1313
 
  # Calendar.
1314
 
  def gregorian() new_start(self.class::GREGORIAN) end
1315
 
 
1316
 
  def offset() @of end
1317
 
 
1318
 
  def new_offset(of=0)
1319
 
    if String === of
1320
 
      of = Rational(zone_to_diff(of) || 0, 86400)
1321
 
    end
1322
 
    self.class.new!(@ajd, of, @sg)
1323
 
  end
1324
 
 
1325
 
  private :offset, :new_offset
1326
 
 
1327
 
  # Return a new Date object that is +n+ days later than the
1328
 
  # current one.
1329
 
  #
1330
 
  # +n+ may be a negative value, in which case the new Date
1331
 
  # is earlier than the current one; however, #-() might be
1332
 
  # more intuitive.
1333
 
  #
1334
 
  # If +n+ is not a Numeric, a TypeError will be thrown.  In
1335
 
  # particular, two Dates cannot be added to each other.
1336
 
  def + (n)
1337
 
    case n
1338
 
    when Numeric; return self.class.new!(@ajd + n, @of, @sg)
1339
 
    end
1340
 
    raise TypeError, 'expected numeric'
1341
 
  end
1342
 
 
1343
 
  # If +x+ is a Numeric value, create a new Date object that is
1344
 
  # +x+ days earlier than the current one.
1345
 
  #
1346
 
  # If +x+ is a Date, return the number of days between the
1347
 
  # two dates; or, more precisely, how many days later the current
1348
 
  # date is than +x+.
1349
 
  #
1350
 
  # If +x+ is neither Numeric nor a Date, a TypeError is raised.
1351
 
  def - (x)
1352
 
    case x
1353
 
    when Numeric; return self.class.new!(@ajd - x, @of, @sg)
1354
 
    when Date;    return @ajd - x.ajd
1355
 
    end
1356
 
    raise TypeError, 'expected numeric or date'
1357
 
  end
1358
 
 
1359
 
  # Compare this date with another date.
1360
 
  #
1361
 
  # +other+ can also be a Numeric value, in which case it is
1362
 
  # interpreted as an Astronomical Julian Day Number.
1363
 
  #
1364
 
  # Comparison is by Astronomical Julian Day Number, including
1365
 
  # fractional days.  This means that both the time and the
1366
 
  # timezone offset are taken into account when comparing
1367
 
  # two DateTime instances.  When comparing a DateTime instance
1368
 
  # with a Date instance, the time of the latter will be
1369
 
  # considered as falling on midnight UTC.
1370
 
  def <=> (other)
1371
 
    case other
1372
 
    when Numeric; return @ajd <=> other
1373
 
    when Date;    return @ajd <=> other.ajd
1374
 
    else
1375
 
      begin
1376
 
        l, r = other.coerce(self)
1377
 
        return l <=> r
1378
 
      rescue NoMethodError
1379
 
      end
1380
 
    end
1381
 
    nil
1382
 
  end
1383
 
 
1384
 
  # The relationship operator for Date.
1385
 
  #
1386
 
  # Compares dates by Julian Day Number.  When comparing
1387
 
  # two DateTime instances, or a DateTime with a Date,
1388
 
  # the instances will be regarded as equivalent if they
1389
 
  # fall on the same date in local time.
1390
 
  def === (other)
1391
 
    case other
1392
 
    when Numeric; return jd == other
1393
 
    when Date;    return jd == other.jd
1394
 
    else
1395
 
      begin
1396
 
        l, r = other.coerce(self)
1397
 
        return l === r
1398
 
      rescue NoMethodError
1399
 
      end
1400
 
    end
1401
 
    false
1402
 
  end
1403
 
 
1404
 
  def next_day(n=1) self + n end
1405
 
  def prev_day(n=1) self - n end
1406
 
 
1407
 
  # Return a new Date one day after this one.
1408
 
  def next() next_day end
1409
 
 
1410
 
  alias_method :succ, :next
1411
 
 
1412
 
  # Return a new Date object that is +n+ months later than
1413
 
  # the current one.
1414
 
  #
1415
 
  # If the day-of-the-month of the current Date is greater
1416
 
  # than the last day of the target month, the day-of-the-month
1417
 
  # of the returned Date will be the last day of the target month.
1418
 
  def >> (n)
1419
 
    y, m = (year * 12 + (mon - 1) + n).divmod(12)
1420
 
    m,   = (m + 1)                    .divmod(1)
1421
 
    d = mday
1422
 
    until jd2 = _valid_civil?(y, m, d, @sg)
1423
 
      d -= 1
1424
 
      raise ArgumentError, 'invalid date' unless d > 0
1425
 
    end
1426
 
    self + (jd2 - jd)
1427
 
  end
1428
 
 
1429
 
  # Return a new Date object that is +n+ months earlier than
1430
 
  # the current one.
1431
 
  #
1432
 
  # If the day-of-the-month of the current Date is greater
1433
 
  # than the last day of the target month, the day-of-the-month
1434
 
  # of the returned Date will be the last day of the target month.
1435
 
  def << (n) self >> -n end
1436
 
 
1437
 
  def next_month(n=1) self >> n end
1438
 
  def prev_month(n=1) self << n end
1439
 
 
1440
 
  def next_year(n=1) self >> n * 12 end
1441
 
  def prev_year(n=1) self << n * 12 end
1442
 
 
1443
 
  require 'enumerator'
1444
 
 
1445
 
  # Step the current date forward +step+ days at a
1446
 
  # time (or backward, if +step+ is negative) until
1447
 
  # we reach +limit+ (inclusive), yielding the resultant
1448
 
  # date at each step.
1449
 
  def step(limit, step=1) # :yield: date
1450
 
=begin
1451
 
    if step.zero?
1452
 
      raise ArgumentError, "step can't be 0"
1453
 
    end
1454
 
=end
1455
 
    unless block_given?
1456
 
      return to_enum(:step, limit, step)
1457
 
    end
1458
 
    da = self
1459
 
    op = %w(- <= >=)[step <=> 0]
1460
 
    while da.__send__(op, limit)
1461
 
      yield da
1462
 
      da += step
1463
 
    end
1464
 
    self
1465
 
  end
1466
 
 
1467
 
  # Step forward one day at a time until we reach +max+
1468
 
  # (inclusive), yielding each date as we go.
1469
 
  def upto(max, &block) # :yield: date
1470
 
    step(max, +1, &block)
1471
 
  end
1472
 
 
1473
 
  # Step backward one day at a time until we reach +min+
1474
 
  # (inclusive), yielding each date as we go.
1475
 
  def downto(min, &block) # :yield: date
1476
 
    step(min, -1, &block)
1477
 
  end
1478
 
 
1479
 
  # Is this Date equal to +other+?
1480
 
  #
1481
 
  # +other+ must both be a Date object, and represent the same date.
1482
 
  def eql? (other) Date === other && self == other end
1483
 
 
1484
 
  # Calculate a hash value for this date.
1485
 
  def hash() @ajd.hash end
1486
 
 
1487
 
  # Return internal object state as a programmer-readable string.
1488
 
  def inspect
1489
 
    format('#<%s: %s (%s,%s,%s)>', self.class, to_s, @ajd, @of, @sg)
1490
 
  end
1491
 
 
1492
 
  # Return the date as a human-readable string.
1493
 
  #
1494
 
  # The format used is YYYY-MM-DD.
1495
 
  def to_s() format('%.4d-%02d-%02d', year, mon, mday) end # 4p
1496
 
 
1497
 
  # Dump to Marshal format.
1498
 
  def marshal_dump() [@ajd, @of, @sg] end
1499
 
 
1500
 
  # Load from Marshal format.
1501
 
  def marshal_load(a)
1502
 
    @ajd, @of, @sg, = a
1503
 
    @__ca__ = {}
1504
 
  end
1505
 
 
1506
 
end
1507
 
 
1508
 
# Class representing a date and time.
1509
 
#
1510
 
# See the documentation to the file date.rb for an overview.
1511
 
#
1512
 
# DateTime objects are immutable once created.
1513
 
#
1514
 
# == Other methods.
1515
 
#
1516
 
# The following methods are defined in Date, but declared private
1517
 
# there.  They are made public in DateTime.  They are documented
1518
 
# here.
1519
 
#
1520
 
# === hour()
1521
 
#
1522
 
# Get the hour-of-the-day of the time.  This is given
1523
 
# using the 24-hour clock, counting from midnight.  The first
1524
 
# hour after midnight is hour 0; the last hour of the day is
1525
 
# hour 23.
1526
 
#
1527
 
# === min()
1528
 
#
1529
 
# Get the minute-of-the-hour of the time.
1530
 
#
1531
 
# === sec()
1532
 
#
1533
 
# Get the second-of-the-minute of the time.
1534
 
#
1535
 
# === sec_fraction()
1536
 
#
1537
 
# Get the fraction of a second of the time.  This is returned as
1538
 
# a +Rational+.
1539
 
#
1540
 
# === zone()
1541
 
#
1542
 
# Get the time zone as a String.  This is representation of the
1543
 
# time offset such as "+1000", not the true time-zone name.
1544
 
#
1545
 
# === offset()
1546
 
#
1547
 
# Get the time zone offset as a fraction of a day.  This is returned
1548
 
# as a +Rational+.
1549
 
#
1550
 
# === new_offset(of=0)
1551
 
#
1552
 
# Create a new DateTime object, identical to the current one, except
1553
 
# with a new time zone offset of +of+.  +of+ is the new offset from
1554
 
# UTC as a fraction of a day.
1555
 
#
1556
 
class DateTime < Date
1557
 
 
1558
 
  # Create a new DateTime object corresponding to the specified
1559
 
  # Julian Day Number +jd+ and hour +h+, minute +min+, second +s+.
1560
 
  #
1561
 
  # The 24-hour clock is used.  Negative values of +h+, +min+, and
1562
 
  # +sec+ are treating as counting backwards from the end of the
1563
 
  # next larger unit (e.g. a +min+ of -2 is treated as 58).  No
1564
 
  # wraparound is performed.  If an invalid time portion is specified,
1565
 
  # an ArgumentError is raised.
1566
 
  #
1567
 
  # +of+ is the offset from UTC as a fraction of a day (defaults to 0).
1568
 
  # +sg+ specifies the Day of Calendar Reform.
1569
 
  #
1570
 
  # All day/time values default to 0.
1571
 
  def self.jd(jd=0, h=0, min=0, s=0, of=0, sg=ITALY)
1572
 
    unless (jd = _valid_jd?(jd, sg)) &&
1573
 
           (fr = _valid_time?(h, min, s))
1574
 
      raise ArgumentError, 'invalid date'
1575
 
    end
1576
 
    if String === of
1577
 
      of = Rational(zone_to_diff(of) || 0, 86400)
1578
 
    end
1579
 
    new!(jd_to_ajd(jd, fr, of), of, sg)
1580
 
  end
1581
 
 
1582
 
  # Create a new DateTime object corresponding to the specified
1583
 
  # Ordinal Date and hour +h+, minute +min+, second +s+.
1584
 
  #
1585
 
  # The 24-hour clock is used.  Negative values of +h+, +min+, and
1586
 
  # +sec+ are treating as counting backwards from the end of the
1587
 
  # next larger unit (e.g. a +min+ of -2 is treated as 58).  No
1588
 
  # wraparound is performed.  If an invalid time portion is specified,
1589
 
  # an ArgumentError is raised.
1590
 
  #
1591
 
  # +of+ is the offset from UTC as a fraction of a day (defaults to 0).
1592
 
  # +sg+ specifies the Day of Calendar Reform.
1593
 
  #
1594
 
  # +y+ defaults to -4712, and +d+ to 1; this is Julian Day Number
1595
 
  # day 0.  The time values default to 0.
1596
 
  def self.ordinal(y=-4712, d=1, h=0, min=0, s=0, of=0, sg=ITALY)
1597
 
    unless (jd = _valid_ordinal?(y, d, sg)) &&
1598
 
           (fr = _valid_time?(h, min, s))
1599
 
      raise ArgumentError, 'invalid date'
1600
 
    end
1601
 
    if String === of
1602
 
      of = Rational(zone_to_diff(of) || 0, 86400)
1603
 
    end
1604
 
    new!(jd_to_ajd(jd, fr, of), of, sg)
1605
 
  end
1606
 
 
1607
 
  # Create a new DateTime object corresponding to the specified
1608
 
  # Civil Date and hour +h+, minute +min+, second +s+.
1609
 
  #
1610
 
  # The 24-hour clock is used.  Negative values of +h+, +min+, and
1611
 
  # +sec+ are treating as counting backwards from the end of the
1612
 
  # next larger unit (e.g. a +min+ of -2 is treated as 58).  No
1613
 
  # wraparound is performed.  If an invalid time portion is specified,
1614
 
  # an ArgumentError is raised.
1615
 
  #
1616
 
  # +of+ is the offset from UTC as a fraction of a day (defaults to 0).
1617
 
  # +sg+ specifies the Day of Calendar Reform.
1618
 
  #
1619
 
  # +y+ defaults to -4712, +m+ to 1, and +d+ to 1; this is Julian Day
1620
 
  # Number day 0.  The time values default to 0.
1621
 
  def self.civil(y=-4712, m=1, d=1, h=0, min=0, s=0, of=0, sg=ITALY)
1622
 
    unless (jd = _valid_civil?(y, m, d, sg)) &&
1623
 
           (fr = _valid_time?(h, min, s))
1624
 
      raise ArgumentError, 'invalid date'
1625
 
    end
1626
 
    if String === of
1627
 
      of = Rational(zone_to_diff(of) || 0, 86400)
1628
 
    end
1629
 
    new!(jd_to_ajd(jd, fr, of), of, sg)
1630
 
  end
1631
 
 
1632
 
  class << self; alias_method :new, :civil end
1633
 
 
1634
 
  # Create a new DateTime object corresponding to the specified
1635
 
  # Commercial Date and hour +h+, minute +min+, second +s+.
1636
 
  #
1637
 
  # The 24-hour clock is used.  Negative values of +h+, +min+, and
1638
 
  # +sec+ are treating as counting backwards from the end of the
1639
 
  # next larger unit (e.g. a +min+ of -2 is treated as 58).  No
1640
 
  # wraparound is performed.  If an invalid time portion is specified,
1641
 
  # an ArgumentError is raised.
1642
 
  #
1643
 
  # +of+ is the offset from UTC as a fraction of a day (defaults to 0).
1644
 
  # +sg+ specifies the Day of Calendar Reform.
1645
 
  #
1646
 
  # +y+ defaults to -4712, +w+ to 1, and +d+ to 1; this is
1647
 
  # Julian Day Number day 0.
1648
 
  # The time values default to 0.
1649
 
  def self.commercial(y=-4712, w=1, d=1, h=0, min=0, s=0, of=0, sg=ITALY)
1650
 
    unless (jd = _valid_commercial?(y, w, d, sg)) &&
1651
 
           (fr = _valid_time?(h, min, s))
1652
 
      raise ArgumentError, 'invalid date'
1653
 
    end
1654
 
    if String === of
1655
 
      of = Rational(zone_to_diff(of) || 0, 86400)
1656
 
    end
1657
 
    new!(jd_to_ajd(jd, fr, of), of, sg)
1658
 
  end
1659
 
 
1660
 
  def self.weeknum(y=-4712, w=0, d=1, f=0, h=0, min=0, s=0, of=0, sg=ITALY) # :nodoc:
1661
 
    unless (jd = _valid_weeknum?(y, w, d, f, sg)) &&
1662
 
           (fr = _valid_time?(h, min, s))
1663
 
      raise ArgumentError, 'invalid date'
1664
 
    end
1665
 
    if String === of
1666
 
      of = Rational(zone_to_diff(of) || 0, 86400)
1667
 
    end
1668
 
    new!(jd_to_ajd(jd, fr, of), of, sg)
1669
 
  end
1670
 
 
1671
 
  private_class_method :weeknum
1672
 
 
1673
 
  def self.nth_kday(y=-4712, m=1, n=1, k=1, h=0, min=0, s=0, of=0, sg=ITALY) # :nodoc:
1674
 
    unless (jd = _valid_nth_kday?(y, m, n, k, sg)) &&
1675
 
           (fr = _valid_time?(h, min, s))
1676
 
      raise ArgumentError, 'invalid date'
1677
 
    end
1678
 
    if String === of
1679
 
      of = Rational(zone_to_diff(of) || 0, 86400)
1680
 
    end
1681
 
    new!(jd_to_ajd(jd, fr, of), of, sg)
1682
 
  end
1683
 
 
1684
 
  private_class_method :nth_kday
1685
 
 
1686
 
  def self.new_by_frags(elem, sg) # :nodoc:
1687
 
    elem = rewrite_frags(elem)
1688
 
    elem = complete_frags(elem)
1689
 
    unless (jd = valid_date_frags?(elem, sg)) &&
1690
 
           (fr = valid_time_frags?(elem))
1691
 
      raise ArgumentError, 'invalid date'
1692
 
    end
1693
 
    fr += (elem[:sec_fraction] || 0) / 86400
1694
 
    of = Rational(elem[:offset] || 0, 86400)
1695
 
    new!(jd_to_ajd(jd, fr, of), of, sg)
1696
 
  end
1697
 
 
1698
 
  private_class_method :new_by_frags
1699
 
 
1700
 
  # Create a new DateTime object by parsing from a String
1701
 
  # according to a specified format.
1702
 
  #
1703
 
  # +str+ is a String holding a date-time representation.
1704
 
  # +fmt+ is the format that the date-time is in.  See
1705
 
  # date/format.rb for details on supported formats.
1706
 
  #
1707
 
  # The default +str+ is '-4712-01-01T00:00:00+00:00', and the default
1708
 
  # +fmt+ is '%FT%T%z'.  This gives midnight on Julian Day Number day 0.
1709
 
  #
1710
 
  # +sg+ specifies the Day of Calendar Reform.
1711
 
  #
1712
 
  # An ArgumentError will be raised if +str+ cannot be
1713
 
  # parsed.
1714
 
  def self.strptime(str='-4712-01-01T00:00:00+00:00', fmt='%FT%T%z', sg=ITALY)
1715
 
    elem = _strptime(str, fmt)
1716
 
    new_by_frags(elem, sg)
1717
 
  end
1718
 
 
1719
 
  # Create a new DateTime object by parsing from a String,
1720
 
  # without specifying the format.
1721
 
  #
1722
 
  # +str+ is a String holding a date-time representation.
1723
 
  # +comp+ specifies whether to interpret 2-digit years
1724
 
  # as 19XX (>= 69) or 20XX (< 69); the default is not to.
1725
 
  # The method will attempt to parse a date-time from the String
1726
 
  # using various heuristics; see #_parse in date/format.rb
1727
 
  # for more details.  If parsing fails, an ArgumentError
1728
 
  # will be raised.
1729
 
  #
1730
 
  # The default +str+ is '-4712-01-01T00:00:00+00:00'; this is Julian
1731
 
  # Day Number day 0.
1732
 
  #
1733
 
  # +sg+ specifies the Day of Calendar Reform.
1734
 
  def self.parse(str='-4712-01-01T00:00:00+00:00', comp=true, sg=ITALY)
1735
 
    elem = _parse(str, comp)
1736
 
    new_by_frags(elem, sg)
1737
 
  end
1738
 
 
1739
 
  def self.iso8601(str='-4712-01-01T00:00:00+00:00', sg=ITALY) # :nodoc:
1740
 
    elem = _iso8601(str)
1741
 
    new_by_frags(elem, sg)
1742
 
  end
1743
 
 
1744
 
  def self.rfc3339(str='-4712-01-01T00:00:00+00:00', sg=ITALY) # :nodoc:
1745
 
    elem = _rfc3339(str)
1746
 
    new_by_frags(elem, sg)
1747
 
  end
1748
 
 
1749
 
  def self.xmlschema(str='-4712-01-01T00:00:00+00:00', sg=ITALY) # :nodoc:
1750
 
    elem = _xmlschema(str)
1751
 
    new_by_frags(elem, sg)
1752
 
  end
1753
 
 
1754
 
  def self.rfc2822(str='Mon, 1 Jan -4712 00:00:00 +0000', sg=ITALY) # :nodoc:
1755
 
    elem = _rfc2822(str)
1756
 
    new_by_frags(elem, sg)
1757
 
  end
1758
 
 
1759
 
  class << self; alias_method :rfc822, :rfc2822 end
1760
 
 
1761
 
  def self.httpdate(str='Mon, 01 Jan -4712 00:00:00 GMT', sg=ITALY) # :nodoc:
1762
 
    elem = _httpdate(str)
1763
 
    new_by_frags(elem, sg)
1764
 
  end
1765
 
 
1766
 
  def self.jisx0301(str='-4712-01-01T00:00:00+00:00', sg=ITALY) # :nodoc:
1767
 
    elem = _jisx0301(str)
1768
 
    new_by_frags(elem, sg)
1769
 
  end
1770
 
 
1771
 
  public :hour, :min, :sec, :sec_fraction, :zone, :offset, :new_offset,
1772
 
         :minute, :second, :second_fraction
1773
 
 
1774
 
  def to_s # 4p
1775
 
    format('%.4d-%02d-%02dT%02d:%02d:%02d%s',
1776
 
           year, mon, mday, hour, min, sec, zone)
1777
 
  end
1778
 
 
1779
 
end
1780
 
 
1781
 
class Time
1782
 
 
1783
 
  def to_time() getlocal end
1784
 
 
1785
 
  def to_date
1786
 
    jd = Date.__send__(:civil_to_jd, year, mon, mday, Date::ITALY)
1787
 
    Date.new!(Date.__send__(:jd_to_ajd, jd, 0, 0), 0, Date::ITALY)
1788
 
  end
1789
 
 
1790
 
  def to_datetime
1791
 
    jd = DateTime.__send__(:civil_to_jd, year, mon, mday, DateTime::ITALY)
1792
 
    fr = DateTime.__send__(:time_to_day_fraction, hour, min, [sec, 59].min) +
1793
 
      Rational(subsec, 86400)
1794
 
    of = Rational(utc_offset, 86400)
1795
 
    DateTime.new!(DateTime.__send__(:jd_to_ajd, jd, fr, of),
1796
 
                  of, DateTime::ITALY)
1797
 
  end
1798
 
 
1799
 
end
1800
 
 
1801
 
class Date
1802
 
 
1803
 
  def to_time() Time.local(year, mon, mday) end
1804
 
  def to_date() self end
1805
 
  def to_datetime() DateTime.new!(jd_to_ajd(jd, 0, 0), @of, @sg) end
1806
 
 
1807
 
  # Create a new Date object representing today.
1808
 
  #
1809
 
  # +sg+ specifies the Day of Calendar Reform.
1810
 
  def self.today(sg=ITALY)
1811
 
    t = Time.now
1812
 
    jd = civil_to_jd(t.year, t.mon, t.mday, sg)
1813
 
    new!(jd_to_ajd(jd, 0, 0), 0, sg)
1814
 
  end
1815
 
 
1816
 
  # Create a new DateTime object representing the current time.
1817
 
  #
1818
 
  # +sg+ specifies the Day of Calendar Reform.
1819
 
  def self.now(sg=ITALY)
1820
 
    t = Time.now
1821
 
    jd = civil_to_jd(t.year, t.mon, t.mday, sg)
1822
 
    fr = time_to_day_fraction(t.hour, t.min, [t.sec, 59].min) +
1823
 
      Rational(t.subsec, 86400)
1824
 
    of = Rational(t.utc_offset, 86400)
1825
 
    new!(jd_to_ajd(jd, fr, of), of, sg)
1826
 
  end
1827
 
 
1828
 
  private_class_method :now
1829
 
 
1830
 
end
1831
 
 
1832
 
class DateTime < Date
1833
 
 
1834
 
  def to_time
1835
 
    d = new_offset(0)
1836
 
    d.instance_eval do
1837
 
      Time.utc(year, mon, mday, hour, min, sec +
1838
 
               sec_fraction)
1839
 
    end.
1840
 
        getlocal
1841
 
  end
1842
 
 
1843
 
  def to_date() Date.new!(jd_to_ajd(jd, 0, 0), 0, @sg) end
1844
 
  def to_datetime() self end
1845
 
 
1846
 
  private_class_method :today
1847
 
  public_class_method  :now
1848
 
 
1849
 
end