1 # depends on: comparable.rb class.rb module.rb
15 ZoneOffset = { 'UTC' => 0, 'Z' => 0, 'UT' => 0, 'GMT' => 0,
16 'EST' => -5, 'EDT' => -4, 'CST' => -6, 'CDT' => -5,
17 'CET' => 1, 'CEST' => 2,
18 'MST' => -7, 'MDT' => -6, 'PST' => -8, 'PDT' => -7,
19 'A' => +1, 'B' => +2, 'C' => +3, 'D' => +4, 'E' => +5,
20 'F' => +6, 'G' => +7, 'H' => +8, 'I' => +9, 'K' => +10,
21 'L' => +11, 'M' => +12, 'N' => -1, 'O' => -2, 'P' => -3,
22 'Q' => -4, 'R' => -5, 'S' => -6, 'T' => -7, 'U' => -8,
23 'V' => -9, 'W' => -10, 'X' => -11, 'Y' => -12, }
26 'JAN' => 1, 'FEB' => 2, 'MAR' => 3, 'APR' => 4, 'MAY' => 5, 'JUN' => 6,
27 'JUL' => 7, 'AUG' => 8, 'SEP' => 9, 'OCT' =>10, 'NOV' =>11, 'DEC' =>12
30 LeapYearMonthDays = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
32 CommonYearMonthDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
34 RFC2822_DAY_NAME = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
36 RFC2822_MONTH_NAME = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
38 PRE_EPOCH_DAYS = 719468
58 @timeval = Time.gettimeofday
60 # this flag specifies whether this Time instance represents
61 # local time or GMT. it is independent of the actual time zone.
64 @tm = time_switch(@timeval.first, false)
68 # TODO: doesn't load nsec or ivars
72 raise TypeError, 'marshaled time format differ' unless data.length == 8
74 major, minor = data.unpack 'VV'
76 if (major & (1 << 31)) == 0 then
81 is_gmt = (major >> 30) & 0x1
82 year = (major >> 14) & 0xffff
83 mon = ((major >> 10) & 0xf) + 1
84 mday = (major >> 5) & 0x1f
87 min = (minor >> 26) & 0x3f
88 sec = (minor >> 20) & 0x3f
91 usec = minor & 0xfffff
93 time = gm year, mon, mday, hour, min, sec, usec
94 time.localtime # unless is_gmt.zero? # HACK MRI ignores the is_gmt flag
100 # TODO: doesn't dump nsec or ivars
103 def _dump(limit = nil)
104 tm = time_switch @timeval.first, true
105 year = tm[TM_FIELDS[:year]]
107 if (year & 0xffff) != year then
108 raise ArgumentError, "year too big to marshal: #{year}"
111 gmt = @is_gmt ? 1 : 0
113 major = 1 << 31 | # 1 bit
115 tm[TM_FIELDS[:year]] << 14 | # 16 bits
116 tm[TM_FIELDS[:mon]] << 10 | # 4 bits
117 tm[TM_FIELDS[:mday]] << 5 | # 5 bits
118 tm[TM_FIELDS[:hour]] # 5 bits
119 minor = tm[TM_FIELDS[:min]] << 26 | # 6 bits
120 tm[TM_FIELDS[:sec]] << 20 | # 6 bits
121 @timeval[TIMEVAL_FIELDS[:usec]] # 20 bits
123 [major, minor].pack 'VV'
128 t.instance_variable_set(:@timeval, @timeval)
129 t.instance_variable_set(:@tm, @tm)
130 t.instance_variable_set(:@is_gmt, @is_gmt)
134 def self.local(first, *args)
143 isdst = args[7] ? 1 : 0
145 # resolve month names to numbers
146 if args[0] && (args[0].kind_of?(String) || args[0].respond_to?(:to_str))
147 args[0] = args[0].to_str if args[0].respond_to?(:to_str)
148 month = MonthValue[args[0].upcase] || args[0].to_i || raise(ArgumentError.new('argument out of range'))
152 month ||= args[0] || 1
155 minute = args[3] || 0
156 second = args[4] || 0
162 t.mktime(second, minute, hour, day, month, year, usec, isdst, false)
166 def self.gm(first, *args)
176 # resolve month names to numbers
177 if args[0] && args[0].respond_to?(:to_str) && (args[0] = args[0].to_str).to_i == 0
178 month = MonthValue[args[0].upcase] || raise(ArgumentError.new('argument out of range'))
181 # set argument defaults
183 month ||= args[0] || 1
186 minute = args[3] || 0
187 second = args[4] || 0
192 t.mktime(second, minute, hour, day, month, year, usec, -1, true)
196 def self.at(secs_or_time, msecs = nil)
197 if secs_or_time.kind_of? Time
198 return secs_or_time.dup
201 Time.allocate.at_gmt(secs_or_time, msecs, false)
205 __strftime(@tm, format.to_str)
210 strftime("%a %b %d %H:%M:%S UTC %Y")
212 strftime("%a %b %d %H:%M:%S %z %Y")
221 raise TypeError, 'time + time?' if other.kind_of?(Time)
222 other = FloatValue(other) unless other.is_a?(Integer)
224 if other.kind_of?(Float)
225 other_usec = (other % 1) * 1000000
228 dup.at_gmt(seconds + other, usec + other_usec, @is_gmt)
232 if other.kind_of? Time
233 seconds - other.seconds + (usec - other.usec) * 0.000001
235 other = FloatValue(other) unless other.is_a?(Integer)
237 if other.kind_of?(Float)
238 other_usec = (1 - other % 1) * 1000000
239 other = other.ceil.to_i
241 dup.at_gmt(seconds - other, usec + other_usec, @is_gmt)
250 if other.kind_of? Time
251 [self.seconds, self.usec] <=> [other.seconds, other.usec]
258 (self <=> other) == 0
262 strftime("%a %b %e %H:%M:%S %Y")
314 seconds + (usec / 1000000.0)
319 # [ sec, min, hour, day, month, year, wday, yday, isdst, zone ]
322 [sec, min, hour, day, month, year, wday, yday, isdst, zone]
330 if year != other.year
331 offset = year < other.year ? -1 : 1
332 elsif month != other.month
333 offset = month < other.month ? -1 : 1
334 elsif mday != other.mday
335 offset = mday < other.mday ? -1 : 1
341 offset += hour - other.hour
344 offset += min - other.min
347 offset += sec - other.sec
351 force_localtime if @is_gmt
357 force_gmtime unless @is_gmt
379 @tm = time_switch(@timeval.first, false)
386 @tm = time_switch(@timeval.first, true)
392 def mktime(sec, min, hour, mday, mon, year, usec, isdst, from_gmt)
401 # This logic is taken from MRI, on how to deal with 2 digit dates.
403 if 0 <= year and year < 39
404 warn "2 digit year used: #{year}"
406 elsif 69 <= year and year < 139
407 warn "2 or 3 digit year used: #{year}"
412 @timeval = time_mktime(sec, min, hour, mday, mon, year, usec, isdst, from_gmt)
413 raise ArgumentError, "time out of range" if @timeval.first == -1
419 # +sec+ and +usec+ are always given in gmt here.
421 # +want_gmt+ says whether the caller wants a gmtime or local time object.
423 def at_gmt(sec, usec, want_gmt)
424 if sec.kind_of?(Integer) || usec
425 sec = Type.coerce_to sec, Integer, :to_i
426 usec = usec ? usec.to_i : 0
428 sec = Type.coerce_to sec, Float, :to_f
429 usec = ((sec % 1) * 1000000).to_i
433 sec = sec + (usec / 1000000)
434 usec = usec % 1000000
436 @timeval = [sec, usec]
445 def self.month_days(y, m)
446 if ((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0)
447 LeapYearMonthDays[m-1]
449 CommonYearMonthDays[m-1]
453 def self.apply_offset(year, mon, day, hour, min, sec, off)
456 off, o = off.divmod(60)
457 if o != 0 then sec += o; o, sec = sec.divmod(60); off += o end
458 off, o = off.divmod(60)
459 if o != 0 then min += o; o, min = min.divmod(60); off += o end
460 off, o = off.divmod(24)
461 if o != 0 then hour += o; o, hour = hour.divmod(24); off += o end
464 if month_days(year, mon) < day
474 off, o = off.divmod(60)
475 if o != 0 then sec -= o; o, sec = sec.divmod(60); off -= o end
476 off, o = off.divmod(60)
477 if o != 0 then min -= o; o, min = min.divmod(60); off -= o end
478 off, o = off.divmod(24)
479 if o != 0 then hour -= o; o, hour = hour.divmod(24); off -= o end
488 day = month_days(year, mon)
492 return year, mon, day, hour, min, sec
498 alias_method :now, :new
499 alias_method :mktime, :local
500 alias_method :utc, :gm
503 alias_method :utc?, :gmt?
504 alias_method :month, :mon
505 alias_method :ctime, :asctime
506 alias_method :mday, :day
507 alias_method :to_i, :seconds
508 alias_method :to_s, :inspect
509 alias_method :tv_sec, :seconds
510 alias_method :tv_usec, :usec
511 alias_method :utc, :gmtime
512 alias_method :isdst, :dst?
513 alias_method :utc_offset, :gmt_offset
514 alias_method :gmtoff, :gmt_offset
515 alias_method :getutc, :getgm