Time handling improved to ensure UTC always used
This commit is contained in:
parent
3a3fc3c41d
commit
38257f4909
9 changed files with 662 additions and 536 deletions
43
lib/couchrest/model/core_extensions/time_parsing.rb
Normal file
43
lib/couchrest/model/core_extensions/time_parsing.rb
Normal file
|
@ -0,0 +1,43 @@
|
|||
module CouchRest
|
||||
module Model
|
||||
module CoreExtensions
|
||||
module TimeParsing
|
||||
|
||||
# Attemtps to parse a time string in ISO8601 format.
|
||||
# If no match is found, the standard time parse will be used.
|
||||
#
|
||||
# Times, unless provided with a time zone, are assumed to be in
|
||||
# UTC.
|
||||
#
|
||||
def parse_iso8601(string)
|
||||
if (string =~ /(\d{4})[\-|\/](\d{2})[\-|\/](\d{2})[T|\s](\d{2}):(\d{2}):(\d{2})(Z| ?([\+|\s|\-])?(\d{2}):?(\d{2}))?/)
|
||||
# $1 = year
|
||||
# $2 = month
|
||||
# $3 = day
|
||||
# $4 = hours
|
||||
# $5 = minutes
|
||||
# $6 = seconds
|
||||
# $7 = UTC or Timezone
|
||||
# $8 = time zone direction
|
||||
# $9 = tz difference hours
|
||||
# $10 = tz difference minutes
|
||||
|
||||
if (!$7.to_s.empty? && $7 != 'Z')
|
||||
new($1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, "#{$8 == '-' ? '-' : '+'}#{$9}:#{$10}")
|
||||
else
|
||||
utc($1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i)
|
||||
end
|
||||
else
|
||||
parse(string)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Time.class_eval do
|
||||
extend CouchRest::Model::CoreExtensions::TimeParsing
|
||||
end
|
||||
|
|
@ -131,8 +131,10 @@ module CouchRest
|
|||
end
|
||||
|
||||
# Automatically set <tt>updated_at</tt> and <tt>created_at</tt> fields
|
||||
# on the document whenever saving occurs. CouchRest uses a pretty
|
||||
# decent time format by default. See Time#to_json
|
||||
# on the document whenever saving occurs.
|
||||
#
|
||||
# These properties are casted as Time objects, so they should always
|
||||
# be set to UTC.
|
||||
def timestamps!
|
||||
class_eval <<-EOS, __FILE__, __LINE__
|
||||
property(:updated_at, Time, :read_only => true, :protected => true, :auto_validation => false)
|
||||
|
|
|
@ -1,26 +1,3 @@
|
|||
class Time
|
||||
# returns a local time value much faster than Time.parse
|
||||
def self.mktime_with_offset(string)
|
||||
string =~ /(\d{4})[\-|\/](\d{2})[\-|\/](\d{2})[T|\s](\d{2}):(\d{2}):(\d{2})(([\+|\s|\-])*(\d{2}):?(\d{2}))?/
|
||||
# $1 = year
|
||||
# $2 = month
|
||||
# $3 = day
|
||||
# $4 = hours
|
||||
# $5 = minutes
|
||||
# $6 = seconds
|
||||
# $8 = time zone direction
|
||||
# $9 = tz difference
|
||||
# utc time with wrong TZ info:
|
||||
time = mktime($1, RFC2822_MONTH_NAME[$2.to_i - 1], $3, $4, $5, $6)
|
||||
if ($7)
|
||||
tz_difference = ("#{$8 == '-' ? '+' : '-'}#{$9}".to_i * 3600)
|
||||
time + tz_difference + zone_offset(time.zone)
|
||||
else
|
||||
time
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module CouchRest
|
||||
module Model
|
||||
module Typecast
|
||||
|
@ -29,7 +6,11 @@ module CouchRest
|
|||
return nil if value.nil?
|
||||
klass = property.type_class
|
||||
if value.instance_of?(klass) || klass == Object
|
||||
value
|
||||
if klass == Time && !value.utc?
|
||||
value.utc # Ensure Time is always in UTC
|
||||
else
|
||||
value
|
||||
end
|
||||
elsif [String, TrueClass, Integer, Float, BigDecimal, DateTime, Time, Date, Class].include?(klass)
|
||||
send('typecast_to_'+klass.to_s.downcase, value)
|
||||
else
|
||||
|
@ -127,12 +108,11 @@ module CouchRest
|
|||
if value.is_a?(Hash)
|
||||
typecast_hash_to_time(value)
|
||||
else
|
||||
Time.mktime_with_offset(value.to_s)
|
||||
Time.parse_iso8601(value.to_s)
|
||||
end
|
||||
rescue ArgumentError
|
||||
value
|
||||
rescue TypeError
|
||||
# After failures, resort to normal time parse
|
||||
value
|
||||
end
|
||||
|
||||
|
@ -150,13 +130,13 @@ module CouchRest
|
|||
# Creates a Time instance from a Hash with keys :year, :month, :day,
|
||||
# :hour, :min, :sec
|
||||
def typecast_hash_to_time(value)
|
||||
Time.local(*extract_time(value))
|
||||
Time.utc(*extract_time(value))
|
||||
end
|
||||
|
||||
# Extracts the given args from the hash. If a value does not exist, it
|
||||
# uses the value of Time.now.
|
||||
def extract_time(value)
|
||||
now = Time.now
|
||||
now = Time.now
|
||||
[:year, :month, :day, :hour, :min, :sec].map do |segment|
|
||||
typecast_to_numeric(value.fetch(segment, now.send(segment)), :to_i)
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue