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.
Files changed (151) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.yardopts +3 -0
  4. data/CHANGES.md +469 -431
  5. data/LICENSE +13 -13
  6. data/README.md +368 -114
  7. data/lib/tzinfo/country.rb +131 -129
  8. data/lib/tzinfo/country_timezone.rb +70 -112
  9. data/lib/tzinfo/data_source.rb +389 -144
  10. data/lib/tzinfo/data_sources/constant_offset_data_timezone_info.rb +56 -0
  11. data/lib/tzinfo/data_sources/country_info.rb +42 -0
  12. data/lib/tzinfo/data_sources/data_timezone_info.rb +91 -0
  13. data/lib/tzinfo/data_sources/linked_timezone_info.rb +33 -0
  14. data/lib/tzinfo/data_sources/ruby_data_source.rb +141 -0
  15. data/lib/tzinfo/data_sources/timezone_info.rb +47 -0
  16. data/lib/tzinfo/data_sources/transitions_data_timezone_info.rb +214 -0
  17. data/lib/tzinfo/data_sources/zoneinfo_data_source.rb +573 -0
  18. data/lib/tzinfo/data_sources/zoneinfo_reader.rb +284 -0
  19. data/lib/tzinfo/data_sources.rb +8 -0
  20. data/lib/tzinfo/data_timezone.rb +33 -47
  21. data/lib/tzinfo/datetime_with_offset.rb +153 -0
  22. data/lib/tzinfo/format1/country_definer.rb +17 -0
  23. data/lib/tzinfo/format1/country_index_definition.rb +64 -0
  24. data/lib/tzinfo/format1/timezone_definer.rb +64 -0
  25. data/lib/tzinfo/format1/timezone_definition.rb +39 -0
  26. data/lib/tzinfo/format1/timezone_index_definition.rb +77 -0
  27. data/lib/tzinfo/format1.rb +10 -0
  28. data/lib/tzinfo/format2/country_definer.rb +68 -0
  29. data/lib/tzinfo/format2/country_index_definer.rb +68 -0
  30. data/lib/tzinfo/format2/country_index_definition.rb +46 -0
  31. data/lib/tzinfo/format2/timezone_definer.rb +94 -0
  32. data/lib/tzinfo/format2/timezone_definition.rb +73 -0
  33. data/lib/tzinfo/format2/timezone_index_definer.rb +45 -0
  34. data/lib/tzinfo/format2/timezone_index_definition.rb +55 -0
  35. data/lib/tzinfo/format2.rb +10 -0
  36. data/lib/tzinfo/info_timezone.rb +26 -21
  37. data/lib/tzinfo/linked_timezone.rb +33 -52
  38. data/lib/tzinfo/offset_timezone_period.rb +42 -0
  39. data/lib/tzinfo/string_deduper.rb +118 -0
  40. data/lib/tzinfo/time_with_offset.rb +128 -0
  41. data/lib/tzinfo/timestamp.rb +548 -0
  42. data/lib/tzinfo/timestamp_with_offset.rb +85 -0
  43. data/lib/tzinfo/timezone.rb +979 -502
  44. data/lib/tzinfo/timezone_offset.rb +84 -74
  45. data/lib/tzinfo/timezone_period.rb +151 -217
  46. data/lib/tzinfo/timezone_proxy.rb +70 -79
  47. data/lib/tzinfo/timezone_transition.rb +77 -109
  48. data/lib/tzinfo/transitions_timezone_period.rb +63 -0
  49. data/lib/tzinfo/version.rb +7 -0
  50. data/lib/tzinfo/with_offset.rb +61 -0
  51. data/lib/tzinfo.rb +60 -40
  52. data.tar.gz.sig +0 -0
  53. metadata +51 -115
  54. metadata.gz.sig +2 -3
  55. data/Rakefile +0 -107
  56. data/lib/tzinfo/annual_rules.rb +0 -51
  57. data/lib/tzinfo/country_index_definition.rb +0 -31
  58. data/lib/tzinfo/country_info.rb +0 -42
  59. data/lib/tzinfo/data_timezone_info.rb +0 -55
  60. data/lib/tzinfo/linked_timezone_info.rb +0 -26
  61. data/lib/tzinfo/offset_rationals.rb +0 -77
  62. data/lib/tzinfo/posix_time_zone_parser.rb +0 -136
  63. data/lib/tzinfo/ruby_core_support.rb +0 -176
  64. data/lib/tzinfo/ruby_country_info.rb +0 -74
  65. data/lib/tzinfo/ruby_data_source.rb +0 -136
  66. data/lib/tzinfo/time_or_datetime.rb +0 -351
  67. data/lib/tzinfo/timezone_definition.rb +0 -36
  68. data/lib/tzinfo/timezone_index_definition.rb +0 -54
  69. data/lib/tzinfo/timezone_info.rb +0 -30
  70. data/lib/tzinfo/timezone_transition_definition.rb +0 -104
  71. data/lib/tzinfo/transition_data_timezone_info.rb +0 -274
  72. data/lib/tzinfo/transition_rule.rb +0 -325
  73. data/lib/tzinfo/zoneinfo_country_info.rb +0 -37
  74. data/lib/tzinfo/zoneinfo_data_source.rb +0 -504
  75. data/lib/tzinfo/zoneinfo_timezone_info.rb +0 -516
  76. data/test/assets/payload.rb +0 -1
  77. data/test/tc_annual_rules.rb +0 -95
  78. data/test/tc_country.rb +0 -240
  79. data/test/tc_country_index_definition.rb +0 -69
  80. data/test/tc_country_info.rb +0 -16
  81. data/test/tc_country_timezone.rb +0 -173
  82. data/test/tc_data_source.rb +0 -218
  83. data/test/tc_data_timezone.rb +0 -99
  84. data/test/tc_data_timezone_info.rb +0 -18
  85. data/test/tc_info_timezone.rb +0 -34
  86. data/test/tc_linked_timezone.rb +0 -155
  87. data/test/tc_linked_timezone_info.rb +0 -23
  88. data/test/tc_offset_rationals.rb +0 -23
  89. data/test/tc_posix_time_zone_parser.rb +0 -261
  90. data/test/tc_ruby_core_support.rb +0 -168
  91. data/test/tc_ruby_country_info.rb +0 -110
  92. data/test/tc_ruby_data_source.rb +0 -175
  93. data/test/tc_time_or_datetime.rb +0 -674
  94. data/test/tc_timezone.rb +0 -1361
  95. data/test/tc_timezone_definition.rb +0 -113
  96. data/test/tc_timezone_index_definition.rb +0 -73
  97. data/test/tc_timezone_info.rb +0 -11
  98. data/test/tc_timezone_london.rb +0 -143
  99. data/test/tc_timezone_melbourne.rb +0 -142
  100. data/test/tc_timezone_new_york.rb +0 -142
  101. data/test/tc_timezone_offset.rb +0 -126
  102. data/test/tc_timezone_period.rb +0 -555
  103. data/test/tc_timezone_proxy.rb +0 -136
  104. data/test/tc_timezone_transition.rb +0 -366
  105. data/test/tc_timezone_transition_definition.rb +0 -295
  106. data/test/tc_timezone_utc.rb +0 -27
  107. data/test/tc_transition_data_timezone_info.rb +0 -433
  108. data/test/tc_transition_rule.rb +0 -663
  109. data/test/tc_zoneinfo_country_info.rb +0 -78
  110. data/test/tc_zoneinfo_data_source.rb +0 -1226
  111. data/test/tc_zoneinfo_timezone_info.rb +0 -2149
  112. data/test/test_utils.rb +0 -214
  113. data/test/ts_all.rb +0 -7
  114. data/test/ts_all_ruby.rb +0 -5
  115. data/test/ts_all_zoneinfo.rb +0 -9
  116. data/test/tzinfo-data/tzinfo/data/definitions/America/Argentina/Buenos_Aires.rb +0 -89
  117. data/test/tzinfo-data/tzinfo/data/definitions/America/New_York.rb +0 -327
  118. data/test/tzinfo-data/tzinfo/data/definitions/Australia/Melbourne.rb +0 -230
  119. data/test/tzinfo-data/tzinfo/data/definitions/EST.rb +0 -19
  120. data/test/tzinfo-data/tzinfo/data/definitions/Etc/GMT__m__1.rb +0 -21
  121. data/test/tzinfo-data/tzinfo/data/definitions/Etc/GMT__p__1.rb +0 -21
  122. data/test/tzinfo-data/tzinfo/data/definitions/Etc/UTC.rb +0 -21
  123. data/test/tzinfo-data/tzinfo/data/definitions/Europe/Amsterdam.rb +0 -273
  124. data/test/tzinfo-data/tzinfo/data/definitions/Europe/Andorra.rb +0 -198
  125. data/test/tzinfo-data/tzinfo/data/definitions/Europe/London.rb +0 -333
  126. data/test/tzinfo-data/tzinfo/data/definitions/Europe/Paris.rb +0 -277
  127. data/test/tzinfo-data/tzinfo/data/definitions/Europe/Prague.rb +0 -235
  128. data/test/tzinfo-data/tzinfo/data/definitions/UTC.rb +0 -16
  129. data/test/tzinfo-data/tzinfo/data/indexes/countries.rb +0 -940
  130. data/test/tzinfo-data/tzinfo/data/indexes/timezones.rb +0 -609
  131. data/test/tzinfo-data/tzinfo/data/version.rb +0 -20
  132. data/test/tzinfo-data/tzinfo/data.rb +0 -8
  133. data/test/zoneinfo/America/Argentina/Buenos_Aires +0 -0
  134. data/test/zoneinfo/America/New_York +0 -0
  135. data/test/zoneinfo/Australia/Melbourne +0 -0
  136. data/test/zoneinfo/EST +0 -0
  137. data/test/zoneinfo/Etc/UTC +0 -0
  138. data/test/zoneinfo/Europe/Amsterdam +0 -0
  139. data/test/zoneinfo/Europe/Andorra +0 -0
  140. data/test/zoneinfo/Europe/London +0 -0
  141. data/test/zoneinfo/Europe/Paris +0 -0
  142. data/test/zoneinfo/Europe/Prague +0 -0
  143. data/test/zoneinfo/Factory +0 -0
  144. data/test/zoneinfo/iso3166.tab +0 -274
  145. data/test/zoneinfo/leapseconds +0 -78
  146. data/test/zoneinfo/posix/Europe/London +0 -0
  147. data/test/zoneinfo/posixrules +0 -0
  148. data/test/zoneinfo/right/Europe/London +0 -0
  149. data/test/zoneinfo/zone.tab +0 -452
  150. data/test/zoneinfo/zone1970.tab +0 -384
  151. data/tzinfo.gemspec +0 -21
