File: datetime_with_offset.rb

package info (click to toggle)
ruby-tzinfo 2.0.6-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,824 kB
  • sloc: ruby: 21,667; makefile: 6
file content (153 lines) | stat: -rw-r--r-- 5,423 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# 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