diff options
author | tadf <tadf@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2006-08-25 23:09:30 +0000 |
---|---|---|
committer | tadf <tadf@b2dd03c8-39d4-4d8f-98ff-823fe69b080e> | 2006-08-25 23:09:30 +0000 |
commit | f5994175269c42ead103993111e334d26234f244 (patch) | |
tree | 3c198edb24a775115c96b75228d19fefb252cdcb /lib/date.rb | |
parent | 583dbcc5484028cbd710c685dc3ac018722bd5f5 (diff) |
updated based on date2 3.8.2.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_1_8@10781 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'lib/date.rb')
-rw-r--r-- | lib/date.rb | 286 |
1 files changed, 227 insertions, 59 deletions
diff --git a/lib/date.rb b/lib/date.rb index 8639b31575..f85db8026f 100644 --- a/lib/date.rb +++ b/lib/date.rb @@ -6,7 +6,7 @@ # Documentation: William Webber <william@williamwebber.com> # #-- -# $Id: date.rb,v 2.15 2005-02-06 11:09:53+09 tadf Exp $ +# $Id: date.rb,v 2.21 2006-08-19 22:38:12+09 tadf Exp $ #++ # # == Overview @@ -249,6 +249,50 @@ class Date # Abbreviated day names, in English. ABBR_DAYNAMES = %w(Sun Mon Tue Wed Thu Fri Sat) + class Infinity < Numeric # :nodoc: + + include Comparable + + def initialize(d=1) @d = d <=> 0 end + + def d() @d end + + protected :d + + def zero? () false end + def finite? () false end + def infinite? () d.nonzero? end + def nan? () d.zero? end + + def abs() self.class.new end + + def -@ () self.class.new(-d) end + def +@ () self.class.new(+d) end + + def <=> (other) + case other + when Infinity; d <=> other.d + when Numeric; d + else + begin + l, r = other.coerce(self) + return l <=> r + rescue NoMethodError + end + end + nil + end + + def coerce(other) + case other + when Numeric; return -d, d + else + super + end + end + + end + # The Julian Day Number of the Day of Calendar Reform for Italy # and the Catholic countries. ITALY = 2299161 # 1582-10-15 @@ -259,11 +303,11 @@ class Date # A constant used to indicate that a Date should always use the # Julian calendar. - JULIAN = false + JULIAN = Infinity.new # A constant used to indicate that a Date should always use the # Gregorian calendar. - GREGORIAN = true + GREGORIAN = -Infinity.new # Does a given Julian Day Number fall inside the old-style (Julian) # calendar? @@ -273,10 +317,16 @@ class Date # the answer is true; or it may a number representing the Day of # Calendar Reform. Date::ENGLAND and Date::ITALY are two possible such # days. - def self.os? (jd, sg) + def self.julian? (jd, sg) case sg - when Numeric; jd < sg - else; not sg + when Numeric + jd < sg + else + if $VERBOSE + warn("#{caller.shift.sub(/:in .*/, '')}: " \ +"warning: do not use non-numerical object as julian day number anymore") + end + not sg end end @@ -285,7 +335,15 @@ class Date # # The reverse of self.os? See the documentation for that method for # more details. - def self.ns? (jd, sg) not os?(jd, sg) end + def self.gregorian? (jd, sg) not julian?(jd, sg) end + + def self.fix_style(jd, sg) # :nodoc: + if julian?(jd, sg) + then JULIAN + else GREGORIAN end + end + + private_class_method :fix_style # Convert a Civil Date to a Julian Day Number. # +y+, +m+, and +d+ are the year, month, and day of the @@ -302,7 +360,7 @@ class Date jd = (365.25 * (y + 4716)).floor + (30.6001 * (m + 1)).floor + d + b - 1524 - if os?(jd, sg) + if julian?(jd, sg) jd -= b end jd @@ -315,7 +373,7 @@ class Date # Returns the corresponding [year, month, day_of_month] # as a three-element array. def self.jd_to_civil(jd, sg=GREGORIAN) - if os?(jd, sg) + if julian?(jd, sg) a = jd else x = ((jd - 1867216.25) / 36524.25).floor @@ -355,7 +413,7 @@ class Date # [year, day_of_year] def self.jd_to_ordinal(jd, sg=GREGORIAN) y = jd_to_civil(jd, sg)[0] - doy = jd - civil_to_jd(y - 1, 12, 31, ns?(jd, sg)) + doy = jd - civil_to_jd(y - 1, 12, 31, fix_style(jd, sg)) return y, doy end @@ -367,7 +425,7 @@ class Date # Returns the corresponding Commercial Date as # [commercial_year, week_of_year, day_of_week] def self.jd_to_commercial(jd, sg=GREGORIAN) - ns = ns?(jd, sg) + ns = fix_style(jd, sg) a = jd_to_civil(jd - 3, ns)[0] y = if jd >= commercial_to_jd(a + 1, 1, 1, ns) then a + 1 else a end w = 1 + ((jd - commercial_to_jd(y, 1, 1, ns)) / 7).floor @@ -388,6 +446,18 @@ class Date (d - 1) end + def self.jd_to_weeknum(jd, k=0, sg=GREGORIAN) # :nodoc: + y, m, d = jd_to_civil(jd, sg) + a = civil_to_jd(y, 1, 1, sg) + 6 + w, d = clfloor(jd - (a - ((a - k) + 1) % 7) + 7, 7) + return y, w, d + end + + def self.weeknum_to_jd(y, w, d, k=0, sg=GREGORIAN) # :nodoc: + a = civil_to_jd(y, 1, 1, sg) + 6 + (a - ((a - k) + 1) % 7 - 7) + 7 * w + d + end + %w(self.clfloor clfloor).each do |name| module_eval <<-"end;" def #{name}(x, y=1) @@ -514,7 +584,7 @@ class Date if d < 0 ny, = clfloor(y + 1, 1) jd = ordinal_to_jd(ny, d + 1, sg) - ns = ns?(jd, sg) + ns = fix_style(jd, sg) return unless [y] == jd_to_ordinal(jd, sg)[0..0] return unless [ny, 1] == jd_to_ordinal(jd - d, ns) else @@ -561,7 +631,7 @@ class Date ny, nm = clfloor(y * 12 + m, 12) nm, = clfloor(nm + 1, 1) jd = civil_to_jd(ny, nm, d + 1, sg) - ns = ns?(jd, sg) + ns = fix_style(jd, sg) return unless [y, m] == jd_to_civil(jd, sg)[0..1] return unless [ny, nm, 1] == jd_to_civil(jd - d, ns) else @@ -617,7 +687,7 @@ class Date w = jd_to_commercial(commercial_to_jd(y + 1, 1, 1) + w * 7)[1] end jd = commercial_to_jd(y, w, d) - return unless ns?(jd, sg) + return unless gregorian?(jd, sg) return unless [y, w, d] == jd_to_commercial(jd) jd end @@ -643,17 +713,104 @@ class Date new0(jd_to_ajd(jd, 0, 0), 0, sg) end - def self.new_with_hash(elem, sg) + def self.valid_weeknum? (y, w, d, k, sg=ITALY) # :nodoc: + if d < 0 + d += 7 + end + if w < 0 + w = jd_to_weeknum(weeknum_to_jd(y + 1, 1, k, k) + w * 7, k)[1] + end + jd = weeknum_to_jd(y, w, d, k) + return unless [y, w, d] == jd_to_weeknum(jd, k) + jd + end + + private_class_method :valid_weeknum? + + def self.rewrite_hash(elem) # :nodoc: elem ||= {} - y, m, d = elem.values_at(:year, :mon, :mday) - if [y, m, d].include? nil - raise ArgumentError, '3 elements of civil date are necessary' - else - civil(y, m, d, sg) + if seconds = elem[:seconds] + d, fr = clfloor(seconds, 86400) + h, fr = clfloor(fr, 3600) + min, fr = clfloor(fr, 60) + s, fr = clfloor(fr, 1) + elem[:jd] = civil_to_jd(1970, 1, 1) + d + elem[:hour] = h + elem[:min] = min + elem[:sec] = s + elem.delete(:seconds) + elem.delete(:offset) end + elem end - private_class_method :new_with_hash + def self.valid_date_with_hash?(elem, sg) # :nodoc: + catch :jd do + a = elem.values_at(:jd) + if a.all? + if jd = valid_jd?(*(a << sg)) + throw :jd, jd + end + end + + a = elem.values_at(:year, :yday) + if a.all? + if jd = valid_ordinal?(*(a << sg)) + throw :jd, jd + end + end + + a = elem.values_at(:year, :mon, :mday) + if a.all? + if jd = valid_civil?(*(a << sg)) + throw :jd, jd + end + end + + a = elem.values_at(:cwyear, :cweek, :cwday) + if a[2].nil? && elem[:wday] + a[2] = elem[:wday].nonzero? || 7 + end + if a.all? + if jd = valid_commercial?(*(a << sg)) + throw :jd, jd + end + end + + a = elem.values_at(:year, :wnum0, :wday) + if a[2].nil? && elem[:cwday] + a[2] = elem[:cwday] % 7 + end + if a.all? + if jd = valid_weeknum?(*(a << 0 << sg)) + throw :jd, jd + end + end + + a = elem.values_at(:year, :wnum1, :wday) + if a[2] + a[2] = (a[2] - 1) % 7 + end + if a[2].nil? && elem[:cwday] + a[2] = (elem[:cwday] - 1) % 7 + end + if a.all? + if jd = valid_weeknum?(*(a << 1 << sg)) + throw :jd, jd + end + end + end + end + + def self.new_with_hash(elem, sg) # :nodoc: + elem = rewrite_hash(elem) + unless jd = valid_date_with_hash?(elem, sg) + raise ArgumentError, 'invalid date' + end + new0(jd_to_ajd(jd, 0, 0), 0, sg) + end + + private_class_method :rewrite_hash, :valid_date_with_hash?, :new_with_hash # Create a new Date object by parsing from a String # according to a specified format. @@ -707,17 +864,11 @@ class Date def once(*ids) # :nodoc: for id in ids - module_eval <<-"end;", __FILE__, __LINE__ + module_eval <<-"end;" alias_method :__#{id.to_i}__, :#{id.to_s} private :__#{id.to_i}__ def #{id.to_s}(*args, &block) - if defined? @__#{id.to_i}__ - @__#{id.to_i}__ - elsif ! self.frozen? - @__#{id.to_i}__ ||= __#{id.to_i}__(*args, &block) - else - __#{id.to_i}__(*args, &block) - end + (@__#{id.to_i}__ ||= [__#{id.to_i}__(*args, &block)])[0] end end; end @@ -772,13 +923,13 @@ class Date once :jd, :day_fraction, :mjd, :ld # Get the date as a Civil Date, [year, month, day_of_month] - def civil() self.class.jd_to_civil(jd, @sg) end + def civil() self.class.jd_to_civil(jd, @sg) end # :nodoc: # Get the date as an Ordinal Date, [year, day_of_year] - def ordinal() self.class.jd_to_ordinal(jd, @sg) end + def ordinal() self.class.jd_to_ordinal(jd, @sg) end # :nodoc: # Get the date as a Commercial Date, [year, week_of_year, day_of_week] - def commercial() self.class.jd_to_commercial(jd, @sg) end + def commercial() self.class.jd_to_commercial(jd, @sg) end # :nodoc: once :civil, :ordinal, :commercial private :civil, :ordinal, :commercial @@ -804,7 +955,7 @@ class Date # Get the time of this date as [hours, minutes, seconds, # fraction_of_a_second] - def time() self.class.day_fraction_to_time(day_fraction) end + def time() self.class.day_fraction_to_time(day_fraction) end # :nodoc: once :time private :time @@ -845,17 +996,25 @@ class Date once :wday # Is the current date old-style (Julian Calendar)? - def os? () self.class.os?(jd, @sg) end + def julian? () self.class.julian?(jd, @sg) end # Is the current date new-style (Gregorian Calendar)? - def ns? () self.class.ns?(jd, @sg) end + def gregorian? () self.class.gregorian?(jd, @sg) end - once :os?, :ns? + once :julian?, :gregorian? + + def fix_style # :nodoc: + if julian? + then Date::JULIAN + else Date::GREGORIAN end + end + + private :fix_style # Is this a leap year? def leap? - self.class.jd_to_civil(self.class.civil_to_jd(year, 3, 1, ns?) - 1, - ns?)[-1] == 29 + self.class.jd_to_civil(self.class.civil_to_jd(year, 3, 1, fix_style) - 1, + fix_style)[-1] == 29 end once :leap? @@ -962,7 +1121,7 @@ class Date y, m = clfloor(year * 12 + (mon - 1) + n, 12) m, = clfloor(m + 1, 1) d = mday - d -= 1 until jd2 = self.class.valid_civil?(y, m, d, ns?) + d -= 1 until jd2 = self.class.valid_civil?(y, m, d, fix_style) self + (jd2 - jd) end @@ -978,7 +1137,7 @@ class Date # time (or backward, if +step+ is negative) until # we reach +limit+ (inclusive), yielding the resultant # date at each step. - def step(limit, step) # :yield: date + def step(limit, step=1) # :yield: date da = self op = [:-,:<=,:>=][step<=>0] while da.__send__(op, limit) @@ -991,13 +1150,13 @@ class Date # Step forward one day at a time until we reach +max+ # (inclusive), yielding each date as we go. def upto(max, &block) # :yield: date - step(max, +1, &block) + step(max, +1, &block) end # Step backward one day at a time until we reach +min+ # (inclusive), yielding each date as we go. def downto(min, &block) # :yield: date - step(min, -1, &block) + step(min, -1, &block) end # Return a new Date one day after this one. @@ -1110,6 +1269,15 @@ class DateTime < Date time_to_day_fraction(h, min, s) end + def self.valid_time_with_hash? (elem) # :nodoc: + h, min, s = elem.values_at(:hour, :min, :sec) + h ||= 0 + min ||= 0 + s ||= 0 + s = [s, 59].min + valid_time?(h, min, s) + end + # Create a new DateTime object corresponding to the specified # Julian Day Number +jd+ and hour +h+, minute +min+, second +s+. # @@ -1200,21 +1368,17 @@ class DateTime < Date new0(jd_to_ajd(jd, fr, of), of, sg) end - def self.new_with_hash(elem, sg) - elem ||= {} - y, m, d, h, min, s, fr, of = - elem.values_at(:year, :mon, :mday, - :hour, :min, :sec, :sec_fraction, :offset) - h ||= 0 - min ||= 0 - s ||= 0 - fr ||= 0 - of ||= 0 - if [y, m, d].include? nil - raise ArgumentError, '3 elements of civil date are necessary' - else - civil(y, m, d, h, min, s, of.to_r/86400, sg) + (fr/86400) + def self.new_with_hash(elem, sg) # :nodoc: + elem = rewrite_hash(elem) + unless (jd = valid_date_with_hash?(elem, sg)) and + (fr = valid_time_with_hash?(elem)) + raise ArgumentError, 'invalid date' end + sf = (elem[:sec_fraction] || 0) + fr += sf/86400 + of = (elem[:offset] || 0) + of = of.to_r/86400 + new0(jd_to_ajd(jd, fr, of), of, sg) end private_class_method :new_with_hash @@ -1258,7 +1422,7 @@ class DateTime < Date new_with_hash(elem, sg) end - class << self; undef_method :today end rescue nil + class << self; undef_method :today end # Create a new DateTime object representing the current time. # @@ -1278,7 +1442,9 @@ end class Date - [ %w(exist1? valid_jd?), + [ %w(os? julian?), + %w(ns? gregorian?), + %w(exist1? valid_jd?), %w(exist2? valid_ordinal?), %w(exist3? valid_date?), %w(exist? valid_date?), @@ -1300,7 +1466,9 @@ class Date end; end - [ %w(sg start), + [ %w(os? julian?), + %w(ns? gregorian?), + %w(sg start), %w(newsg new_start), %w(of offset), %w(newof new_offset) |