2 # Copyright (c) 2006 Philip Ross
4 # Permission is hereby granted, free of charge, to any person obtaining a copy
5 # of this software and associated documentation files (the "Software"), to deal
6 # in the Software without restriction, including without limitation the rights
7 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 # copies of the Software, and to permit persons to whom the Software is
9 # furnished to do so, subject to the following conditions:
11 # The above copyright notice and this permission notice shall be included in all
12 # copies or substantial portions of the Software.
14 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 require 'tzinfo/offset_rationals'
28 # Used by TZInfo internally to represent either a Time, DateTime or integer
29 # timestamp (seconds since 1970-01-01 00:00:00).
30 class TimeOrDateTime
#:nodoc:
33 # Constructs a new TimeOrDateTime. timeOrDateTime can be a Time, DateTime
34 # or an integer. If using a Time or DateTime, any time zone information is
36 def initialize(timeOrDateTime
)
41 if timeOrDateTime
.is_a
?(Time
)
42 @time = timeOrDateTime
43 @time = Time
.utc(@time.year
, @time.mon
, @time.mday
, @time.hour
, @time.min
, @time.sec
) unless @time.zone
== 'UTC'
45 elsif timeOrDateTime
.is_a
?(DateTime
)
46 @datetime = timeOrDateTime
47 @datetime = @datetime.new_offset(0) unless @datetime.offset
== 0
50 @timestamp = timeOrDateTime
.to_i
55 # Returns the time as a Time.
59 @time = Time
.at(@timestamp).utc
61 @time = Time
.utc(year
, mon
, mday
, hour
, min
, sec
)
68 # Returns the time as a DateTime.
71 @datetime = DateTime
.new(year
, mon
, mday
, hour
, min
, sec
)
77 # Returns the time as an integer timestamp.
80 @timestamp = to_time
.to_i
86 # Returns the time as the original time passed to new.
91 # Returns a string representation of the TimeOrDateTime.
95 elsif @orig.is_a
?(DateTime
)
96 "DateTime: #{@orig.to_s}"
98 "Timestamp: #{@orig.to_s}"
102 # Returns internal object state as a programmer-readable string.
104 "#<#{self.class}: #{@orig.inspect}>"
118 # Returns the month of the year (1..12).
130 # Returns the day of the month (1..n).
142 # Returns the hour of the day (0..23).
153 # Returns the minute of the hour (0..59).
164 # Returns the second of the minute (0..60). (60 for a leap second).
175 # Compares this TimeOrDateTime with another Time, DateTime, integer
176 # timestamp or TimeOrDateTime. Returns -1, 0 or +1 depending whether the
177 # receiver is less than, equal to, or greater than timeOrDateTime.
179 # Milliseconds and smaller units are ignored in the comparison.
180 def <=>(timeOrDateTime
)
181 if timeOrDateTime
.is_a
?(TimeOrDateTime
)
182 orig
= timeOrDateTime
.to_orig
184 if @orig.is_a
?(DateTime
) || orig
.is_a
?(DateTime
)
185 # If either is a DateTime, assume it is there for a reason
187 to_datetime
<=> timeOrDateTime
.to_datetime
188 elsif orig
.is_a
?(Time
)
189 to_time
<=> timeOrDateTime
.to_time
191 to_i
<=> timeOrDateTime
.to_i
193 elsif @orig.is_a
?(DateTime
) || timeOrDateTime
.is_a
?(DateTime
)
194 # If either is a DateTime, assume it is there for a reason
196 to_datetime
<=> TimeOrDateTime
.wrap(timeOrDateTime
).to_datetime
197 elsif timeOrDateTime
.is_a
?(Time
)
198 to_time
<=> timeOrDateTime
200 to_i
<=> timeOrDateTime
.to_i
204 # Adds a number of seconds to the TimeOrDateTime. Returns a new
205 # TimeOrDateTime, preserving what the original constructed type was.
206 # If the original type is a Time and the resulting calculation goes out of
207 # range for Times, then an exception will be raised by the Time class.
212 if @orig.is_a
?(DateTime
)
213 TimeOrDateTime
.new(@orig + OffsetRationals
.rational_for_offset(seconds
))
215 # + defined for Time and integer timestamps
216 TimeOrDateTime
.new(@orig + seconds
)
221 # Subtracts a number of seconds from the TimeOrDateTime. Returns a new
222 # TimeOrDateTime, preserving what the original constructed type was.
223 # If the original type is a Time and the resulting calculation goes out of
224 # range for Times, then an exception will be raised by the Time class.
229 # Similar to the + operator, but for cases where adding would cause a
230 # timestamp or time to go out of the allowed range, converts to a DateTime
231 # based TimeOrDateTime.
232 def add_with_convert(seconds
)
236 if @orig.is_a
?(DateTime
)
237 TimeOrDateTime
.new(@orig + OffsetRationals
.rational_for_offset(seconds
))
239 # A Time or timestamp.
240 result
= to_i
+ seconds
242 if result
< 0 || result
> 2147483647
243 result
= TimeOrDateTime
.new(to_datetime
+ OffsetRationals
.rational_for_offset(seconds
))
245 result
= TimeOrDateTime
.new(@orig + seconds
)
251 # Returns true if todt represents the same time and was originally
252 # constructed with the same type (DateTime, Time or timestamp) as this
255 todt
.respond_to
?(:to_orig) && to_orig
.eql
?(todt
.to_orig
)
258 # Returns a hash of this TimeOrDateTime.
263 # If no block is given, returns a TimeOrDateTime wrapping the given
264 # timeOrDateTime. If a block is specified, a TimeOrDateTime is constructed
265 # and passed to the block. The result of the block must be a TimeOrDateTime.
266 # to_orig will be called on the result and the result of to_orig will be
269 # timeOrDateTime can be a Time, DateTime, integer timestamp or TimeOrDateTime.
270 # If a TimeOrDateTime is passed in, no new TimeOrDateTime will be constructed,
271 # the passed in value will be used.
272 def self.wrap(timeOrDateTime
)
273 t
= timeOrDateTime
.is_a
?(TimeOrDateTime
) ? timeOrDateTime
: TimeOrDateTime
.new(timeOrDateTime
)
278 if timeOrDateTime
.is_a
?(TimeOrDateTime
)
280 elsif timeOrDateTime
.is_a
?(Time
)
282 elsif timeOrDateTime
.is_a
?(DateTime
)