@@ -1,63 +1,44 @@
1
+ # encoding: UTF-8
2
+
1
3
  module TZInfo
4
+ # Represents time zones that are defined as a link to or alias for another
5
+ # time zone.
6
+ class LinkedTimezone < InfoTimezone
7
+ # Initializes a new {LinkedTimezone}.
8
+ #
9
+ # {LinkedTimezone} instances should not normally be created directly. Use
10
+ # the {Timezone.get} method to obtain {Timezone} instances.
11
+ #
12
+ # @param info [DataSources::LinkedTimezoneInfo] a
13
+ # {DataSources::LinkedTimezoneInfo} instance supplied by a {DataSource}
14
+ # that will be used as the source of data for this {LinkedTimezone}.
15
+ def initialize(info)
16
+ super
17
+ @linked_timezone = Timezone.get(info.link_to_identifier)
18
+ end
2
19
 
3
- # A Timezone based on a LinkedTimezoneInfo.
4
- #
5
- # @private
6
- class LinkedTimezone < InfoTimezone #:nodoc:
7
- # Returns the TimezonePeriod for the given UTC time. utc can either be
8
- # a DateTime, Time or integer timestamp (Time.to_i). Any timezone
9
- # information in utc is ignored (it is treated as a UTC time).
10
- #
11
- # If no TimezonePeriod could be found, PeriodNotFound is raised.
12
- def period_for_utc(utc)
13
- @linked_timezone.period_for_utc(utc)
20
+ # (see Timezone#period_for)
21
+ def period_for(time)
22
+ @linked_timezone.period_for(time)
14
23
  end
