File: //usr/local/rvm/gems/ruby-3.0.2/gems/tzinfo-2.0.4/lib/tzinfo/datetime_with_offset.rb
# encoding: UTF-8
# frozen_string_literal: true
require 'date'
module TZInfo
# A subclass of `DateTime` used to represent local times. {DateTimeWithOffset}
# holds a reference to the related {TimezoneOffset} and overrides various
# methods to return results appropriate for the {TimezoneOffset}. Certain
# operations will clear the associated {TimezoneOffset} (if the
# {TimezoneOffset} would not necessarily be valid for the result). Once the
# {TimezoneOffset} has been cleared, {DateTimeWithOffset} behaves identically
# to `DateTime`.
#
# Arithmetic performed on {DateTimeWithOffset} instances is _not_ time
# zone-aware. Regardless of whether transitions in the time zone are crossed,
# results of arithmetic operations will always maintain the same offset from
# UTC (`offset`). The associated {TimezoneOffset} will aways be cleared.
class DateTimeWithOffset < DateTime
include WithOffset
# @return [TimezoneOffset] the {TimezoneOffset} associated with this
# instance.
attr_reader :timezone_offset
# Sets the associated {TimezoneOffset}.
#
# @param timezone_offset [TimezoneOffset] a {TimezoneOffset} valid at the
# time and for the offset of this {DateTimeWithOffset}.
# @return [DateTimeWithOffset] `self`.
# @raise [ArgumentError] if `timezone_offset` is `nil`.
# @raise [ArgumentError] if `timezone_offset.observed_utc_offset` does not
# equal `self.offset * 86400`.
def set_timezone_offset(timezone_offset)
raise ArgumentError, 'timezone_offset must be specified' unless timezone_offset
raise ArgumentError, 'timezone_offset.observed_utc_offset does not match self.utc_offset' if offset * 86400 != timezone_offset.observed_utc_offset
@timezone_offset = timezone_offset
self
end
# An overridden version of `DateTime#to_time` that, if there is an
# associated {TimezoneOffset}, returns a {DateTimeWithOffset} with that
# offset.
#
# @return [Time] if there is an associated {TimezoneOffset}, a
# {TimeWithOffset} representation of this {DateTimeWithOffset}, otherwise
# a `Time` representation.
def to_time
if_timezone_offset(super) do |o,t|
# Ruby 2.4.0 changed the behaviour of to_time so that it preserves the
# offset instead of converting to the system local timezone.
#
# When self has an associated TimezonePeriod, this implementation will
# preserve the offset on all versions of Ruby.
TimeWithOffset.at(t.to_i, t.subsec * 1_000_000).set_timezone_offset(o)
end
end
# An overridden version of `DateTime#downto` that clears the associated
# {TimezoneOffset} of the returned or yielded instances.
def downto(min)
if block_given?
super {|dt| yield dt.clear_timezone_offset }
else
enum = super
enum.each {|dt| dt.clear_timezone_offset }
enum
end
end
# An overridden version of `DateTime#england` that preserves the associated
# {TimezoneOffset}.
#
# @return [DateTime]
def england
# super doesn't call #new_start on MRI, so each method has to be
# individually overridden.
if_timezone_offset(super) {|o,dt| dt.set_timezone_offset(o) }
end
# An overridden version of `DateTime#gregorian` that preserves the
# associated {TimezoneOffset}.
#
# @return [DateTime]
def gregorian
# super doesn't call #new_start on MRI, so each method has to be
# individually overridden.
if_timezone_offset(super) {|o,dt| dt.set_timezone_offset(o) }
end
# An overridden version of `DateTime#italy` that preserves the associated
# {TimezoneOffset}.
#
# @return [DateTime]
def italy
# super doesn't call #new_start on MRI, so each method has to be
# individually overridden.
if_timezone_offset(super) {|o,dt| dt.set_timezone_offset(o) }
end
# An overridden version of `DateTime#julian` that preserves the associated
# {TimezoneOffset}.
#
# @return [DateTime]
def julian
# super doesn't call #new_start on MRI, so each method has to be
# individually overridden.
if_timezone_offset(super) {|o,dt| dt.set_timezone_offset(o) }
end
# An overridden version of `DateTime#new_start` that preserves the
# associated {TimezoneOffset}.
#
# @return [DateTime]
def new_start(start = Date::ITALY)
if_timezone_offset(super) {|o,dt| dt.set_timezone_offset(o) }
end
# An overridden version of `DateTime#step` that clears the associated
# {TimezoneOffset} of the returned or yielded instances.
def step(limit, step = 1)
if block_given?
super {|dt| yield dt.clear_timezone_offset }
else
enum = super
enum.each {|dt| dt.clear_timezone_offset }
enum
end
end
# An overridden version of `DateTime#upto` that clears the associated
# {TimezoneOffset} of the returned or yielded instances.
def upto(max)
if block_given?
super {|dt| yield dt.clear_timezone_offset }
else
enum = super
enum.each {|dt| dt.clear_timezone_offset }
enum
end
end
protected
# Clears the associated {TimezoneOffset}.
#
# @return [DateTimeWithOffset] `self`.
def clear_timezone_offset
@timezone_offset = nil
self
end
end
end