core/time.rb
class Time
include Comparable
def self.now
Rubinius.primitive :time_s_now
raise PrimitiveFailure, "Time.now primitive failed"
end
def self.duplicate(other)
Rubinius.primitive :time_s_dup
raise ArgumentError, "descriptors reference invalid time"
end
def self.specific(sec, nsec, from_gmt, offset)
Rubinius.primitive :time_s_specific
raise ArgumentError, "descriptors reference invalid time"
end
def dup
self.class.duplicate(self)
end
def seconds
Rubinius.primitive :time_seconds
raise PrimitiveFailure, "Time#second primitive failed"
end
def usec
Rubinius.primitive :time_useconds
raise PrimitiveFailure, "Time#usec primitive failed"
end
def to_a
Rubinius.primitive :time_decompose
raise PrimitiveFailure, "Time#to_a primitive failed"
end
def strftime(format)
Rubinius.primitive :time_strftime
raise PrimitiveFailure, "Time#strftime primitive failed"
end
def self.at(sec, usec=undefined)
if undefined.equal?(usec)
if sec.kind_of?(Time)
return duplicate(sec)
elsif sec.kind_of?(Integer)
return specific(sec, 0, false, nil)
end
end
if sec.kind_of?(Time) && usec.kind_of?(Integer)
raise TypeError, "can't convert Time into an exact number"
end
usec = 0 if undefined.equal?(usec)
s = Rubinius::Type.coerce_to_exact_num(sec)
u = Rubinius::Type.coerce_to_exact_num(usec)
sec = s.to_i
nsec_frac = s % 1.0
sec -= 1 if s < 0 && nsec_frac > 0
nsec = (nsec_frac * 1_000_000_000 + 0.5).to_i + (u * 1000).to_i
sec += nsec / 1_000_000_000
nsec %= 1_000_000_000
specific(sec, nsec, false, nil)
end
def self.from_array(sec, min, hour, mday, month, year, nsec, is_dst, from_gmt, utc_offset)
Rubinius.primitive :time_s_from_array
if sec.kind_of?(String)
sec = sec.to_i
elsif nsec
sec = Rubinius::Type.coerce_to(sec || 0, Integer, :to_int)
else
s = Rubinius::Type.coerce_to_exact_num(sec || 0)
sec = s.to_i
nsec_frac = s % 1.0
if s < 0 && nsec_frac > 0
sec -= 1
end
nsec = (nsec_frac * 1_000_000_000 + 0.5).to_i
end
nsec ||= 0
sec += nsec / 1_000_000_000
nsec %= 1_000_000_000
if utc_offset
utc_offset_sec = utc_offset.to_i
utc_offset_nsec = ((utc_offset % 1.0) * 1_000_000_000 + 0.5).to_i
else
utc_offset_sec = 0
utc_offset_nsec = 0
end
from_array(sec, min, hour, mday, month, year, nsec, is_dst, from_gmt, utc_offset, utc_offset_sec, utc_offset_nsec)
end
def self.new(year=undefined, month=nil, day=nil, hour=nil, minute=nil, second=nil, utc_offset=nil)
if undefined.equal?(year)
now
elsif utc_offset == nil
compose(:local, year, month, day, hour, minute, second)
else
compose(Rubinius::Type.coerce_to_utc_offset(utc_offset), year, month, day, hour, minute, second)
end
end
def inspect
if @is_gmt
str = strftime("%Y-%m-%d %H:%M:%S UTC")
else
str = strftime("%Y-%m-%d %H:%M:%S %z")
end
str.force_encoding Encoding::US_ASCII
end
alias_method :to_s, :inspect
def nsec
Rubinius.primitive :time_nseconds
raise PrimitiveFailure, "Time#nsec primitive failed"
end
def nsec=(nanoseconds)
Rubinius.primitive :time_set_nseconds
raise PrimitiveFailure, "Time#nsec= primitive failed"
end
private :nsec=
alias_method :tv_nsec, :nsec
def subsec
if nsec == 0
0
else
Rational(nsec, 1_000_000_000)
end
end
def sunday?
wday == 0
end
def monday?
wday == 1
end
def tuesday?
wday == 2
end
def wednesday?
wday == 3
end
def thursday?
wday == 4
end
def friday?
wday == 5
end
def saturday?
wday == 6
end
def to_r
(seconds + subsec).to_r
end
def to_f
to_r.to_f
end
def +(other)
raise TypeError, 'time + time?' if other.kind_of?(Time)
case other = Rubinius::Type.coerce_to_exact_num(other)
when Integer
other_sec = other
other_nsec = 0
else
other_sec, nsec_frac = other.divmod(1)
other_nsec = (nsec_frac * 1_000_000_000).to_i
end
# Don't use self.class, MRI doesn't honor subclasses here
Time.specific(seconds + other_sec, nsec + other_nsec, @is_gmt, @offset)
end
def -(other)
if other.kind_of?(Time)
return (seconds - other.seconds) + ((nsec - other.nsec) * 0.000000001)
end
case other = Rubinius::Type.coerce_to_exact_num(other)
when Integer
other_sec = other
other_nsec = 0
else
other_sec, nsec_frac = other.divmod(1)
other_nsec = (nsec_frac * 1_000_000_000 + 0.5).to_i
end
# Don't use self.class, MRI doesn't honor subclasses here
Time.specific(seconds - other_sec, nsec - other_nsec, @is_gmt, @offset)
end
def localtime(offset=nil)
@is_gmt = false
@offset = nil
@decomposed = nil
if offset
@offset = Rubinius::Type.coerce_to_utc_offset(offset)
@zone = nil
else
@offset = gmt_offset
@zone = Rubinius.invoke_primitive(:time_env_zone, self)
end
self
end
def getlocal(offset=nil)
dup.localtime(offset)
end
def eql?(other)
other.kind_of?(Time) and seconds == other.seconds and nsec == other.nsec
end
def <=>(other)
if other.kind_of? Time
(seconds <=> other.seconds).nonzero? or (nsec <=> other.nsec)
else
r = (other <=> self)
return nil if r == nil
return -1 if r > 0
return 1 if r < 0
0
end
end
#
# Rounds sub seconds to a given precision in decimal digits
#
# Returns a new time object.
#
# places should be nonnegative, it is 0 by default.
#
def round(places = 0)
return dup if nsec == 0
roundable_time = (to_i + subsec.to_r).round(places)
sec = roundable_time.floor
nano = ((roundable_time - sec) * 1_000_000_000).floor
Time.specific(sec, nano, @is_gmt, @offset)
end
class << self
def compose_deal_with_year(year)
year
end
private :compose_deal_with_year
end
#--
# TODO: doesn't load ivars
#++
def self._load(data)
raise TypeError, 'marshaled time format differ' unless data.bytesize == 8
major, minor = data.unpack 'VV'
if (major & (1 << 31)) == 0
at major, minor
else
major &= ~(1 << 31)
is_gmt = (major >> 30) & 0x1
year = ((major >> 14) & 0xffff) + 1900
mon = ((major >> 10) & 0xf) + 1
mday = (major >> 5) & 0x1f
hour = major & 0x1f
min = (minor >> 26) & 0x3f
sec = (minor >> 20) & 0x3f
isdst = false
usec = minor & 0xfffff
time = gm year, mon, mday, hour, min, sec, usec
time.localtime if is_gmt.zero?
time
end
end
class << self
private :_load
end
#--
# TODO: doesn't dump ivars
#++
def _dump(limit = nil)
tm = getgm.to_a
if (year & 0xffff) != year || year < 1900
raise ArgumentError, "year too big to marshal: #{year}"
end
gmt = @is_gmt ? 1 : 0
major = 1 << 31 | # 1 bit
gmt << 30 | # 1 bit
(tm[5] - 1900) << 14 | # 16 bits
(tm[4] - 1) << 10 | # 4 bits
tm[3] << 5 | # 5 bits
tm[2] # 5 bits
minor = tm[1] << 26 | # 6 bits
tm[0] << 20 | # 6 bits
usec # 20 bits
[major, minor].pack 'VV'
end
private :_dump
def self.compose(offset, p1, p2=nil, p3=nil, p4=nil, p5=nil, p6=nil, p7=nil,
yday=undefined, is_dst=undefined, tz=undefined)
if undefined.equal?(tz)
unless undefined.equal?(is_dst)
raise ArgumentError, "wrong number of arguments (9 for 1..8)"
end
y = p1
m = p2
d = p3
hr = p4
min = p5
sec = p6
usec = p7
is_dst = -1
else
y = p6
m = p5
d = p4
hr = p3
min = p2
sec = p1
usec = 0
is_dst = is_dst ? 1 : 0
end
if m.kind_of?(String) or m.respond_to?(:to_str)
m = StringValue(m)
m = MonthValue[m.upcase] || m.to_i
raise ArgumentError, "month argument out of range" unless m
else
m = Rubinius::Type.coerce_to(m || 1, Integer, :to_int)
end
y = y.kind_of?(String) ? y.to_i : Rubinius::Type.coerce_to(y, Integer, :to_int)
d = d.kind_of?(String) ? d.to_i : Rubinius::Type.coerce_to(d || 1, Integer, :to_int)
hr = hr.kind_of?(String) ? hr.to_i : Rubinius::Type.coerce_to(hr || 0, Integer, :to_int)
min = min.kind_of?(String) ? min.to_i : Rubinius::Type.coerce_to(min || 0, Integer, :to_int)
nsec = nil
if usec.kind_of?(String)
nsec = usec.to_i * 1000
elsif usec
nsec = (usec * 1000).to_i
end
y = compose_deal_with_year(y)
case offset
when :utc
is_dst = -1
is_utc = true
offset = nil
when :local
is_utc = false
offset = nil
else
is_dst = -1
is_utc = false
end
from_array(sec, min, hr, d, m, y, nsec, is_dst, is_utc, offset)
end
def self.local(*args)
compose(:local, *args)
end
def self.gm(*args)
compose(:utc, *args)
end
def succ
self + 1
end
def asctime
strftime("%a %b %e %H:%M:%S %Y")
end
def sec
to_a[0]
end
def min
to_a[1]
end
def hour
to_a[2]
end
def day
to_a[3]
end
def mon
to_a[4]
end
def year
to_a[5]
end
def wday
to_a[6]
end
def yday
to_a[7]
end
def dst?
to_a[8]
end
def zone
zone = to_a[9]
if zone && Encoding.default_internal
zone.encode Encoding.default_internal
else
zone
end
end
def gmt?
@is_gmt
end
alias_method :to_i, :seconds
def gmt_offset
Rubinius.primitive :time_utc_offset
raise PrimitiveFailure, "Time#gmt_offset primitive failed"
end
def gmtime
unless @is_gmt
@is_gmt = true
@offset = nil
@decomposed = nil
end
self
end
def getgm
dup.gmtime
end
def hash
seconds ^ usec
end
class << self
alias_method :mktime, :local
alias_method :utc, :gm
end
alias_method :utc?, :gmt?
alias_method :month, :mon
alias_method :ctime, :asctime
alias_method :mday, :day
alias_method :to_i, :seconds
alias_method :tv_sec, :seconds
alias_method :tv_usec, :usec
alias_method :utc, :gmtime
alias_method :isdst, :dst?
alias_method :utc_offset, :gmt_offset
alias_method :gmtoff, :gmt_offset
alias_method :getutc, :getgm
end