Skip to content

Commit

Permalink
Implement fixed UTC offset for Time
Browse files Browse the repository at this point in the history
  • Loading branch information
jfirebaugh committed Mar 26, 2012
1 parent 80748ba commit c7ec4e0
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 81 deletions.
4 changes: 2 additions & 2 deletions kernel/bootstrap/time.rb
Expand Up @@ -11,7 +11,7 @@ def self.duplicate(other)
raise ArgumentError, "descriptors reference invalid time"
end

def self.specific(sec, nsec, from_gmt)
def self.specific(sec, nsec, from_gmt, offset)
Rubinius.primitive :time_s_specific
raise ArgumentError, "descriptors reference invalid time"
end
Expand All @@ -30,7 +30,7 @@ def usec
raise PrimitiveFailure, "Time#usec failed"
end

def decomposed(gmt)
def decomposed
Rubinius.primitive :time_decompose
raise PrimitiveFailure, "Time#decompose primitive failed"
end
Expand Down
49 changes: 25 additions & 24 deletions kernel/common/time.rb
Expand Up @@ -45,7 +45,7 @@ def self._load(data)
#++

def _dump(limit = nil)
tm = decomposed(true)
tm = getgm.decomposed

if (year & 0xffff) != year || year < 1900 then
raise ArgumentError, "year too big to marshal: #{year}"
Expand All @@ -66,18 +66,18 @@ def _dump(limit = nil)
[major, minor].pack 'VV'
end

def self.compose(is_utc, p1, p2=nil, p3=nil, p4=nil, p5=nil, p6=nil, p7=nil,
yday=undefined, isdst=undefined, tz=undefined)
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 tz.equal?(undefined)
unless isdst.equal?(undefined)
unless is_dst.equal?(undefined)
raise ArgumentError, "wrong number of arguments (9 for 1..8)"
end

y, m, d, hr, min, sec, usec = p1, p2, p3, p4, p5, p6, p7
isdst = -1
is_dst = -1
else
y, m, d, hr, min, sec, usec = p6, p5, p4, p3, p2, p1, 0
isdst = isdst ? 1 : 0
is_dst = is_dst ? 1 : 0
end

if m.kind_of?(String) or m.respond_to?(:to_str)
Expand Down Expand Up @@ -113,15 +113,28 @@ def self.compose(is_utc, p1, p2=nil, p3=nil, p4=nil, p5=nil, p6=nil, p7=nil,
end
end

from_array(sec, min, hr, d, m, y, nsec, is_utc ? -1 : isdst, is_utc)
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(false, *args)
compose(:local, *args)
end

def self.gm(*args)
compose(true, *args)
compose(:utc, *args)
end

def self.times
Expand All @@ -137,7 +150,7 @@ def asctime
end

def to_a
decomposed(@is_gmt)
decomposed
end

def sec
Expand Down Expand Up @@ -192,6 +205,7 @@ def to_f

def gmt_offset
return 0 if @is_gmt
return @offset if @offset

other = dup.gmtime

Expand All @@ -215,28 +229,16 @@ def gmt_offset
offset += sec - other.sec
end

def localtime
if @is_gmt
@is_gmt = false
@decomposed = nil
end

self
end

def gmtime
unless @is_gmt
@is_gmt = true
@offset = nil
@decomposed = nil
end

self
end

def getlocal
dup.localtime
end

def getgm
dup.gmtime
end
Expand All @@ -246,7 +248,6 @@ def hash
end

class << self
alias_method :new, :now
alias_method :mktime, :local
alias_method :utc, :gm
end
Expand Down
44 changes: 31 additions & 13 deletions kernel/common/time18.rb
Expand Up @@ -10,11 +10,11 @@ def self.at(sec, usec=undefined)
sec = sec + (nsec / 1000000000)
nsec = nsec % 1000000000

specific(sec, nsec, false)
specific(sec, nsec, false, nil)
elsif sec.kind_of?(Time)
duplicate(sec)
elsif sec.kind_of?(Integer)
specific(sec, 0, false)
specific(sec, 0, false, nil)
elsif sec.kind_of?(String)
raise TypeError, "can't convert #{sec} into an exact number"
else
Expand All @@ -27,16 +27,20 @@ def self.at(sec, usec=undefined)
end

nsec = (nsec_frac * 1_000_000_000 + 0.5).to_i
specific(sec, nsec, false)
specific(sec, nsec, false, nil)
end
end

def self.from_array(sec, min, hour, mday, month, year, nsec, is_dst, from_gmt)
class << self
alias_method :new, :now
end

def self.from_array(sec, min, hour, mday, month, year, nsec, is_dst, from_gmt, utc_offset)
Rubinius.primitive :time_s_from_array

sec = sec.kind_of?(String) ? sec.to_i : Rubinius::Type.coerce_to(sec || 0, Integer, :to_int)

from_array(sec, min, hour, mday, month, year, nsec || 0, is_dst, from_gmt)
from_array(sec, min, hour, mday, month, year, nsec || 0, is_dst, from_gmt, utc_offset)
end

def inspect
Expand All @@ -61,25 +65,39 @@ def +(arg)
end

# Don't use self.class, MRI doesn't honor subclasses here
Time.specific(seconds + other_sec, (usec + other_usec) * 1000, @is_gmt)
Time.specific(seconds + other_sec, (usec + other_usec) * 1000, @is_gmt, @offset)
end

def -(other)
if other.kind_of?(Time)
return (seconds - other.seconds) + ((usec - other.usec) * 0.000001)
end

case other
when Time
(seconds - other.seconds) + ((usec - other.usec) * 0.000001)
when Integer
# Don't use self.class, MRI doesn't honor subclasses here
Time.specific(seconds - other, usec * 1000, @is_gmt)
other_sec = other
other_usec = 0
else
other = FloatValue(other)

other_sec, usec_frac = FloatValue(other).divmod(1)
other_usec = (usec_frac * 1_000_000 + 0.5).to_i
end

# Don't use self.class, MRI doesn't honor subclasses here
Time.specific(seconds - other_sec, (usec - other_usec) * 1000, @is_gmt)
# Don't use self.class, MRI doesn't honor subclasses here
Time.specific(seconds - other_sec, (usec - other_usec) * 1000, @is_gmt, @offset)
end

def localtime
if @is_gmt
@is_gmt = false
@decomposed = nil
end

self
end

def getlocal
dup.localtime
end

def eql?(other)
Expand Down
73 changes: 45 additions & 28 deletions kernel/common/time19.rb
Expand Up @@ -6,7 +6,7 @@ def self.at(sec, usec=undefined)
if sec.kind_of?(Time)
return duplicate(sec)
elsif sec.kind_of?(Integer)
return specific(sec, 0, false)
return specific(sec, 0, false, nil)
end
end

Expand All @@ -24,10 +24,10 @@ def self.at(sec, usec=undefined)
sec += nsec / 1_000_000_000
nsec %= 1_000_000_000

specific(sec, nsec, false)
specific(sec, nsec, false, nil)
end

def self.from_array(sec, min, hour, mday, month, year, nsec, is_dst, from_gmt)
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)
Expand All @@ -51,7 +51,17 @@ def self.from_array(sec, min, hour, mday, month, year, nsec, is_dst, from_gmt)
sec += nsec / 1_000_000_000
nsec %= 1_000_000_000