15
-
16
- # Returns the set of TimezonePeriod instances that are valid for the given
17
- # local time as an array. If you just want a single period, use
18
- # period_for_local instead and specify how abiguities should be resolved.
19
- # Raises PeriodNotFound if no periods are found for the given time.
20
- def periods_for_local(local)
21
- @linked_timezone.periods_for_local(local)
24
+
25
+ # (see Timezone#periods_for_local)
26
+ def periods_for_local(local_time)
27
+ @linked_timezone.periods_for_local(local_time)
22
28
  end
23
-
24
- # Returns an Array of TimezoneTransition instances representing the times
25
- # where the UTC offset of the timezone changes.
26
- #
27
- # Transitions are returned up to a given date and time up to a given date
28
- # and time, specified in UTC (utc_to).
29
- #
30
- # A from date and time may also be supplied using the utc_from parameter
31
- # (also specified in UTC). If utc_from is not nil, only transitions from
32
- # that date and time onwards will be returned.
33
- #
34
- # Comparisons with utc_to are exclusive. Comparisons with utc_from are
35
- # inclusive. If a transition falls precisely on utc_to, it will be excluded.
36
- # If a transition falls on utc_from, it will be included.
37
- #
38
- # Transitions returned are ordered by when they occur, from earliest to
39
- # latest.
40
- #
41
- # utc_to and utc_from can be specified using either DateTime, Time or
42
- # integer timestamps (Time.to_i).
43
- #
44
- # If utc_from is specified and utc_to is not greater than utc_from, then
45
- # transitions_up_to raises an ArgumentError exception.
46
- def transitions_up_to(utc_to, utc_from = nil)
47
- @linked_timezone.transitions_up_to(utc_to, utc_from)
29
+
30
+ # (see Timezone#transitions_up_to)
31
+ def transitions_up_to(to, from = nil)
32
+ @linked_timezone.transitions_up_to(to, from)
48
33
  end
