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 v.public_send(output_timezone == :utc ? :getutc : :getlocal) 58 end 59 else 60 v 61 end 62 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 67 def convert_timestamp(v, input_timezone) 68 begin 69 if v.is_a?(Date) && !v.is_a?(DateTime) 70 # Dates handled specially as they are assumed to already be in the application_timezone 71 if datetime_class == DateTime 72 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) 73 else 74 Time.public_send(application_timezone == :utc ? :utc : :local, v.year, v.month, v.day) 75 end 76 else 77 convert_output_timestamp(convert_input_timestamp(v, input_timezone), application_timezone) 78 end 79 rescue InvalidValue 80 raise 81 rescue => e 82 raise convert_exception_class(e, InvalidValue) 83 end 84 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 89 def database_to_application_timestamp(v) 90 convert_timestamp(v, Sequel.database_timezone) 91 end
Sets the database, application, and typecasting timezones to the given timezone.
# File lib/sequel/timezones.rb 94 def default_timezone=(tz) 95 self.database_timezone = tz 96 self.application_timezone = tz 97 self.typecast_timezone = tz 98 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 103 def typecast_to_application_timestamp(v) 104 convert_timestamp(v, Sequel.typecast_timezone) 105 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 111 def convert_input_datetime_no_offset(v, input_timezone) 112 case input_timezone 113 when :utc, nil 114 v # DateTime assumes UTC if no offset is given 115 when :local 116 offset = local_offset_for_datetime(v) 117 v.new_offset(offset) - offset 118 else 119 convert_input_datetime_other(v, input_timezone) 120 end 121 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 126 def convert_input_datetime_other(v, input_timezone) 127 raise InvalidValue, "Invalid input_timezone: #{input_timezone.inspect}" 128 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 133 def convert_input_timestamp(v, input_timezone) 134 case v 135 when String 136 v2 = Sequel.string_to_datetime(v) 137 if !input_timezone || Date._parse(v).has_key?(:offset) 138 v2 139 else 140 # Correct for potentially wrong offset if string doesn't include offset 141 if v2.is_a?(DateTime) 142 v2 = convert_input_datetime_no_offset(v2, input_timezone) 143 else 144 # Time assumes local time if no offset is given 145 v2 = v2.getutc + v2.utc_offset if input_timezone == :utc 146 end 147 v2 148 end 149 when Array 150 y, mo, d, h, mi, s, ns, off = v 151 if datetime_class == DateTime 152 s += Rational(ns, 1000000000) if ns 153 if off 154 DateTime.civil(y, mo, d, h, mi, s, off) 155 else 156 convert_input_datetime_no_offset(DateTime.civil(y, mo, d, h, mi, s), input_timezone) 157 end 158 else 159 Time.public_send(input_timezone == :utc ? :utc : :local, y, mo, d, h, mi, s, (ns ? ns / 1000.0 : 0)) 160 end 161 when Hash 162 ary = [:year, :month, :day, :hour, :minute, :second, :nanos].map{|x| (v[x] || v[x.to_s]).to_i} 163 if (offset = (v[:offset] || v['offset'])) 164 ary << offset 165 end 166 convert_input_timestamp(ary, input_timezone) 167 when Time 168 if datetime_class == DateTime 169 v.to_datetime 170 else 171 v 172 end 173 when DateTime 174 if datetime_class == DateTime 175 v 176 else 177 v.to_time 178 end 179 else 180 raise InvalidValue, "Invalid convert_input_timestamp type: #{v.inspect}" 181 end 182 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 187 def convert_output_datetime_other(v, output_timezone) 188 raise InvalidValue, "Invalid output_timezone: #{output_timezone.inspect}" 189 end
Convert the timezone setter argument. Returns argument given by default, exists for easier overriding in extensions.
# File lib/sequel/timezones.rb 193 def convert_timezone_setter_arg(tz) 194 tz 195 end
Takes a DateTime dt, and returns the correct local offset for that dt, daylight savings included.
# File lib/sequel/timezones.rb 198 def local_offset_for_datetime(dt) 199 time_offset_to_datetime_offset Time.local(dt.year, dt.month, dt.day, dt.hour, dt.min, dt.sec).utc_offset 200 end
Caches offset conversions to avoid excess Rational math.
# File lib/sequel/timezones.rb 203 def time_offset_to_datetime_offset(offset_secs) 204 @local_offsets ||= {} 205 @local_offsets[offset_secs] ||= Rational(offset_secs, 86400) 206 end