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
The timezone you want the application to use. This is the timezone that incoming times from the database and typecasting are converted to.
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
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
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
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
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
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
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 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 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 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
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 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 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 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
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
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