from_array(sec, min, hour, mday, month, year, nsec, is_dst, from_gmt)
from_array(sec, min, hour, mday, month, year, nsec, is_dst, from_gmt, utc_offset)
end

def self.new(year=undefined, month=nil, day=nil, hour=nil, minute=nil, second=nil, utc_offset=nil)
if year.equal?(undefined)
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
Expand Down Expand Up @@ -111,47 +121,54 @@ def to_r
to_f.to_r
end

def +(arg)
raise TypeError, 'time + time?' if arg.kind_of?(Time)
def +(other)
raise TypeError, 'time + time?' if other.kind_of?(Time)

case arg
when NilClass
raise TypeError, "can't convert nil into an exact number"
when String
raise TypeError, "can't convert String into an exact number"
case other = Rubinius::Type.coerce_to_exact_num(other)
when Integer
other_sec = arg
other_sec = other
other_nsec = 0
else
arg = Rubinius::Type.coerce_to arg, Rational, :to_r
other_sec, nsec_frac = arg.divmod(1)
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)
Time.specific(seconds + other_sec, nsec + other_nsec, @is_gmt, @offset)
end

def -(other)
case other
when NilClass
raise TypeError, "can't convert nil into an exact number"
when String
raise TypeError, "can't convert String into an exact number"
when Time
(seconds - other.seconds) + ((usec - other.usec) * 0.000001)
if other.kind_of?(Time)
return (seconds - other.seconds) + ((usec - other.usec) * 0.000001)
end

case other = Rubinius::Type.coerce_to_exact_num(other)
when Integer
# Don't use self.class, MRI doesn't honor subclasses here
Time.specific(seconds - other, nsec, @is_gmt)
other_sec = other
other_nsec = 0
else
other = Rubinius::Type.coerce_to other, Rational, :to_r

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

# Don't use self.class, MRI doesn't honor subclasses here
Time.specific(seconds - other_sec, nsec - other_nsec, @is_gmt)
def localtime(offset=nil)
offset = Rubinius::Type.coerce_to_utc_offset(offset) unless offset.nil?

if @is_gmt or offset
@is_gmt = false
@offset = offset
@decomposed = nil
end

self
end

def getlocal(offset=nil)
dup.localtime(offset)
end

def eql?(other)
Expand Down
21 changes: 21 additions & 0 deletions kernel/common/type19.rb
Expand Up @@ -104,5 +104,26 @@ def self.coerce_to_exact_num(obj)
check_convert_type(obj, Rational, :to_r) || coerce_to(obj, Integer, :to_int)
end
end

def self.coerce_to_utc_offset(offset)
offset = String.try_convert(offset) || offset

if offset.kind_of?(String)
unless offset.encoding.ascii_compatible? && offset.match(/\A(\+|-)(\d\d):(\d\d)\z/)
raise ArgumentError, '"+HH:MM" or "-HH:MM" expected for utc_offset'
end

offset = $2.to_i*60*60 + $3.to_i*60
offset = -offset if $1.ord == 45
else
offset = Rubinius::Type.coerce_to_exact_num(offset)
end

if offset <= -86400 || offset >= 86400
raise ArgumentError, "utc_offset out of range"
end

offset
end
end
end

0 comments on commit c7ec4e0

Please sign in to comment.