tzinfo 1.2.11 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/.yardopts +3 -0
- data/CHANGES.md +469 -431
- data/LICENSE +13 -13
- data/README.md +368 -114
- data/lib/tzinfo/country.rb +131 -129
- data/lib/tzinfo/country_timezone.rb +70 -112
- data/lib/tzinfo/data_source.rb +389 -144
- data/lib/tzinfo/data_sources/constant_offset_data_timezone_info.rb +56 -0
- data/lib/tzinfo/data_sources/country_info.rb +42 -0
- data/lib/tzinfo/data_sources/data_timezone_info.rb +91 -0
- data/lib/tzinfo/data_sources/linked_timezone_info.rb +33 -0
- data/lib/tzinfo/data_sources/ruby_data_source.rb +141 -0
- data/lib/tzinfo/data_sources/timezone_info.rb +47 -0
- data/lib/tzinfo/data_sources/transitions_data_timezone_info.rb +214 -0
- data/lib/tzinfo/data_sources/zoneinfo_data_source.rb +573 -0
- data/lib/tzinfo/data_sources/zoneinfo_reader.rb +284 -0
- data/lib/tzinfo/data_sources.rb +8 -0
- data/lib/tzinfo/data_timezone.rb +33 -47
- data/lib/tzinfo/datetime_with_offset.rb +153 -0
- data/lib/tzinfo/format1/country_definer.rb +17 -0
- data/lib/tzinfo/format1/country_index_definition.rb +64 -0
- data/lib/tzinfo/format1/timezone_definer.rb +64 -0
- data/lib/tzinfo/format1/timezone_definition.rb +39 -0
- data/lib/tzinfo/format1/timezone_index_definition.rb +77 -0
- data/lib/tzinfo/format1.rb +10 -0
- data/lib/tzinfo/format2/country_definer.rb +68 -0
- data/lib/tzinfo/format2/country_index_definer.rb +68 -0
- data/lib/tzinfo/format2/country_index_definition.rb +46 -0
- data/lib/tzinfo/format2/timezone_definer.rb +94 -0
- data/lib/tzinfo/format2/timezone_definition.rb +73 -0
- data/lib/tzinfo/format2/timezone_index_definer.rb +45 -0
- data/lib/tzinfo/format2/timezone_index_definition.rb +55 -0
- data/lib/tzinfo/format2.rb +10 -0
- data/lib/tzinfo/info_timezone.rb +26 -21
- data/lib/tzinfo/linked_timezone.rb +33 -52
- data/lib/tzinfo/offset_timezone_period.rb +42 -0
- data/lib/tzinfo/string_deduper.rb +118 -0
- data/lib/tzinfo/time_with_offset.rb +128 -0
- data/lib/tzinfo/timestamp.rb +548 -0
- data/lib/tzinfo/timestamp_with_offset.rb +85 -0
- data/lib/tzinfo/timezone.rb +979 -502
- data/lib/tzinfo/timezone_offset.rb +84 -74
- data/lib/tzinfo/timezone_period.rb +151 -217
- data/lib/tzinfo/timezone_proxy.rb +70 -79
- data/lib/tzinfo/timezone_transition.rb +77 -109
- data/lib/tzinfo/transitions_timezone_period.rb +63 -0
- data/lib/tzinfo/version.rb +7 -0
- data/lib/tzinfo/with_offset.rb +61 -0
- data/lib/tzinfo.rb +60 -40
- data.tar.gz.sig +0 -0
- metadata +51 -115
- metadata.gz.sig +2 -3
- data/Rakefile +0 -107
- data/lib/tzinfo/annual_rules.rb +0 -51
- data/lib/tzinfo/country_index_definition.rb +0 -31
- data/lib/tzinfo/country_info.rb +0 -42
- data/lib/tzinfo/data_timezone_info.rb +0 -55
- data/lib/tzinfo/linked_timezone_info.rb +0 -26
- data/lib/tzinfo/offset_rationals.rb +0 -77
- data/lib/tzinfo/posix_time_zone_parser.rb +0 -136
- data/lib/tzinfo/ruby_core_support.rb +0 -176
- data/lib/tzinfo/ruby_country_info.rb +0 -74
- data/lib/tzinfo/ruby_data_source.rb +0 -136
- data/lib/tzinfo/time_or_datetime.rb +0 -351
- data/lib/tzinfo/timezone_definition.rb +0 -36
- data/lib/tzinfo/timezone_index_definition.rb +0 -54
- data/lib/tzinfo/timezone_info.rb +0 -30
- data/lib/tzinfo/timezone_transition_definition.rb +0 -104
- data/lib/tzinfo/transition_data_timezone_info.rb +0 -274
- data/lib/tzinfo/transition_rule.rb +0 -325
- data/lib/tzinfo/zoneinfo_country_info.rb +0 -37
- data/lib/tzinfo/zoneinfo_data_source.rb +0 -504
- data/lib/tzinfo/zoneinfo_timezone_info.rb +0 -516
- data/test/assets/payload.rb +0 -1
- data/test/tc_annual_rules.rb +0 -95
- data/test/tc_country.rb +0 -240
- data/test/tc_country_index_definition.rb +0 -69
- data/test/tc_country_info.rb +0 -16
- data/test/tc_country_timezone.rb +0 -173
- data/test/tc_data_source.rb +0 -218
- data/test/tc_data_timezone.rb +0 -99
- data/test/tc_data_timezone_info.rb +0 -18
- data/test/tc_info_timezone.rb +0 -34
- data/test/tc_linked_timezone.rb +0 -155
- data/test/tc_linked_timezone_info.rb +0 -23
- data/test/tc_offset_rationals.rb +0 -23
- data/test/tc_posix_time_zone_parser.rb +0 -261
- data/test/tc_ruby_core_support.rb +0 -168
- data/test/tc_ruby_country_info.rb +0 -110
- data/test/tc_ruby_data_source.rb +0 -175
- data/test/tc_time_or_datetime.rb +0 -674
- data/test/tc_timezone.rb +0 -1361
- data/test/tc_timezone_definition.rb +0 -113
- data/test/tc_timezone_index_definition.rb +0 -73
- data/test/tc_timezone_info.rb +0 -11
- data/test/tc_timezone_london.rb +0 -143
- data/test/tc_timezone_melbourne.rb +0 -142
- data/test/tc_timezone_new_york.rb +0 -142
- data/test/tc_timezone_offset.rb +0 -126
- data/test/tc_timezone_period.rb +0 -555
- data/test/tc_timezone_proxy.rb +0 -136
- data/test/tc_timezone_transition.rb +0 -366
- data/test/tc_timezone_transition_definition.rb +0 -295
- data/test/tc_timezone_utc.rb +0 -27
- data/test/tc_transition_data_timezone_info.rb +0 -433
- data/test/tc_transition_rule.rb +0 -663
- data/test/tc_zoneinfo_country_info.rb +0 -78
- data/test/tc_zoneinfo_data_source.rb +0 -1226
- data/test/tc_zoneinfo_timezone_info.rb +0 -2149
- data/test/test_utils.rb +0 -214
- data/test/ts_all.rb +0 -7
- data/test/ts_all_ruby.rb +0 -5
- data/test/ts_all_zoneinfo.rb +0 -9
- data/test/tzinfo-data/tzinfo/data/definitions/America/Argentina/Buenos_Aires.rb +0 -89
- data/test/tzinfo-data/tzinfo/data/definitions/America/New_York.rb +0 -327
- data/test/tzinfo-data/tzinfo/data/definitions/Australia/Melbourne.rb +0 -230
- data/test/tzinfo-data/tzinfo/data/definitions/EST.rb +0 -19
- data/test/tzinfo-data/tzinfo/data/definitions/Etc/GMT__m__1.rb +0 -21
- data/test/tzinfo-data/tzinfo/data/definitions/Etc/GMT__p__1.rb +0 -21
- data/test/tzinfo-data/tzinfo/data/definitions/Etc/UTC.rb +0 -21
- data/test/tzinfo-data/tzinfo/data/definitions/Europe/Amsterdam.rb +0 -273
- data/test/tzinfo-data/tzinfo/data/definitions/Europe/Andorra.rb +0 -198
- data/test/tzinfo-data/tzinfo/data/definitions/Europe/London.rb +0 -333
- data/test/tzinfo-data/tzinfo/data/definitions/Europe/Paris.rb +0 -277
- data/test/tzinfo-data/tzinfo/data/definitions/Europe/Prague.rb +0 -235
- data/test/tzinfo-data/tzinfo/data/definitions/UTC.rb +0 -16
- data/test/tzinfo-data/tzinfo/data/indexes/countries.rb +0 -940
- data/test/tzinfo-data/tzinfo/data/indexes/timezones.rb +0 -609
- data/test/tzinfo-data/tzinfo/data/version.rb +0 -20
- data/test/tzinfo-data/tzinfo/data.rb +0 -8
- data/test/zoneinfo/America/Argentina/Buenos_Aires +0 -0
- data/test/zoneinfo/America/New_York +0 -0
- data/test/zoneinfo/Australia/Melbourne +0 -0
- data/test/zoneinfo/EST +0 -0
- data/test/zoneinfo/Etc/UTC +0 -0
- data/test/zoneinfo/Europe/Amsterdam +0 -0
- data/test/zoneinfo/Europe/Andorra +0 -0
- data/test/zoneinfo/Europe/London +0 -0
- data/test/zoneinfo/Europe/Paris +0 -0
- data/test/zoneinfo/Europe/Prague +0 -0
- data/test/zoneinfo/Factory +0 -0
- data/test/zoneinfo/iso3166.tab +0 -274
- data/test/zoneinfo/leapseconds +0 -78
- data/test/zoneinfo/posix/Europe/London +0 -0
- data/test/zoneinfo/posixrules +0 -0
- data/test/zoneinfo/right/Europe/London +0 -0
- data/test/zoneinfo/zone.tab +0 -452
- data/test/zoneinfo/zone1970.tab +0 -384
- data/tzinfo.gemspec +0 -21
|
@@ -0,0 +1,548 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module TZInfo
|
|
5
|
+
# A time represented as an `Integer` number of seconds since 1970-01-01
|
|
6
|
+
# 00:00:00 UTC (ignoring leap seconds), the fraction through the second
|
|
7
|
+
# (sub_second as a `Rational`) and an optional UTC offset. Like Ruby's `Time`
|
|
8
|
+
# class, {Timestamp} can distinguish between a local time with a zero offset
|
|
9
|
+
# and a time specified explicitly as UTC.
|
|
10
|
+
class Timestamp
|
|
11
|
+
include Comparable
|
|
12
|
+
|
|
13
|
+
# The Unix epoch (1970-01-01 00:00:00 UTC) as a chronological Julian day
|
|
14
|
+
# number.
|
|
15
|
+
JD_EPOCH = 2440588
|
|
16
|
+
private_constant :JD_EPOCH
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
# Returns a new {Timestamp} representing the (Gregorian calendar) date and
|
|
20
|
+
# time specified by the supplied parameters.
|
|
21
|
+
#
|
|
22
|
+
# If `utc_offset` is `nil`, `:utc` or 0, the date and time parameters will
|
|
23
|
+
# be interpreted as representing a UTC date and time. Otherwise the date
|
|
24
|
+
# and time parameters will be interpreted as a local date and time with
|
|
25
|
+
# the given offset.
|
|
26
|
+
#
|
|
27
|
+
# @param year [Integer] the year.
|
|
28
|
+
# @param month [Integer] the month (1-12).
|
|
29
|
+
# @param day [Integer] the day of the month (1-31).
|
|
30
|
+
# @param hour [Integer] the hour (0-23).
|
|
31
|
+
# @param minute [Integer] the minute (0-59).
|
|
32
|
+
# @param second [Integer] the second (0-59).
|
|
33
|
+
# @param sub_second [Numeric] the fractional part of the second as either
|
|
34
|
+
# a `Rational` that is greater than or equal to 0 and less than 1, or
|
|
35
|
+
# the `Integer` 0.
|
|
36
|
+
# @param utc_offset [Object] either `nil` for a {Timestamp} without a
|
|
37
|
+
# specified offset, an offset from UTC specified as an `Integer` number
|
|
38
|
+
# of seconds or the `Symbol` `:utc`).
|
|
39
|
+
# @return [Timestamp] a new {Timestamp} representing the specified
|
|
40
|
+
# (Gregorian calendar) date and time.
|
|
41
|
+
# @raise [ArgumentError] if either of `year`, `month`, `day`, `hour`,
|
|
42
|
+
# `minute`, or `second` is not an `Integer`.
|
|
43
|
+
# @raise [ArgumentError] if `sub_second` is not a `Rational`, or the
|
|
44
|
+
# `Integer` 0.
|
|
45
|
+
# @raise [ArgumentError] if `utc_offset` is not `nil`, not an `Integer`
|
|
46
|
+
# and not the `Symbol` `:utc`.
|
|
47
|
+
# @raise [RangeError] if `month` is not between 1 and 12.
|
|
48
|
+
# @raise [RangeError] if `day` is not between 1 and 31.
|
|
49
|
+
# @raise [RangeError] if `hour` is not between 0 and 23.
|
|
50
|
+
# @raise [RangeError] if `minute` is not between 0 and 59.
|
|
51
|
+
# @raise [RangeError] if `second` is not between 0 and 59.
|
|
52
|
+
# @raise [RangeError] if `sub_second` is a `Rational` but that is less
|
|
53
|
+
# than 0 or greater than or equal to 1.
|
|
54
|
+
def create(year, month = 1, day = 1, hour = 0, minute = 0, second = 0, sub_second = 0, utc_offset = nil)
|
|
55
|
+
raise ArgumentError, 'year must be an Integer' unless year.kind_of?(Integer)
|
|
56
|
+
raise ArgumentError, 'month must be an Integer' unless month.kind_of?(Integer)
|
|
57
|
+
raise ArgumentError, 'day must be an Integer' unless day.kind_of?(Integer)
|
|
58
|
+
raise ArgumentError, 'hour must be an Integer' unless hour.kind_of?(Integer)
|
|
59
|
+
raise ArgumentError, 'minute must be an Integer' unless minute.kind_of?(Integer)
|
|
60
|
+
raise ArgumentError, 'second must be an Integer' unless second.kind_of?(Integer)
|
|
61
|
+
raise RangeError, 'month must be between 1 and 12' if month < 1 || month > 12
|
|
62
|
+
raise RangeError, 'day must be between 1 and 31' if day < 1 || day > 31
|
|
63
|
+
raise RangeError, 'hour must be between 0 and 23' if hour < 0 || hour > 23
|
|
64
|
+
raise RangeError, 'minute must be between 0 and 59' if minute < 0 || minute > 59
|
|
65
|
+
raise RangeError, 'second must be between 0 and 59' if second < 0 || second > 59
|
|
66
|
+
|
|
67
|
+
# Based on days_from_civil from https://howardhinnant.github.io/date_algorithms.html#days_from_civil
|
|
68
|
+
after_february = month > 2
|
|
69
|
+
year -= 1 unless after_february
|
|
70
|
+
era = year / 400
|
|
71
|
+
year_of_era = year - era * 400
|
|
72
|
+
day_of_year = (153 * (month + (after_february ? -3 : 9)) + 2) / 5 + day - 1
|
|
73
|
+
day_of_era = year_of_era * 365 + year_of_era / 4 - year_of_era / 100 + day_of_year
|
|
74
|
+
days_since_epoch = era * 146097 + day_of_era - 719468
|
|
75
|
+
value = ((days_since_epoch * 24 + hour) * 60 + minute) * 60 + second
|
|
76
|
+
value -= utc_offset if utc_offset.kind_of?(Integer)
|
|
77
|
+
|
|
78
|
+
new(value, sub_second, utc_offset)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# When used without a block, returns a {Timestamp} representation of a
|
|
82
|
+
# given `Time`, `DateTime` or {Timestamp}.
|
|
83
|
+
#
|
|
84
|
+
# When called with a block, the {Timestamp} representation of `value` is
|
|
85
|
+
# passed to the block. The block must then return a {Timestamp}, which
|
|
86
|
+
# will be converted back to the type of the initial value. If the initial
|
|
87
|
+
# value was a {Timestamp}, the block result will just be returned.
|
|
88
|
+
#
|
|
89
|
+
# The UTC offset of `value` can either be preserved (the {Timestamp}
|
|
90
|
+
# representation will have the same UTC offset as `value`), ignored (the
|
|
91
|
+
# {Timestamp} representation will have no defined UTC offset), or treated
|
|
92
|
+
# as though it were UTC (the {Timestamp} representation will have a
|
|
93
|
+
# {utc_offset} of 0 and {utc?} will return `true`).
|
|
94
|
+
#
|
|
95
|
+
# @param value [Object] a `Time`, `DateTime` or {Timestamp}.
|
|
96
|
+
# @param offset [Symbol] either `:preserve` to preserve the offset of
|
|
97
|
+
# `value`, `:ignore` to ignore the offset of `value` and create a
|
|
98
|
+
# {Timestamp} with an unspecified offset, or `:treat_as_utc` to treat
|
|
99
|
+
# the offset of `value` as though it were UTC and create a UTC
|
|
100
|
+
# {Timestamp}.
|
|
101
|
+
# @yield [timestamp] if a block is provided, the {Timestamp}
|
|
102
|
+
# representation is passed to the block.
|
|
103
|
+
# @yieldparam timestamp [Timestamp] the {Timestamp} representation of
|
|
104
|
+
# `value`.
|
|
105
|
+
# @yieldreturn [Timestamp] a {Timestamp} to be converted back to the type
|
|
106
|
+
# of `value`.
|
|
107
|
+
# @return [Object] if called without a block, the {Timestamp}
|
|
108
|
+
# representation of `value`, otherwise the result of the block,
|
|
109
|
+
# converted back to the type of `value`.
|
|
110
|
+
def for(value, offset = :preserve)
|
|
111
|
+
raise ArgumentError, 'value must be specified' unless value
|
|
112
|
+
|
|
113
|
+
case offset
|
|
114
|
+
when :ignore
|
|
115
|
+
ignore_offset = true
|
|
116
|
+
target_utc_offset = nil
|
|
117
|
+
when :treat_as_utc
|
|
118
|
+
ignore_offset = true
|
|
119
|
+
target_utc_offset = :utc
|
|
120
|
+
when :preserve
|
|
121
|
+
ignore_offset = false
|
|
122
|
+
target_utc_offset = nil
|
|
123
|
+
else
|
|
124
|
+
raise ArgumentError, 'offset must be :preserve, :ignore or :treat_as_utc'
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
time_like = false
|
|
128
|
+
timestamp = case value
|
|
129
|
+
when Time
|
|
130
|
+
for_time(value, ignore_offset, target_utc_offset)
|
|
131
|
+
when DateTime
|
|
132
|
+
for_datetime(value, ignore_offset, target_utc_offset)
|
|
133
|
+
when Timestamp
|
|
134
|
+
for_timestamp(value, ignore_offset, target_utc_offset)
|
|
135
|
+
else
|
|
136
|
+
raise ArgumentError, "#{value.class} values are not supported" unless is_time_like?(value)
|
|
137
|
+
time_like = true
|
|
138
|
+
for_time_like(value, ignore_offset, target_utc_offset)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
if block_given?
|
|
142
|
+
result = yield timestamp
|
|
143
|
+
raise ArgumentError, 'block must return a Timestamp' unless result.kind_of?(Timestamp)
|
|
144
|
+
|
|
145
|
+
case value
|
|
146
|
+
when Time
|
|
147
|
+
result.to_time
|
|
148
|
+
when DateTime
|
|
149
|
+
result.to_datetime
|
|
150
|
+
else # A Time-like value or a Timestamp
|
|
151
|
+
time_like ? result.to_time : result
|
|
152
|
+
end
|
|
153
|
+
else
|
|
154
|
+
timestamp
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Creates a new UTC {Timestamp}.
|
|
159
|
+
#
|
|
160
|
+
# @param value [Integer] the number of seconds since 1970-01-01 00:00:00
|
|
161
|
+
# UTC ignoring leap seconds.
|
|
162
|
+
# @param sub_second [Numeric] the fractional part of the second as either
|
|
163
|
+
# a `Rational` that is greater than or equal to 0 and less than 1, or
|
|
164
|
+
# the `Integer` 0.
|
|
165
|
+
# @raise [ArgumentError] if `value` is not an `Integer`.
|
|
166
|
+
# @raise [ArgumentError] if `sub_second` is not a `Rational`, or the
|
|
167
|
+
# `Integer` 0.
|
|
168
|
+
# @raise [RangeError] if `sub_second` is a `Rational` but that is less
|
|
169
|
+
# than 0 or greater than or equal to 1.
|
|
170
|
+
def utc(value, sub_second = 0)
|
|
171
|
+
new(value, sub_second, :utc)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
private
|
|
175
|
+
|
|
176
|
+
# Constructs a new instance of `self` (i.e. {Timestamp} or a subclass of
|
|
177
|
+
# {Timestamp}) without validating the parameters. This method is used
|
|
178
|
+
# internally within {Timestamp} to avoid the overhead of checking
|
|
179
|
+
# parameters.
|
|
180
|
+
#
|
|
181
|
+
# @param value [Integer] the number of seconds since 1970-01-01 00:00:00
|
|
182
|
+
# UTC ignoring leap seconds.
|
|
183
|
+
# @param sub_second [Numeric] the fractional part of the second as either
|
|
184
|
+
# a `Rational` that is greater than or equal to 0 and less than 1, or
|
|
185
|
+
# the `Integer` 0.
|
|
186
|
+
# @param utc_offset [Object] either `nil` for a {Timestamp} without a
|
|
187
|
+
# specified offset, an offset from UTC specified as an `Integer` number
|
|
188
|
+
# of seconds or the `Symbol` `:utc`).
|
|
189
|
+
# @return [Timestamp] a new instance of `self`.
|
|
190
|
+
def new!(value, sub_second = 0, utc_offset = nil)
|
|
191
|
+
result = allocate
|
|
192
|
+
result.send(:initialize!, value, sub_second, utc_offset)
|
|
193
|
+
result
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Creates a {Timestamp} that represents a given `Time`, optionally
|
|
197
|
+
# ignoring the offset.
|
|
198
|
+
#
|
|
199
|
+
# @param time [Time] a `Time`.
|
|
200
|
+
# @param ignore_offset [Boolean] whether to ignore the offset of `time`.
|
|
201
|
+
# @param target_utc_offset [Object] if `ignore_offset` is `true`, the UTC
|
|
202
|
+
# offset of the result (`:utc`, `nil` or an `Integer`).
|
|
203
|
+
# @return [Timestamp] the {Timestamp} representation of `time`.
|
|
204
|
+
def for_time(time, ignore_offset, target_utc_offset)
|
|
205
|
+
value = time.to_i
|
|
206
|
+
sub_second = time.subsec
|
|
207
|
+
|
|
208
|
+
if ignore_offset
|
|
209
|
+
utc_offset = target_utc_offset
|
|
210
|
+
value += time.utc_offset
|
|
211
|
+
elsif time.utc?
|
|
212
|
+
utc_offset = :utc
|
|
213
|
+
else
|
|
214
|
+
utc_offset = time.utc_offset
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
new!(value, sub_second, utc_offset)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Creates a {Timestamp} that represents a given `DateTime`, optionally
|
|
221
|
+
# ignoring the offset.
|
|
222
|
+
#
|
|
223
|
+
# @param datetime [DateTime] a `DateTime`.
|
|
224
|
+
# @param ignore_offset [Boolean] whether to ignore the offset of
|
|
225
|
+
# `datetime`.
|
|
226
|
+
# @param target_utc_offset [Object] if `ignore_offset` is `true`, the UTC
|
|
227
|
+
# offset of the result (`:utc`, `nil` or an `Integer`).
|
|
228
|
+
# @return [Timestamp] the {Timestamp} representation of `datetime`.
|
|
229
|
+
def for_datetime(datetime, ignore_offset, target_utc_offset)
|
|
230
|
+
value = (datetime.jd - JD_EPOCH) * 86400 + datetime.sec + datetime.min * 60 + datetime.hour * 3600
|
|
231
|
+
sub_second = datetime.sec_fraction
|
|
232
|
+
|
|
233
|
+
if ignore_offset
|
|
234
|
+
utc_offset = target_utc_offset
|
|
235
|
+
else
|
|
236
|
+
utc_offset = (datetime.offset * 86400).to_i
|
|
237
|
+
value -= utc_offset
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
new!(value, sub_second, utc_offset)
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# Returns a {Timestamp} that represents another {Timestamp}, optionally
|
|
244
|
+
# ignoring the offset. If the result would be identical to `value`, the
|
|
245
|
+
# same instance is returned. If the passed in value is an instance of a
|
|
246
|
+
# subclass of {Timestamp}, then a new {Timestamp} will always be returned.
|
|
247
|
+
#
|
|
248
|
+
# @param timestamp [Timestamp] a {Timestamp}.
|
|
249
|
+
# @param ignore_offset [Boolean] whether to ignore the offset of
|
|
250
|
+
# `timestamp`.
|
|
251
|
+
# @param target_utc_offset [Object] if `ignore_offset` is `true`, the UTC
|
|
252
|
+
# offset of the result (`:utc`, `nil` or an `Integer`).
|
|
253
|
+
# @return [Timestamp] a [Timestamp] representation of `timestamp`.
|
|
254
|
+
def for_timestamp(timestamp, ignore_offset, target_utc_offset)
|
|
255
|
+
if ignore_offset
|
|
256
|
+
if target_utc_offset
|
|
257
|
+
unless target_utc_offset == :utc && timestamp.utc? || timestamp.utc_offset == target_utc_offset
|
|
258
|
+
return new!(timestamp.value + (timestamp.utc_offset || 0), timestamp.sub_second, target_utc_offset)
|
|
259
|
+
end
|
|
260
|
+
elsif timestamp.utc_offset
|
|
261
|
+
return new!(timestamp.value + timestamp.utc_offset, timestamp.sub_second)
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
unless timestamp.instance_of?(Timestamp)
|
|
266
|
+
# timestamp is identical in value, sub_second and utc_offset but is a
|
|
267
|
+
# subclass (i.e. TimestampWithOffset). Return a new Timestamp
|
|
268
|
+
# instance.
|
|
269
|
+
return new!(timestamp.value, timestamp.sub_second, timestamp.utc? ? :utc : timestamp.utc_offset)
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
timestamp
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
# Determines if an object is like a `Time` (for the purposes of converting
|
|
276
|
+
# to a {Timestamp} with {for}), responding to `to_i` and `subsec`.
|
|
277
|
+
#
|
|
278
|
+
# @param value [Object] an object to test.
|
|
279
|
+
# @return [Boolean] `true` if the object is `Time`-like, otherwise
|
|
280
|
+
# `false`.
|
|
281
|
+
def is_time_like?(value)
|
|
282
|
+
value.respond_to?(:to_i) && value.respond_to?(:subsec)
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
# Creates a {Timestamp} that represents a given `Time`-like object,
|
|
286
|
+
# optionally ignoring the offset (if the `time_like` responds to
|
|
287
|
+
# `utc_offset`).
|
|
288
|
+
#
|
|
289
|
+
# @param time_like [Object] a `Time`-like object.
|
|
290
|
+
# @param ignore_offset [Boolean] whether to ignore the offset of `time`.
|
|
291
|
+
# @param target_utc_offset [Object] if `ignore_offset` is `true`, the UTC
|
|
292
|
+
# offset of the result (`:utc`, `nil` or an `Integer`).
|
|
293
|
+
# @return [Timestamp] the {Timestamp} representation of `time_like`.
|
|
294
|
+
def for_time_like(time_like, ignore_offset, target_utc_offset)
|
|
295
|
+
value = time_like.to_i
|
|
296
|
+
sub_second = time_like.subsec.to_r
|
|
297
|
+
|
|
298
|
+
if ignore_offset
|
|
299
|
+
utc_offset = target_utc_offset
|
|
300
|
+
value += time_like.utc_offset.to_i if time_like.respond_to?(:utc_offset)
|
|
301
|
+
elsif time_like.respond_to?(:utc_offset)
|
|
302
|
+
utc_offset = time_like.utc_offset.to_i
|
|
303
|
+
else
|
|
304
|
+
utc_offset = 0
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
new(value, sub_second, utc_offset)
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
# @return [Integer] the number of seconds since 1970-01-01 00:00:00 UTC
|
|
312
|
+
# ignoring leap seconds (i.e. each day is treated as if it were 86,400
|
|
313
|
+
# seconds long).
|
|
314
|
+
attr_reader :value
|
|
315
|
+
|
|
316
|
+
# @return [Numeric] the fraction of a second elapsed since timestamp as
|
|
317
|
+
# either a `Rational` or the `Integer` 0. Always greater than or equal to
|
|
318
|
+
# 0 and less than 1.
|
|
319
|
+
attr_reader :sub_second
|
|
320
|
+
|
|
321
|
+
# @return [Integer] the offset from UTC in seconds or `nil` if the
|
|
322
|
+
# {Timestamp} doesn't have a specified offset.
|
|
323
|
+
attr_reader :utc_offset
|
|
324
|
+
|
|
325
|
+
# Initializes a new {Timestamp}.
|
|
326
|
+
#
|
|
327
|
+
# @param value [Integer] the number of seconds since 1970-01-01 00:00:00 UTC
|
|
328
|
+
# ignoring leap seconds.
|
|
329
|
+
# @param sub_second [Numeric] the fractional part of the second as either a
|
|
330
|
+
# `Rational` that is greater than or equal to 0 and less than 1, or
|
|
331
|
+
# the `Integer` 0.
|
|
332
|
+
# @param utc_offset [Object] either `nil` for a {Timestamp} without a
|
|
333
|
+
# specified offset, an offset from UTC specified as an `Integer` number of
|
|
334
|
+
# seconds or the `Symbol` `:utc`).
|
|
335
|
+
# @raise [ArgumentError] if `value` is not an `Integer`.
|
|
336
|
+
# @raise [ArgumentError] if `sub_second` is not a `Rational`, or the
|
|
337
|
+
# `Integer` 0.
|
|
338
|
+
# @raise [RangeError] if `sub_second` is a `Rational` but that is less
|
|
339
|
+
# than 0 or greater than or equal to 1.
|
|
340
|
+
# @raise [ArgumentError] if `utc_offset` is not `nil`, not an `Integer` and
|
|
341
|
+
# not the `Symbol` `:utc`.
|
|
342
|
+
def initialize(value, sub_second = 0, utc_offset = nil)
|
|
343
|
+
raise ArgumentError, 'value must be an Integer' unless value.kind_of?(Integer)
|
|
344
|
+
raise ArgumentError, 'sub_second must be a Rational or the Integer 0' unless (sub_second.kind_of?(Integer) && sub_second == 0) || sub_second.kind_of?(Rational)
|
|
345
|
+
raise RangeError, 'sub_second must be >= 0 and < 1' if sub_second < 0 || sub_second >= 1
|
|
346
|
+
raise ArgumentError, 'utc_offset must be an Integer, :utc or nil' if utc_offset && utc_offset != :utc && !utc_offset.kind_of?(Integer)
|
|
347
|
+
initialize!(value, sub_second, utc_offset)
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
# @return [Boolean] `true` if this {Timestamp} represents UTC, `false` if
|
|
351
|
+
# the {Timestamp} wasn't specified as UTC or `nil` if the {Timestamp} has
|
|
352
|
+
# no specified offset.
|
|
353
|
+
def utc?
|
|
354
|
+
@utc
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
# Adds a number of seconds to the {Timestamp} value, setting the UTC offset
|
|
358
|
+
# of the result.
|
|
359
|
+
#
|
|
360
|
+
# @param seconds [Integer] the number of seconds to be added.
|
|
361
|
+
# @param utc_offset [Object] either `nil` for a {Timestamp} without a
|
|
362
|
+
# specified offset, an offset from UTC specified as an `Integer` number of
|
|
363
|
+
# seconds or the `Symbol` `:utc`).
|
|
364
|
+
# @return [Timestamp] the result of adding `seconds` to the
|
|
365
|
+
# {Timestamp} value as a new {Timestamp} instance with the chosen
|
|
366
|
+
# `utc_offset`.
|
|
367
|
+
# @raise [ArgumentError] if `seconds` is not an `Integer`.
|
|
368
|
+
# @raise [ArgumentError] if `utc_offset` is not `nil`, not an `Integer` and
|
|
369
|
+
# not the `Symbol` `:utc`.
|
|
370
|
+
def add_and_set_utc_offset(seconds, utc_offset)
|
|
371
|
+
raise ArgumentError, 'seconds must be an Integer' unless seconds.kind_of?(Integer)
|
|
372
|
+
raise ArgumentError, 'utc_offset must be an Integer, :utc or nil' if utc_offset && utc_offset != :utc && !utc_offset.kind_of?(Integer)
|
|
373
|
+
return self if seconds == 0 && utc_offset == (@utc ? :utc : @utc_offset)
|
|
374
|
+
Timestamp.send(:new!, @value + seconds, @sub_second, utc_offset)
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
# @return [Timestamp] a UTC {Timestamp} equivalent to this instance. Returns
|
|
378
|
+
# `self` if {#utc? self.utc?} is `true`.
|
|
379
|
+
def utc
|
|
380
|
+
return self if @utc
|
|
381
|
+
Timestamp.send(:new!, @value, @sub_second, :utc)
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
# Converts this {Timestamp} to a `Time`.
|
|
385
|
+
#
|
|
386
|
+
# @return [Time] a `Time` representation of this {Timestamp}. If the UTC
|
|
387
|
+
# offset of this {Timestamp} is not specified, a UTC `Time` will be
|
|
388
|
+
# returned.
|
|
389
|
+
def to_time
|
|
390
|
+
time = new_time
|
|
391
|
+
|
|
392
|
+
if @utc_offset && !@utc
|
|
393
|
+
time.localtime(@utc_offset)
|
|
394
|
+
else
|
|
395
|
+
time.utc
|
|
396
|
+
end
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
# Converts this {Timestamp} to a `DateTime`.
|
|
400
|
+
#
|
|
401
|
+
# @return [DateTime] a DateTime representation of this {Timestamp}. If the
|
|
402
|
+
# UTC offset of this {Timestamp} is not specified, a UTC `DateTime` will
|
|
403
|
+
# be returned.
|
|
404
|
+
def to_datetime
|
|
405
|
+
new_datetime
|
|
406
|
+
end
|
|
407
|
+
|
|
408
|
+
# Converts this {Timestamp} to an `Integer` number of seconds since
|
|
409
|
+
# 1970-01-01 00:00:00 UTC (ignoring leap seconds).
|
|
410
|
+
#
|
|
411
|
+
# @return [Integer] an Integer representation of this {Timestamp} (the
|
|
412
|
+
# number of seconds since 1970-01-01 00:00:00 UTC ignoring leap seconds).
|
|
413
|
+
def to_i
|
|
414
|
+
value
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
# Formats this {Timestamp} according to the directives in the given format
|
|
418
|
+
# string.
|
|
419
|
+
#
|
|
420
|
+
# @param format [String] the format string. Please refer to `Time#strftime`
|
|
421
|
+
# for a list of supported format directives.
|
|
422
|
+
# @return [String] the formatted {Timestamp}.
|
|
423
|
+
# @raise [ArgumentError] if `format` is not specified.
|
|
424
|
+
def strftime(format)
|
|
425
|
+
raise ArgumentError, 'format must be specified' unless format
|
|
426
|
+
to_time.strftime(format)
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
# @return [String] a `String` representation of this {Timestamp}.
|
|
430
|
+
def to_s
|
|
431
|
+
return value_and_sub_second_to_s unless @utc_offset
|
|
432
|
+
return "#{value_and_sub_second_to_s} UTC" if @utc
|
|
433
|
+
|
|
434
|
+
sign = @utc_offset >= 0 ? '+' : '-'
|
|
435
|
+
min, sec = @utc_offset.abs.divmod(60)
|
|
436
|
+
hour, min = min.divmod(60)
|
|
437
|
+
|
|
438
|
+
"#{value_and_sub_second_to_s(@utc_offset)} #{sign}#{'%02d' % hour}:#{'%02d' % min}#{sec > 0 ? ':%02d' % sec : nil}#{@utc_offset != 0 ? " (#{value_and_sub_second_to_s} UTC)" : nil}"
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
# Compares this {Timestamp} with another.
|
|
442
|
+
#
|
|
443
|
+
# {Timestamp} instances without a defined UTC offset are not comparable with
|
|
444
|
+
# {Timestamp} instances that have a defined UTC offset.
|
|
445
|
+
#
|
|
446
|
+
# @param t [Timestamp] the {Timestamp} to compare this instance with.
|
|
447
|
+
# @return [Integer] -1, 0 or 1 depending if this instance is earlier, equal
|
|
448
|
+
# or later than `t` respectively. Returns `nil` when comparing a
|
|
449
|
+
# {Timestamp} that does not have a defined UTC offset with a {Timestamp}
|
|
450
|
+
# that does have a defined UTC offset. Returns `nil` if `t` is not a
|
|
451
|
+
# {Timestamp}.
|
|
452
|
+
def <=>(t)
|
|
453
|
+
return nil unless t.kind_of?(Timestamp)
|
|
454
|
+
return nil if utc_offset && !t.utc_offset
|
|
455
|
+
return nil if !utc_offset && t.utc_offset
|
|
456
|
+
|
|
457
|
+
result = value <=> t.value
|
|
458
|
+
result = sub_second <=> t.sub_second if result == 0
|
|
459
|
+
result
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
alias eql? ==
|
|
463
|
+
|
|
464
|
+
# @return [Integer] a hash based on the value, sub-second and whether there
|
|
465
|
+
# is a defined UTC offset.
|
|
466
|
+
def hash
|
|
467
|
+
[@value, @sub_second, !!@utc_offset].hash
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
# @return [String] the internal object state as a programmer-readable
|
|
471
|
+
# `String`.
|
|
472
|
+
def inspect
|
|
473
|
+
"#<#{self.class}: @value=#{@value}, @sub_second=#{@sub_second}, @utc_offset=#{@utc_offset.inspect}, @utc=#{@utc.inspect}>"
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
protected
|
|
477
|
+
|
|
478
|
+
# Creates a new instance of a `Time` or `Time`-like class matching the
|
|
479
|
+
# {value} and {sub_second} of this {Timestamp}, but not setting the offset.
|
|
480
|
+
#
|
|
481
|
+
# @param klass [Class] the class to instantiate.
|
|
482
|
+
#
|
|
483
|
+
# @private
|
|
484
|
+
def new_time(klass = Time)
|
|
485
|
+
klass.at(@value, @sub_second * 1_000_000)
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
# Constructs a new instance of a `DateTime` or `DateTime`-like class with
|
|
489
|
+
# the same {value}, {sub_second} and {utc_offset} as this {Timestamp}.
|
|
490
|
+
#
|
|
491
|
+
# @param klass [Class] the class to instantiate.
|
|
492
|
+
#
|
|
493
|
+
# @private
|
|
494
|
+
def new_datetime(klass = DateTime)
|
|
495
|
+
datetime = klass.jd(JD_EPOCH + ((@value.to_r + @sub_second) / 86400))
|
|
496
|
+
@utc_offset && @utc_offset != 0 ? datetime.new_offset(Rational(@utc_offset, 86400)) : datetime
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
private
|
|
500
|
+
|
|
501
|
+
# Converts the value and sub-seconds to a `String`, adding on the given
|
|
502
|
+
# offset.
|
|
503
|
+
#
|
|
504
|
+
# @param offset [Integer] the offset to add to the value.
|
|
505
|
+
# @return [String] the value and sub-seconds.
|
|
506
|
+
def value_and_sub_second_to_s(offset = 0)
|
|
507
|
+
"#{@value + offset}#{sub_second_to_s}"
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
# Converts the {sub_second} value to a `String` suitable for appending to
|
|
511
|
+
# the `String` representation of a {Timestamp}.
|
|
512
|
+
#
|
|
513
|
+
# @return [String] a `String` representation of {sub_second}.
|
|
514
|
+
def sub_second_to_s
|
|
515
|
+
if @sub_second == 0
|
|
516
|
+
''
|
|
517
|
+
else
|
|
518
|
+
" #{@sub_second.numerator}/#{@sub_second.denominator}"
|
|
519
|
+
end
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
# Initializes a new {Timestamp} without validating the parameters. This
|
|
523
|
+
# method is used internally within {Timestamp} to avoid the overhead of
|
|
524
|
+
# checking parameters.
|
|
525
|
+
#
|
|
526
|
+
# @param value [Integer] the number of seconds since 1970-01-01 00:00:00 UTC
|
|
527
|
+
# ignoring leap seconds.
|
|
528
|
+
# @param sub_second [Numeric] the fractional part of the second as either a
|
|
529
|
+
# `Rational` that is greater than or equal to 0 and less than 1, or the
|
|
530
|
+
# `Integer` 0.
|
|
531
|
+
# @param utc_offset [Object] either `nil` for a {Timestamp} without a
|
|
532
|
+
# specified offset, an offset from UTC specified as an `Integer` number of
|
|
533
|
+
# seconds or the `Symbol` `:utc`).
|
|
534
|
+
def initialize!(value, sub_second = 0, utc_offset = nil)
|
|
535
|
+
@value = value
|
|
536
|
+
|
|
537
|
+
# Convert Rational(0,1) to 0.
|
|
538
|
+
@sub_second = sub_second == 0 ? 0 : sub_second
|
|
539
|
+
|
|
540
|
+
if utc_offset
|
|
541
|
+
@utc = utc_offset == :utc
|
|
542
|
+
@utc_offset = @utc ? 0 : utc_offset
|
|
543
|
+
else
|
|
544
|
+
@utc = @utc_offset = nil
|
|
545
|
+
end
|
|
546
|
+
end
|
|
547
|
+
end
|
|
548
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
module TZInfo
|
|
5
|
+
# A subclass of {Timestamp} used to represent local times.
|
|
6
|
+
# {TimestampWithOffset} holds a reference to the related {TimezoneOffset} and
|
|
7
|
+
# overrides various methods to return results appropriate for the
|
|
8
|
+
# {TimezoneOffset}. Certain operations will clear the associated
|
|
9
|
+
# {TimezoneOffset} (if the {TimezoneOffset} would not necessarily be valid for
|
|
10
|
+
# the result). Once the {TimezoneOffset} has been cleared,
|
|
11
|
+
# {TimestampWithOffset} behaves identically to {Timestamp}.
|
|
12
|
+
class TimestampWithOffset < Timestamp
|
|
13
|
+
include WithOffset
|
|
14
|
+
|
|
15
|
+
# @return [TimezoneOffset] the {TimezoneOffset} associated with this
|
|
16
|
+
# instance.
|
|
17
|
+
attr_reader :timezone_offset
|
|
18
|
+
|
|
19
|
+
# Creates a new {TimestampWithOffset} from a given {Timestamp} and
|
|
20
|
+
# {TimezoneOffset}.
|
|
21
|
+
#
|
|
22
|
+
# @param timestamp [Timestamp] a {Timestamp}.
|
|
23
|
+
# @param timezone_offset [TimezoneOffset] a {TimezoneOffset} valid at the
|
|
24
|
+
# time of `timestamp`.
|
|
25
|
+
# @return [TimestampWithOffset] a {TimestampWithOffset} that has the same
|
|
26
|
+
# {value value} and {sub_second sub_second} as the `timestamp` parameter,
|
|
27
|
+
# a {utc_offset utc_offset} equal to the
|
|
28
|
+
# {TimezoneOffset#observed_utc_offset observed_utc_offset} of the
|
|
29
|
+
# `timezone_offset` parameter and {timezone_offset timezone_offset} set to
|
|
30
|
+
# the `timezone_offset` parameter.
|
|
31
|
+
# @raise [ArgumentError] if `timestamp` or `timezone_offset` is `nil`.
|
|
32
|
+
def self.set_timezone_offset(timestamp, timezone_offset)
|
|
33
|
+
raise ArgumentError, 'timestamp must be specified' unless timestamp
|
|
34
|
+
raise ArgumentError, 'timezone_offset must be specified' unless timezone_offset
|
|
35
|
+
new!(timestamp.value, timestamp.sub_second, timezone_offset.observed_utc_offset).set_timezone_offset(timezone_offset)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Sets the associated {TimezoneOffset} of this {TimestampWithOffset}.
|
|
39
|
+
#
|
|
40
|
+
# @param timezone_offset [TimezoneOffset] a {TimezoneOffset} valid at the time
|
|
41
|
+
# and for the offset of this {TimestampWithOffset}.
|
|
42
|
+
# @return [TimestampWithOffset] `self`.
|
|
43
|
+
# @raise [ArgumentError] if `timezone_offset` is `nil`.
|
|
44
|
+
# @raise [ArgumentError] if {utc? self.utc?} is `true`.
|
|
45
|
+
# @raise [ArgumentError] if `timezone_offset.observed_utc_offset` does not equal
|
|
46
|
+
# `self.utc_offset`.
|
|
47
|
+
def set_timezone_offset(timezone_offset)
|
|
48
|
+
raise ArgumentError, 'timezone_offset must be specified' unless timezone_offset
|
|
49
|
+
raise ArgumentError, 'timezone_offset.observed_utc_offset does not match self.utc_offset' if utc? || utc_offset != timezone_offset.observed_utc_offset
|
|
50
|
+
@timezone_offset = timezone_offset
|
|
51
|
+
self
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# An overridden version of {Timestamp#to_time} that, if there is an
|
|
55
|
+
# associated {TimezoneOffset}, returns a {TimeWithOffset} with that offset.
|
|
56
|
+
#
|
|
57
|
+
# @return [Time] if there is an associated {TimezoneOffset}, a
|
|
58
|
+
# {TimeWithOffset} representation of this {TimestampWithOffset}, otherwise
|
|
59
|
+
# a `Time` representation.
|
|
60
|
+
def to_time
|
|
61
|
+
to = timezone_offset
|
|
62
|
+
if to
|
|
63
|
+
new_time(TimeWithOffset).set_timezone_offset(to)
|
|
64
|
+
else
|
|
65
|
+
super
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# An overridden version of {Timestamp#to_datetime}, if there is an
|
|
70
|
+
# associated {TimezoneOffset}, returns a {DateTimeWithOffset} with that
|
|
71
|
+
# offset.
|
|
72
|
+
#
|
|
73
|
+
# @return [DateTime] if there is an associated {TimezoneOffset}, a
|
|
74
|
+
# {DateTimeWithOffset} representation of this {TimestampWithOffset},
|
|
75
|
+
# otherwise a `DateTime` representation.
|
|
76
|
+
def to_datetime
|
|
77
|
+
to = timezone_offset
|
|
78
|
+
if to
|
|
79
|
+
new_datetime(DateTimeWithOffset).set_timezone_offset(to)
|
|
80
|
+
else
|
|
81
|
+
super
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|