module Sequel::Timezones

Sequel doesn't pay much attention to timezones by default, but you can set it handle timezones if you want. There are three separate timezone settings, application_timezone, database_timezone, and typecast_timezone. All three timezones have getter and setter methods. You can set all three timezones to the same value at once via Sequel.default_timezone=.

The only timezone values that are supported by default are :utc (convert to UTC), :local (convert to local time), and nil (don't convert). If you need to convert to a specific timezone, or need the timezones being used to change based on the environment (e.g. current user), you need to use the named_timezones extension (and use DateTime as the datetime_class). Sequel also ships with a thread_local_timezones extensions which allows each thread to have its own timezone values for each of the timezones.

Attributes

application_timezone[R]

The timezone you want the application to use. This is the timezone that incoming times from the database and typecasting are converted to.

database_timezone[R]

The timezone for storage in the database. This is the timezone to which Sequel will convert timestamps before literalizing them for storage in the database. It is also the timezone that Sequel will assume database timestamp values are already in (if they don't include an offset).

typecast_timezone[R]

The timezone that incoming data that Sequel needs to typecast is assumed to be already in (if they don't include an offset).

Public Instance Methods

application_to_database_timestamp(v) click to toggle source

Convert the given Time/DateTime object into the database timezone, used when literalizing objects in an SQL string.

   # File lib/sequel/timezones.rb
40 def application_to_database_timestamp(v)
41   convert_output_timestamp(v, Sequel.database_timezone)
42 end
convert_output_timestamp(v, output_timezone) click to toggle source

Converts the object to the given output_timezone.

   # File lib/sequel/timezones.rb
45 def convert_output_timestamp(v, output_timezone)
46   if output_timezone
47     if v.is_a?(DateTime)
48       case output_timezone
49       when :utc
50         v.new_offset(0)
51       when :local
52         v.new_offset(local_offset_for_datetime(v))
53       else
54         convert_output_datetime_other(v, output_timezone)
55       end
56     else
57       case output_timezone
58       when :utc
59         v.getutc
60       when :local
61         v.getlocal
62       else
63         convert_output_time_other(v, output_timezone)
64       end
65     end
66   else
67     v
68   end
69 end
convert_timestamp(v, input_timezone) click to toggle source

Converts the given object from the given input timezone to the application_timezone using convert_input_timestamp and convert_output_timestamp.

   # File lib/sequel/timezones.rb
74 def convert_timestamp(v, input_timezone)
75   begin
76     if v.is_a?(Date) && !v.is_a?(DateTime)
77       # Dates handled specially as they are assumed to already be in the application_timezone
78       if datetime_class == DateTime
79         DateTime.civil(v.year, v.month, v.day, 0, 0, 0, application_timezone == :local ? Rational(Time.local(v.year, v.month, v.day).utc_offset, 86400) : 0)
80       else
81         Time.public_send(application_timezone == :utc ? :utc : :local, v.year, v.month, v.day)
82       end
83     else
84       convert_output_timestamp(convert_input_timestamp(v, input_timezone), application_timezone)
85     end
86   rescue InvalidValue
87     raise
88   rescue => e
89     raise convert_exception_class(e, InvalidValue)
90   end
91 end
database_to_application_timestamp(v) click to toggle source

Convert the given object into an object of Sequel.datetime_class in the application_timezone. Used when converting datetime/timestamp columns returned by the database.

   # File lib/sequel/timezones.rb
96 def database_to_application_timestamp(v)
97   convert_timestamp(v, Sequel.database_timezone)
98 end
default_timezone=(tz) click to toggle source

Sets the database, application, and typecasting timezones to the given timezone.

    # File lib/sequel/timezones.rb
101 def default_timezone=(tz)
102   self.database_timezone = tz
103   self.application_timezone = tz
104   self.typecast_timezone = tz
105 end
typecast_to_application_timestamp(v) click to toggle source

Convert the given object into an object of Sequel.datetime_class in the application_timezone. Used when typecasting values when assigning them to model datetime attributes.

    # File lib/sequel/timezones.rb
110 def typecast_to_application_timestamp(v)
111   convert_timestamp(v, Sequel.typecast_timezone)
112 end

Private Instance Methods

convert_input_datetime_no_offset(v, input_timezone) click to toggle source

Convert the given DateTime to the given input_timezone, keeping the same time and just modifying the timezone.

    # File lib/sequel/timezones.rb
118 def convert_input_datetime_no_offset(v, input_timezone)
119   case input_timezone
120   when nil, :utc
121     v # DateTime assumes UTC if no offset is given
122   when :local
123     offset = local_offset_for_datetime(v)
124     v.new_offset(offset) - offset
125   else
126     convert_input_datetime_other(v, input_timezone)
127   end
128 end
convert_input_datetime_other(v, input_timezone) click to toggle source

Convert the given DateTime to the given input_timezone that is not supported by default (i.e. one other than nil, :local, or :utc). Raises an InvalidValue by default. Can be overridden in extensions.

    # File lib/sequel/timezones.rb
133 def convert_input_datetime_other(v, input_timezone)
134   raise InvalidValue, "Invalid input_timezone: #{input_timezone.inspect}"
135 end
convert_input_time_other(v, input_timezone) click to toggle source

Convert the given Time to the given input_timezone that is not supported by default (i.e. one other than nil, :local, or :utc). Raises an InvalidValue by default. Can be overridden in extensions.

    # File lib/sequel/timezones.rb
140 def convert_input_time_other(v, input_timezone)
141   raise InvalidValue, "Invalid input_timezone: #{input_timezone.inspect}"
142 end
convert_input_timestamp(v, input_timezone) click to toggle source

Converts the object from a String, Array, Date, DateTime, or Time into an instance of Sequel.datetime_class. If given an array or a string that doesn't contain an offset, assume that the array/string is already in the given input_timezone.

    # File lib/sequel/timezones.rb
147 def convert_input_timestamp(v, input_timezone)
148   case v
149   when String
150     v2 = Sequel.string_to_datetime(v)
151     if !input_timezone || Date._parse(v).has_key?(:offset)
152       v2
153     else
154       # Correct for potentially wrong offset if string doesn't include offset
155       if v2.is_a?(DateTime)
156         convert_input_datetime_no_offset(v2, input_timezone)
157       else
158         case input_timezone
159         when nil, :local
160           v2
161         when :utc
162           (v2 + v2.utc_offset).utc
163         else
164           convert_input_time_other((v2 + v2.utc_offset).utc, input_timezone)
165         end
166       end
167     end
168   when Array
169     y, mo, d, h, mi, s, ns, off = v
170     if datetime_class == DateTime
171       s += Rational(ns, 1000000000) if ns
172       if off
173         DateTime.civil(y, mo, d, h, mi, s, off)
174       else
175         convert_input_datetime_no_offset(DateTime.civil(y, mo, d, h, mi, s), input_timezone)
176       end
177     elsif off
178       s += Rational(ns, 1000000000) if ns
179       Time.new(y, mo, d, h, mi, s, (off*86400).to_i)
180     else
181       case input_timezone
182       when nil, :local
183         Time.local(y, mo, d, h, mi, s, (ns ? ns / 1000.0 : 0))
184       when :utc
185         Time.utc(y, mo, d, h, mi, s, (ns ? ns / 1000.0 : 0))
186       else
187         convert_input_time_other(Time.utc(y, mo, d, h, mi, s, (ns ? ns / 1000.0 : 0)), input_timezone)
188       end
189     end
190   when Hash
191     ary = [:year, :month, :day, :hour, :minute, :second, :nanos].map{|x| (v[x] || v[x.to_s]).to_i}
192     if (offset = (v[:offset] || v['offset']))
193       ary << offset
194     end
195     convert_input_timestamp(ary, input_timezone)
196   when Time
197     if datetime_class == DateTime
198       v.to_datetime
199     else
200       v
201     end
202   when DateTime
203     if datetime_class == DateTime
204       v
205     else
206       v.to_time
207     end
208   else
209     raise InvalidValue, "Invalid convert_input_timestamp type: #{v.inspect}"
210   end
211 end
convert_output_datetime_other(v, output_timezone) click to toggle source

Convert the given DateTime to the given output_timezone that is not supported by default (i.e. one other than nil, :local, or :utc). Raises an InvalidValue by default. Can be overridden in extensions.

    # File lib/sequel/timezones.rb
216 def convert_output_datetime_other(v, output_timezone)
217   raise InvalidValue, "Invalid output_timezone: #{output_timezone.inspect}"
218 end
convert_output_time_other(v, output_timezone) click to toggle source

Convert the given Time to the given output_timezone that is not supported by default (i.e. one other than nil, :local, or :utc). Raises an InvalidValue by default. Can be overridden in extensions.

    # File lib/sequel/timezones.rb
223 def convert_output_time_other(v, output_timezone)
224   raise InvalidValue, "Invalid output_timezone: #{output_timezone.inspect}"
225 end
convert_timezone_setter_arg(tz) click to toggle source

Convert the timezone setter argument. Returns argument given by default, exists for easier overriding in extensions.

    # File lib/sequel/timezones.rb
229 def convert_timezone_setter_arg(tz)
230   tz
231 end
local_offset_for_datetime(dt) click to toggle source

Takes a DateTime dt, and returns the correct local offset for that dt, daylight savings included, in fraction of a day.

    # File lib/sequel/timezones.rb
234 def local_offset_for_datetime(dt)
235   time_offset_to_datetime_offset Time.local(dt.year, dt.month, dt.day, dt.hour, dt.min, dt.sec).utc_offset
236 end
time_offset_to_datetime_offset(offset_secs) click to toggle source

Caches offset conversions to avoid excess Rational math.

    # File lib/sequel/timezones.rb
239 def time_offset_to_datetime_offset(offset_secs)
240   if offset = Sequel.synchronize{@local_offsets[offset_secs]}
241     return offset
242   end
243   Sequel.synchronize{@local_offsets[offset_secs] = Rational(offset_secs, 86400)}
244 end