49
-
50
- # Returns the canonical zone for this Timezone.
34
+
35
+ # Returns the canonical {Timezone} instance for this {LinkedTimezone}.
36
+ #
37
+ # For a {LinkedTimezone}, this is the canonical zone of the link target.
51
38
  #
52
- # For a LinkedTimezone, this is the canonical zone of the link target.
39
+ # @return [Timezone] the canonical {Timezone} instance for this {Timezone}.
53
40
  def canonical_zone
54
41
  @linked_timezone.canonical_zone
55
42
  end
56
-
57
- protected
58
- def setup(info)
59
- super(info)
60
- @linked_timezone = Timezone.get(info.link_to_identifier)
61
- end
62
43
  end
63
44
  end
@@ -0,0 +1,42 @@
1
+ # encoding: UTF-8
2
+
3
+ module TZInfo
4
+ # Represents the infinite period of time in a time zone that constantly
5
+ # observes the same offset from UTC (has an unbounded start and end).
6
+ class OffsetTimezonePeriod < TimezonePeriod
7
+ # Initializes an {OffsetTimezonePeriod}.
8
+ #
9
+ # @param offset [TimezoneOffset] the offset that is constantly observed.
10
+ # @raise [ArgumentError] if `offset` is `nil`.
11
+ def initialize(offset)
12
+ super
13
+ end
14
+
15
+ # @return [TimezoneTransition] the transition that defines the start of this
16
+ # {TimezonePeriod}, always `nil` for {OffsetTimezonePeriod}.
17
+ def start_transition
18
+ nil
19
+ end
20
+
21
+ # @return [TimezoneTransition] the transition that defines the end of this
22
+ # {TimezonePeriod}, always `nil` for {OffsetTimezonePeriod}.
23
+ def end_transition
24
+ nil
25
+ end
26
+
27
+ # Determines if this {OffsetTimezonePeriod} is equal to another instance.
28
+ #
29
+ # @param p [Object] the instance to test for equality.
30
+ # @return [Boolean] `true` if `p` is a {OffsetTimezonePeriod} with the same
31
+ # {offset}, otherwise `false`.
32
+ def ==(p)
33
+ p.kind_of?(OffsetTimezonePeriod) && offset == p.offset
34
+ end
35
+ alias eql? ==
36
+
37
+ # @return [Integer] a hash based on {offset}.
38
+ def hash
39
+ offset.hash
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,118 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ require 'concurrent'
5
+
6
+ module TZInfo
7
+ # Maintains a pool of `String` instances. The {#dedupe} method will return
8
+ # either a pooled copy of a given `String` or add the instance to the pool.
9
+ #
10
+ # @private
11
+ class StringDeduper #:nodoc:
12
+ class << self
13
+ # @return [StringDeduper] a globally available singleton instance of
14
+ # {StringDeduper}. This instance is safe for use in concurrently
15
+ # executing threads.
16
+ attr_reader :global
17
+ end
18
+
19
+ # Initializes a new {StringDeduper}.
20
+ def initialize
21
+ @strings = create_hash do |h, k|
22
+ v = k.dup.freeze
23
+ h[v] = v
24
+ end
25
+ end
26
+
27
+ # @param string [String] the string to deduplicate.
28
+ # @return [bool] `string` if it is frozen, otherwise a frozen, possibly
29
+ # pre-existing copy of `string`.
30
+ def dedupe(string)
31
+ return string if string.frozen?
32
+ @strings[string]
33
+ end
34
+
35
+ protected
36
+
37
+ # Creates a `Hash` to store pooled `String` instances.
38
+ #
39
+ # @param block [Proc] Default value block to be passed to `Hash.new`.
40
+ # @return [Hash] a `Hash` to store pooled `String` instances.
41
+ def create_hash(&block)
42
+ Hash.new(&block)
43
+ end
44
+ end
45
+ private_constant :StringDeduper
46
+
47
+ # A thread-safe version of {StringDeduper}.
48
+ #
49
+ # @private
50
+ class ConcurrentStringDeduper < StringDeduper #:nodoc:
51
+ protected
52
+
53
+ def create_hash(&block)
54
+ Concurrent::Map.new(&block)
55
+ end
56
+ end
57
+ private_constant :ConcurrentStringDeduper
58
+
59
+
60
+ string_unary_minus_does_dedupe = if '0'.respond_to?(:-@)
61
+ # :nocov_no_string_-@:
62
+ s1 = -('0'.dup)
63
+ s2 = -('0'.dup)
64
+ s1.object_id == s2.object_id
65
+ # :nocov_no_string_-@:
66
+ else
67
+ # :nocov_string_-@:
68
+ false
69
+ # :nocov_string_-@:
70
+ end
71
+
72
+ if string_unary_minus_does_dedupe
73
+ # :nocov_no_deduping_string_unary_minus:
74
+
75
+ # An implementation of {StringDeduper} using the `String#-@` method where
76
+ # that method performs deduplication (Ruby 2.5 and later).
77
+ #
78
+ # Note that this is slightly different to the plain {StringDeduper}
79
+ # implementation. In this implementation, frozen literal strings are already
80
+ # in the pool and are candidates for being returned, even when passed
81
+ # another equal frozen non-literal string. {StringDeduper} will always
82
+ # return frozen strings.
83
+ #
84
+ # There are also differences in encoding handling. This implementation will
85
+ # treat strings with different encodings as different strings.
86
+ # {StringDeduper} will treat strings with the compatible encodings as the
87
+ # same string.
88
+ #
89
+ # @private
90
+ class UnaryMinusGlobalStringDeduper #:nodoc:
91
+ # @param string [String] the string to deduplicate.
92
+ # @return [bool] `string` if it is frozen, otherwise a frozen, possibly
93
+ # pre-existing copy of `string`.
94
+ def dedupe(string)
95
+ # String#-@ on Ruby 2.6 will dedupe a frozen non-literal String. Ruby
96
+ # 2.5 will just return frozen strings.
97
+ #
98
+ # The pooled implementation can't tell the difference between frozen
99
+ # literals and frozen non-literals, so must always return frozen String
100
+ # instances to avoid doing unncessary work when loading format 2
101
+ # TZInfo::Data modules.
102
+ #
103
+ # For compatibility with the pooled implementation, just return frozen
104
+ # string instances (acting like Ruby 2.5).
105
+ return string if string.frozen?
106
+ -string
107
+ end
108
+ end
109
+ private_constant :UnaryMinusGlobalStringDeduper
110
+
111
+ StringDeduper.instance_variable_set(:@global, UnaryMinusGlobalStringDeduper.new)
112
+ # :nocov_no_deduping_string_unary_minus:
113
+ else
114
+ # :nocov_deduping_string_unary_minus:
115
+ StringDeduper.instance_variable_set(:@global, ConcurrentStringDeduper.new)
116
+ # :nocov_deduping_string_unary_minus:
117
+ end
118
+ end
@@ -0,0 +1,128 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ module TZInfo
5
+ # A subclass of `Time` used to represent local times. {TimeWithOffset} holds a
6
+ # reference to the related {TimezoneOffset} and overrides various methods to
7
+ # return results appropriate for the {TimezoneOffset}. Certain operations will
8
+ # clear the associated {TimezoneOffset} (if the {TimezoneOffset} would not
9
+ # necessarily be valid for the result). Once the {TimezoneOffset} has been
10
+ # cleared, {TimeWithOffset} behaves identically to `Time`.
11
+ #
12
+ # Arithmetic performed on {TimeWithOffset} instances is _not_ time zone-aware.
13
+ # Regardless of whether transitions in the time zone are crossed, results of
14
+ # arithmetic operations will always maintain the same offset from UTC
15
+ # (`utc_offset`). The associated {TimezoneOffset} will aways be cleared.
16
+ class TimeWithOffset < Time
17
+ include WithOffset
18
+
19
+ # @return [TimezoneOffset] the {TimezoneOffset} associated with this
20
+ # instance.
21
+ attr_reader :timezone_offset
22
+
23
+ # Marks this {TimeWithOffset} as a local time with the UTC offset of a given
24
+ # {TimezoneOffset} and sets the associated {TimezoneOffset}.
25
+ #
26
+ # @param timezone_offset [TimezoneOffset] the {TimezoneOffset} to use to set
27
+ # the offset of this {TimeWithOffset}.
28
+ # @return [TimeWithOffset] `self`.
29
+ # @raise [ArgumentError] if `timezone_offset` is `nil`.
30
+ def set_timezone_offset(timezone_offset)
31
+ raise ArgumentError, 'timezone_offset must be specified' unless timezone_offset
32
+ localtime(timezone_offset.observed_utc_offset)
33
+ @timezone_offset = timezone_offset
34
+ self
35
+ end
36
+
37
+ # An overridden version of `Time#dst?` that, if there is an associated
38
+ # {TimezoneOffset}, returns the result of calling {TimezoneOffset#dst? dst?}
39
+ # on that offset.
40
+ #
41
+ # @return [Boolean] `true` if daylight savings time is being observed,
42
+ # otherwise `false`.
43
+ def dst?
44
+ to = timezone_offset
45
+ to ? to.dst? : super
46
+ end
47
+ alias isdst dst?
48
+
49
+ # An overridden version of `Time#gmtime` that clears the associated
50
+ # {TimezoneOffset}.
51
+ #
52
+ # @return [TimeWithOffset] `self`.
53
+ def gmtime
54
+ super
55
+ @timezone_offset = nil
56
+ self
57
+ end
58
+
59
+ # An overridden version of `Time#localtime` that clears the associated
60
+ # {TimezoneOffset}.
61
+ #
62
+ # @return [TimeWithOffset] `self`.
63
+ def localtime(*args)
64
+ super
65
+ @timezone_offset = nil
66
+ self
67
+ end
68
+
69
+ # An overridden version of `Time#round` that, if there is an associated
70
+ # {TimezoneOffset}, returns a {TimeWithOffset} preserving that offset.
71
+ #
72
+ # @return [Time] the rounded time.
73
+ def round(ndigits = 0)
74
+ if_timezone_offset(super) {|o,t| self.class.at(t.to_i, t.subsec * 1_000_000).set_timezone_offset(o) }
75
+ end
76
+
77
+ # An overridden version of `Time#to_a`. The `isdst` (index 8) and `zone`
78
+ # (index 9) elements of the array are set according to the associated
79
+ # {TimezoneOffset}.
80
+ #
81
+ # @return [Array] an `Array` representation of the {TimeWithOffset}.
82
+ def to_a
83
+ if_timezone_offset(super) do |o,a|
84
+ a[8] = o.dst?
85
+ a[9] = o.abbreviation
86
+ a
87
+ end
88
+ end
89
+
90
+ # An overridden version of `Time#utc` that clears the associated
91
+ # {TimezoneOffset}.
92
+ #
93
+ # @return [TimeWithOffset] `self`.
94
+ def utc
95
+ super
96
+ @timezone_offset = nil
97
+ self
98
+ end
99
+
100
+ # An overridden version of `Time#zone` that, if there is an associated
101
+ # {TimezoneOffset}, returns the {TimezoneOffset#abbreviation abbreviation}
102
+ # of that offset.
103
+ #
104
+ # @return [String] the {TimezoneOffset#abbreviation abbreviation} of the
105
+ # associated {TimezoneOffset}, or the result from `Time#zone` if there is
106
+ # no such offset.
107
+ def zone
108
+ to = timezone_offset
109
+ to ? to.abbreviation : super
110
+ end
111
+
112
+ # An overridden version of `Time#to_datetime` that, if there is an
113
+ # associated {TimezoneOffset}, returns a {DateTimeWithOffset} with that
114
+ # offset.
115
+ #
116
+ # @return [DateTime] if there is an associated {TimezoneOffset}, a
117
+ # {DateTimeWithOffset} representation of this {TimeWithOffset}, otherwise
118
+ # a `Time` representation.
119
+ def to_datetime
120
+ if_timezone_offset(super) do |o,dt|
121
+ offset = dt.offset
122
+ result = DateTimeWithOffset.jd(dt.jd + dt.day_fraction - offset)
123
+ result = result.new_offset(offset) unless offset == 0
124
+ result.set_timezone_offset(o)
125
+ end
126
+ end
127
+ end
128
+ end