tzinfo 1.2.10 → 2.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (152) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/.yardopts +3 -0
  4. data/CHANGES.md +583 -391
  5. data/LICENSE +13 -13
  6. data/README.md +368 -114
  7. data/lib/tzinfo/annual_rules.rb +32 -12
  8. data/lib/tzinfo/country.rb +141 -129
  9. data/lib/tzinfo/country_timezone.rb +70 -112
  10. data/lib/tzinfo/data_source.rb +400 -144
  11. data/lib/tzinfo/data_sources/constant_offset_data_timezone_info.rb +56 -0
  12. data/lib/tzinfo/data_sources/country_info.rb +42 -0
  13. data/lib/tzinfo/data_sources/data_timezone_info.rb +91 -0
  14. data/lib/tzinfo/data_sources/linked_timezone_info.rb +33 -0
  15. data/lib/tzinfo/data_sources/posix_time_zone_parser.rb +177 -0
  16. data/lib/tzinfo/data_sources/ruby_data_source.rb +141 -0
  17. data/lib/tzinfo/data_sources/timezone_info.rb +47 -0
  18. data/lib/tzinfo/data_sources/transitions_data_timezone_info.rb +214 -0
  19. data/lib/tzinfo/data_sources/zoneinfo_data_source.rb +592 -0
  20. data/lib/tzinfo/data_sources/zoneinfo_reader.rb +482 -0
  21. data/lib/tzinfo/data_sources.rb +8 -0
  22. data/lib/tzinfo/data_timezone.rb +33 -47
  23. data/lib/tzinfo/datetime_with_offset.rb +153 -0
  24. data/lib/tzinfo/format1/country_definer.rb +17 -0
  25. data/lib/tzinfo/format1/country_index_definition.rb +64 -0
  26. data/lib/tzinfo/format1/timezone_definer.rb +64 -0
  27. data/lib/tzinfo/format1/timezone_definition.rb +39 -0
  28. data/lib/tzinfo/format1/timezone_index_definition.rb +77 -0
  29. data/lib/tzinfo/format1.rb +10 -0
  30. data/lib/tzinfo/format2/country_definer.rb +68 -0
  31. data/lib/tzinfo/format2/country_index_definer.rb +68 -0
  32. data/lib/tzinfo/format2/country_index_definition.rb +46 -0
  33. data/lib/tzinfo/format2/timezone_definer.rb +94 -0
  34. data/lib/tzinfo/format2/timezone_definition.rb +73 -0
  35. data/lib/tzinfo/format2/timezone_index_definer.rb +45 -0
  36. data/lib/tzinfo/format2/timezone_index_definition.rb +55 -0
  37. data/lib/tzinfo/format2.rb +10 -0
  38. data/lib/tzinfo/info_timezone.rb +26 -21
  39. data/lib/tzinfo/linked_timezone.rb +33 -52
  40. data/lib/tzinfo/offset_timezone_period.rb +42 -0
  41. data/lib/tzinfo/ruby_core_support.rb +24 -155
  42. data/lib/tzinfo/string_deduper.rb +118 -0
  43. data/lib/tzinfo/time_with_offset.rb +154 -0
  44. data/lib/tzinfo/timestamp.rb +552 -0
  45. data/lib/tzinfo/timestamp_with_offset.rb +85 -0
  46. data/lib/tzinfo/timezone.rb +989 -502
  47. data/lib/tzinfo/timezone_offset.rb +84 -74
  48. data/lib/tzinfo/timezone_period.rb +151 -217
  49. data/lib/tzinfo/timezone_proxy.rb +70 -79
  50. data/lib/tzinfo/timezone_transition.rb +77 -109
  51. data/lib/tzinfo/transition_rule.rb +207 -77
  52. data/lib/tzinfo/transitions_timezone_period.rb +63 -0
  53. data/lib/tzinfo/version.rb +7 -0
  54. data/lib/tzinfo/with_offset.rb +61 -0
  55. data/lib/tzinfo.rb +78 -40
  56. data.tar.gz.sig +0 -0
  57. metadata +50 -105
  58. metadata.gz.sig +0 -0
  59. data/Rakefile +0 -107
  60. data/lib/tzinfo/country_index_definition.rb +0 -31
  61. data/lib/tzinfo/country_info.rb +0 -42
  62. data/lib/tzinfo/data_timezone_info.rb +0 -55
  63. data/lib/tzinfo/linked_timezone_info.rb +0 -26
  64. data/lib/tzinfo/offset_rationals.rb +0 -77
  65. data/lib/tzinfo/posix_time_zone_parser.rb +0 -136
  66. data/lib/tzinfo/ruby_country_info.rb +0 -74
  67. data/lib/tzinfo/ruby_data_source.rb +0 -140
  68. data/lib/tzinfo/time_or_datetime.rb +0 -351
  69. data/lib/tzinfo/timezone_definition.rb +0 -36
  70. data/lib/tzinfo/timezone_index_definition.rb +0 -54
  71. data/lib/tzinfo/timezone_info.rb +0 -30
  72. data/lib/tzinfo/timezone_transition_definition.rb +0 -104
  73. data/lib/tzinfo/transition_data_timezone_info.rb +0 -274
  74. data/lib/tzinfo/zoneinfo_country_info.rb +0 -37
  75. data/lib/tzinfo/zoneinfo_data_source.rb +0 -512
  76. data/lib/tzinfo/zoneinfo_timezone_info.rb +0 -520
  77. data/test/assets/payload.rb +0 -1
  78. data/test/tc_annual_rules.rb +0 -95
  79. data/test/tc_country.rb +0 -238
  80. data/test/tc_country_index_definition.rb +0 -69
  81. data/test/tc_country_info.rb +0 -16
  82. data/test/tc_country_timezone.rb +0 -173
  83. data/test/tc_data_source.rb +0 -218
  84. data/test/tc_data_timezone.rb +0 -99
  85. data/test/tc_data_timezone_info.rb +0 -18
  86. data/test/tc_info_timezone.rb +0 -34
  87. data/test/tc_linked_timezone.rb +0 -155
  88. data/test/tc_linked_timezone_info.rb +0 -23
  89. data/test/tc_offset_rationals.rb +0 -23
  90. data/test/tc_posix_time_zone_parser.rb +0 -261
  91. data/test/tc_ruby_core_support.rb +0 -168
  92. data/test/tc_ruby_country_info.rb +0 -110
  93. data/test/tc_ruby_data_source.rb +0 -173
  94. data/test/tc_time_or_datetime.rb +0 -674
  95. data/test/tc_timezone.rb +0 -1361
  96. data/test/tc_timezone_definition.rb +0 -113
  97. data/test/tc_timezone_index_definition.rb +0 -73
  98. data/test/tc_timezone_info.rb +0 -11
  99. data/test/tc_timezone_london.rb +0 -143
  100. data/test/tc_timezone_melbourne.rb +0 -142
  101. data/test/tc_timezone_new_york.rb +0 -142
  102. data/test/tc_timezone_offset.rb +0 -126
  103. data/test/tc_timezone_period.rb +0 -555
  104. data/test/tc_timezone_proxy.rb +0 -136
  105. data/test/tc_timezone_transition.rb +0 -366
  106. data/test/tc_timezone_transition_definition.rb +0 -295
  107. data/test/tc_timezone_utc.rb +0 -27
  108. data/test/tc_transition_data_timezone_info.rb +0 -433
  109. data/test/tc_transition_rule.rb +0 -663
  110. data/test/tc_zoneinfo_country_info.rb +0 -78
  111. data/test/tc_zoneinfo_data_source.rb +0 -1223
  112. data/test/tc_zoneinfo_timezone_info.rb +0 -2153
  113. data/test/test_utils.rb +0 -208
  114. data/test/ts_all.rb +0 -7
  115. data/test/ts_all_ruby.rb +0 -5
  116. data/test/ts_all_zoneinfo.rb +0 -9
  117. data/test/tzinfo-data/tzinfo/data/definitions/America/Argentina/Buenos_Aires.rb +0 -89
  118. data/test/tzinfo-data/tzinfo/data/definitions/America/New_York.rb +0 -327
  119. data/test/tzinfo-data/tzinfo/data/definitions/Australia/Melbourne.rb +0 -230
  120. data/test/tzinfo-data/tzinfo/data/definitions/EST.rb +0 -19
  121. data/test/tzinfo-data/tzinfo/data/definitions/Etc/GMT__m__1.rb +0 -21
  122. data/test/tzinfo-data/tzinfo/data/definitions/Etc/GMT__p__1.rb +0 -21
  123. data/test/tzinfo-data/tzinfo/data/definitions/Etc/UTC.rb +0 -21
  124. data/test/tzinfo-data/tzinfo/data/definitions/Europe/Amsterdam.rb +0 -273
  125. data/test/tzinfo-data/tzinfo/data/definitions/Europe/Andorra.rb +0 -198
  126. data/test/tzinfo-data/tzinfo/data/definitions/Europe/London.rb +0 -333
  127. data/test/tzinfo-data/tzinfo/data/definitions/Europe/Paris.rb +0 -277
  128. data/test/tzinfo-data/tzinfo/data/definitions/Europe/Prague.rb +0 -235
  129. data/test/tzinfo-data/tzinfo/data/definitions/UTC.rb +0 -16
  130. data/test/tzinfo-data/tzinfo/data/indexes/countries.rb +0 -940
  131. data/test/tzinfo-data/tzinfo/data/indexes/timezones.rb +0 -609
  132. data/test/tzinfo-data/tzinfo/data/version.rb +0 -20
  133. data/test/tzinfo-data/tzinfo/data.rb +0 -8
  134. data/test/zoneinfo/America/Argentina/Buenos_Aires +0 -0
  135. data/test/zoneinfo/America/New_York +0 -0
  136. data/test/zoneinfo/Australia/Melbourne +0 -0
  137. data/test/zoneinfo/EST +0 -0
  138. data/test/zoneinfo/Etc/UTC +0 -0
  139. data/test/zoneinfo/Europe/Amsterdam +0 -0
  140. data/test/zoneinfo/Europe/Andorra +0 -0
  141. data/test/zoneinfo/Europe/London +0 -0
  142. data/test/zoneinfo/Europe/Paris +0 -0
  143. data/test/zoneinfo/Europe/Prague +0 -0
  144. data/test/zoneinfo/Factory +0 -0
  145. data/test/zoneinfo/iso3166.tab +0 -274
  146. data/test/zoneinfo/leapseconds +0 -78
  147. data/test/zoneinfo/posix/Europe/London +0 -0
  148. data/test/zoneinfo/posixrules +0 -0
  149. data/test/zoneinfo/right/Europe/London +0 -0
  150. data/test/zoneinfo/zone.tab +0 -452
  151. data/test/zoneinfo/zone1970.tab +0 -384
  152. data/tzinfo.gemspec +0 -21
@@ -0,0 +1,42 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ module TZInfo
5
+ module DataSources
6
+ # Represents a country and references to its time zones as returned by a
7
+ # {DataSource}.
8
+ class CountryInfo
9
+ # @return [String] the ISO 3166-1 alpha-2 country code.
10
+ attr_reader :code
11
+
12
+ # @return [String] the name of the country.
13
+ attr_reader :name
14
+
15
+ # @return [Array<CountryTimezone>] the time zones observed in the country.
16
+ attr_reader :zones
17
+
18
+ # Initializes a new {CountryInfo}. The passed in `code`, `name` and
19
+ # `zones` instances will be frozen.
20
+ #
21
+ # @param code [String] an ISO 3166-1 alpha-2 country code.
22
+ # @param name [String] the name of the country.
23
+ # @param zones [Array<CountryTimezone>] the time zones observed in the
24
+ # country.
25
+ # @raise [ArgumentError] if `code`, `name` or `zones` is `nil`.
26
+ def initialize(code, name, zones)
27
+ raise ArgumentError, 'code must be specified' unless code
28
+ raise ArgumentError, 'name must be specified' unless name
29
+ raise ArgumentError, 'zones must be specified' unless zones
30
+ @code = code.freeze
31
+ @name = name.freeze
32
+ @zones = zones.freeze
33
+ end
34
+
35
+ # @return [String] the internal object state as a programmer-readable
36
+ # `String`.
37
+ def inspect
38
+ "#<#{self.class}: #@code>"
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,91 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ module TZInfo
5
+ module DataSources
6
+ # The base class for time zones defined as either a series of transitions
7
+ # ({TransitionsDataTimezoneInfo}) or a constantly observed offset
8
+ # ({ConstantOffsetDataTimezoneInfo}).
9
+ #
10
+ # @abstract Data sources return instances of {DataTimezoneInfo} subclasses.
11
+ class DataTimezoneInfo < TimezoneInfo
12
+ # @param timestamp [Timestamp] a {Timestamp} with a specified
13
+ # {Timestamp#utc_offset utc_offset}.
14
+ # @return [TimezonePeriod] the {TimezonePeriod} observed at the time
15
+ # specified by `timestamp`.
16
+ # @raise [ArgumentError] may be raised if `timestamp` is `nil` or does not
17
+ # have a specified {Timestamp#utc_offset utc_offset}.
18
+ def period_for(timestamp)
19
+ raise_not_implemented('period_for')
20
+ end
21
+
22
+ # Returns an `Array` containing the {TimezonePeriod TimezonePeriods} that
23
+ # could be observed at the local time specified by `local_timestamp`. The
24
+ # results are are ordered by increasing UTC start date. An empty `Array`
25
+ # is returned if no periods are found for the given local time.
26
+ #
27
+ # @param local_timestamp [Timestamp] a {Timestamp} representing a local
28
+ # time - must have an unspecified {Timestamp#utc_offset utc_offset}.
29
+ # @return [Array<TimezonePeriod>] an `Array` containing the
30
+ # {TimezonePeriod TimezonePeriods} that could be observed at the local
31
+ # time specified by `local_timestamp`.
32
+ # @raise [ArgumentError] may be raised if `local_timestamp` is `nil`, or
33
+ # has a specified {Timestamp#utc_offset utc_offset}.
34
+ def periods_for_local(local_timestamp)
35
+ raise_not_implemented('periods_for_local')
36
+ end
37
+
38
+ # Returns an `Array` of {TimezoneTransition} instances representing the
39
+ # times where the UTC offset of the time zone changes.
40
+ #
41
+ # Transitions are returned up to a given {Timestamp} (`to_timestamp`).
42
+ #
43
+ # A from {Timestamp} may also be supplied using the `from_timestamp`
44
+ # parameter. If `from_timestamp` is specified, only transitions from that
45
+ # time onwards will be returned.
46
+ #
47
+ # Comparisons with `to_timestamp` are exclusive. Comparisons with
48
+ # `from_timestamp` are inclusive. If a transition falls precisely on
49
+ # `to_timestamp`, it will be excluded. If a transition falls on
50
+ # `from_timestamp`, it will be included.
51
+ #
52
+ # Transitions returned are ordered by when they occur, from earliest to
53
+ # latest.
54
+ #
55
+ # @param to_timestamp [Timestamp] a {Timestamp} with a specified
56
+ # {Timestamp#utc_offset utc_offset}. Transitions are returned if they
57
+ # occur before this time.
58
+ # @param from_timestamp [Timestamp] an optional {Timestamp} with a
59
+ # specified {Timestamp#utc_offset utc_offset}. If specified, transitions
60
+ # are returned if they occur at or after this time.
61
+ # @return [Array<TimezoneTransition>] an `Array` of {TimezoneTransition}
62
+ # instances representing the times where the UTC offset of the time zone
63
+ # changes.
64
+ # @raise [ArgumentError] may be raised if `to_timestamp` is `nil` or does
65
+ # not have a specified {Timestamp#utc_offset utc_offset}.
66
+ # @raise [ArgumentError] may be raised if `from_timestamp` is specified
67
+ # but does not have a specified {Timestamp#utc_offset utc_offset}.
68
+ # @raise [ArgumentError] may be raised if `from_timestamp` is specified
69
+ # but is not earlier than or at the same time as `to_timestamp`.
70
+ def transitions_up_to(to_timestamp, from_timestamp = nil)
71
+ raise_not_implemented('transitions_up_to')
72
+ end
73
+
74
+ # @return [DataTimezone] a new {DataTimezone} instance for the time zone
75
+ # represented by this {DataTimezoneInfo}.
76
+ def create_timezone
77
+ DataTimezone.new(self)
78
+ end
79
+
80
+ private
81
+
82
+ # Raises a {NotImplementedError} to indicate that the base class is
83
+ # incorrectly being used directly.
84
+ #
85
+ # raise [NotImplementedError] always.
86
+ def raise_not_implemented(method_name)
87
+ raise NotImplementedError, "Subclasses must override #{method_name}"
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,33 @@
1
+ # encoding: UTF-8
2
+
3
+ module TZInfo
4
+ module DataSources
5
+ # Represents a time zone that is defined as a link to or alias of another
6
+ # zone.
7
+ class LinkedTimezoneInfo < TimezoneInfo
8
+ # @return [String] the identifier of the time zone that provides the data
9
+ # (that this zone links to or is an alias for).
10
+ attr_reader :link_to_identifier
11
+
12
+ # Initializes a new {LinkedTimezoneInfo}. The passed in `identifier` and
13
+ # `link_to_identifier` instances will be frozen.
14
+ #
15
+ # @param identifier [String] the identifier of the time zone.
16
+ # @param link_to_identifier [String] the identifier of the time zone that
17
+ # this zone link to.
18
+ # @raise [ArgumentError] if `identifier` or `link_to_identifier` are
19
+ # `nil`.
20
+ def initialize(identifier, link_to_identifier)
21
+ super(identifier)
22
+ raise ArgumentError, 'link_to_identifier must be specified' unless link_to_identifier
23
+ @link_to_identifier = link_to_identifier.freeze
24
+ end
25
+
26
+ # @return [LinkedTimezone] a new {LinkedTimezone} instance for the time
27
+ # zone represented by this {LinkedTimezoneInfo}.
28
+ def create_timezone
29
+ LinkedTimezone.new(self)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,177 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ require 'strscan'
5
+
6
+ module TZInfo
7
+ module DataSources
8
+ # An {InvalidPosixTimeZone} exception is raised if an invalid POSIX-style
9
+ # time zone string is encountered.
10
+ #
11
+ # @private
12
+ class InvalidPosixTimeZone < StandardError #:nodoc:
13
+ end
14
+ private_constant :InvalidPosixTimeZone
15
+
16
+ # A parser for POSIX-style TZ strings used in zoneinfo files and specified
17
+ # by tzfile.5 and tzset.3.
18
+ #
19
+ # @private
20
+ class PosixTimeZoneParser #:nodoc:
21
+ # Initializes a new {PosixTimeZoneParser}.
22
+ #
23
+ # @param string_deduper [StringDeduper] a {StringDeduper} instance to use
24
+ # to dedupe abbreviations.
25
+ def initialize(string_deduper)
26
+ @string_deduper = string_deduper
27
+ end
28
+
29
+ # Parses a POSIX-style TZ string.
30
+ #
31
+ # @param tz_string [String] the string to parse.
32
+ # @return [Object] either a {TimezoneOffset} for a constantly applied
33
+ # offset or an {AnnualRules} instance representing the rules.
34
+ # @raise [InvalidPosixTimeZone] if `tz_string` is not a `String`.
35
+ # @raise [InvalidPosixTimeZone] if `tz_string` is is not valid.
36
+ def parse(tz_string)
37
+ raise InvalidPosixTimeZone unless tz_string.kind_of?(String)
38
+ return nil if tz_string.empty?
39
+
40
+ s = StringScanner.new(tz_string)
41
+ check_scan(s, /([^-+,\d<][^-+,\d]*) | <([^>]+)>/x)
42
+ std_abbrev = @string_deduper.dedupe(RubyCoreSupport.untaint(s[1] || s[2]))
43
+ check_scan(s, /([-+]?\d+)(?::(\d+)(?::(\d+))?)?/)
44
+ std_offset = get_offset_from_hms(s[1], s[2], s[3])
45
+
46
+ if s.scan(/([^-+,\d<][^-+,\d]*) | <([^>]+)>/x)
47
+ dst_abbrev = @string_deduper.dedupe(RubyCoreSupport.untaint(s[1] || s[2]))
48
+
49
+ if s.scan(/([-+]?\d+)(?::(\d+)(?::(\d+))?)?/)
50
+ dst_offset = get_offset_from_hms(s[1], s[2], s[3])
51
+ else
52
+ # POSIX is negative for ahead of UTC.
53
+ dst_offset = std_offset - 3600
54
+ end
55
+
56
+ dst_difference = std_offset - dst_offset
57
+
58
+ start_rule = parse_rule(s, 'start')
59
+ end_rule = parse_rule(s, 'end')
60
+
61
+ raise InvalidPosixTimeZone, "Expected the end of a POSIX-style time zone string but found '#{s.rest}'." if s.rest?
62
+
63
+ if start_rule.is_always_first_day_of_year? && start_rule.transition_at == 0 &&
64
+ end_rule.is_always_last_day_of_year? && end_rule.transition_at == 86400 + dst_difference
65
+ # Constant daylight savings time.
66
+ # POSIX is negative for ahead of UTC.
67
+ TimezoneOffset.new(-std_offset, dst_difference, dst_abbrev)
68
+ else
69
+ AnnualRules.new(
70
+ TimezoneOffset.new(-std_offset, 0, std_abbrev),
71
+ TimezoneOffset.new(-std_offset, dst_difference, dst_abbrev),
72
+ start_rule,
73
+ end_rule)
74
+ end
75
+ elsif !s.rest?
76
+ # Constant standard time.
77
+ # POSIX is negative for ahead of UTC.
78
+ TimezoneOffset.new(-std_offset, 0, std_abbrev)
79
+ else
80
+ raise InvalidPosixTimeZone, "Expected the end of a POSIX-style time zone string but found '#{s.rest}'."
81
+ end
82
+ end
83
+
84
+ private
85
+
86
+ # Parses a rule.
87
+ #
88
+ # @param s [StringScanner] the `StringScanner` to read the rule from.
89
+ # @param type [String] the type of rule (either `'start'` or `'end'`).
90
+ # @raise [InvalidPosixTimeZone] if the rule is not valid.
91
+ # @return [TransitionRule] the parsed rule.
92
+ def parse_rule(s, type)
93
+ check_scan(s, /,(?: (?: J(\d+) ) | (\d+) | (?: M(\d+)\.(\d)\.(\d) ) )/x)
94
+ julian_day_of_year = s[1]
95
+ absolute_day_of_year = s[2]
96
+ month = s[3]
97
+ week = s[4]
98
+ day_of_week = s[5]
99
+
100
+ if s.scan(/\//)
101
+ check_scan(s, /([-+]?\d+)(?::(\d+)(?::(\d+))?)?/)
102
+ transition_at = get_seconds_after_midnight_from_hms(s[1], s[2], s[3])
103
+ else
104
+ transition_at = 7200
105
+ end
106
+
107
+ begin
108
+ if julian_day_of_year
109
+ JulianDayOfYearTransitionRule.new(julian_day_of_year.to_i, transition_at)
110
+ elsif absolute_day_of_year
111
+ AbsoluteDayOfYearTransitionRule.new(absolute_day_of_year.to_i, transition_at)
112
+ elsif week == '5'
113
+ LastDayOfMonthTransitionRule.new(month.to_i, day_of_week.to_i, transition_at)
114
+ else
115
+ DayOfMonthTransitionRule.new(month.to_i, week.to_i, day_of_week.to_i, transition_at)
116
+ end
117
+ rescue ArgumentError => e
118
+ raise InvalidPosixTimeZone, "Invalid #{type} rule in POSIX-style time zone string: #{e}"
119
+ end
120
+ end
121
+
122
+ # Returns an offset in seconds from hh:mm:ss values. The value can be
123
+ # negative. -02:33:12 would represent 2 hours, 33 minutes and 12 seconds
124
+ # ahead of UTC.
125
+ #
126
+ # @param h [String] the hours.
127
+ # @param m [String] the minutes.
128
+ # @param s [String] the seconds.
129
+ # @return [Integer] the offset.
130
+ # @raise [InvalidPosixTimeZone] if the mm and ss values are greater than
131
+ # 59.
132
+ def get_offset_from_hms(h, m, s)
133
+ h = h.to_i
134
+ m = m.to_i
135
+ s = s.to_i
136
+ raise InvalidPosixTimeZone, "Invalid minute #{m} in offset for POSIX-style time zone string." if m > 59
137
+ raise InvalidPosixTimeZone, "Invalid second #{s} in offset for POSIX-style time zone string." if s > 59
138
+ magnitude = (h.abs * 60 + m) * 60 + s
139
+ h < 0 ? -magnitude : magnitude
140
+ end
141
+
142
+ # Returns the seconds from midnight from hh:mm:ss values. Hours can exceed
143
+ # 24 for a time on the following day. Hours can be negative to subtract
144
+ # hours from midnight on the given day. -02:33:12 represents 22:33:12 on
145
+ # the prior day.
146
+ #
147
+ # @param h [String] the hour.
148
+ # @param m [String] the minutes past the hour.
149
+ # @param s [String] the seconds past the minute.
150
+ # @return [Integer] the number of seconds after midnight.
151
+ # @raise [InvalidPosixTimeZone] if the mm and ss values are greater than
152
+ # 59.
153
+ def get_seconds_after_midnight_from_hms(h, m, s)
154
+ h = h.to_i
155
+ m = m.to_i
156
+ s = s.to_i
157
+ raise InvalidPosixTimeZone, "Invalid minute #{m} in time for POSIX-style time zone string." if m > 59
158
+ raise InvalidPosixTimeZone, "Invalid second #{s} in time for POSIX-style time zone string." if s > 59
159
+ (h * 3600) + m * 60 + s
160
+ end
161
+
162
+ # Scans for a pattern and raises an exception if the pattern does not
163
+ # match the input.
164
+ #
165
+ # @param s [StringScanner] the `StringScanner` to scan.
166
+ # @param pattern [Regexp] the pattern to match.
167
+ # @return [String] the result of the scan.
168
+ # @raise [InvalidPosixTimeZone] if the pattern does not match the input.
169
+ def check_scan(s, pattern)
170
+ result = s.scan(pattern)
171
+ raise InvalidPosixTimeZone, "Expected '#{s.rest}' to match #{pattern} in POSIX-style time zone string." unless result
172
+ result
173
+ end
174
+ end
175
+ private_constant :PosixTimeZoneParser
176
+ end
177
+ end
@@ -0,0 +1,141 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ module TZInfo
5
+ module DataSources
6
+ # A {TZInfoDataNotFound} exception is raised if the tzinfo-data gem could
7
+ # not be found (i.e. `require 'tzinfo/data'` failed) when selecting the Ruby
8
+ # data source.
9
+ class TZInfoDataNotFound < StandardError
10
+ end
11
+
12
+ # A DataSource implementation that loads data from the set of Ruby modules
13
+ # included in the tzinfo-data gem.
14
+ #
15
+ # TZInfo will use {RubyDataSource} by default if the tzinfo-data gem
16
+ # is available on the load path. It can also be selected by calling
17
+ # {DataSource.set} as follows:
18
+ #
19
+ # TZInfo::DataSource.set(:ruby)
20
+ class RubyDataSource < DataSource
21
+ # (see DataSource#data_timezone_identifiers)
22
+ attr_reader :data_timezone_identifiers
23
+
24
+ # (see DataSource#linked_timezone_identifiers)
25
+ attr_reader :linked_timezone_identifiers
26
+
27
+ # (see DataSource#country_codes)
28
+ attr_reader :country_codes
29
+
30
+ # Initializes a new {RubyDataSource} instance.
31
+ #
32
+ # @raise [TZInfoDataNotFound] if the tzinfo-data gem could not be found
33
+ # (i.e. `require 'tzinfo/data'` failed).
34
+ def initialize
35
+ super
36
+
37
+ begin
38
+ require('tzinfo/data')
39
+ rescue LoadError
40
+ raise TZInfoDataNotFound, "The tzinfo-data gem could not be found (require 'tzinfo/data' failed)."
41
+ end
42
+
43
+ if TZInfo::Data.const_defined?(:LOCATION)
44
+ # Format 2
45
+ @base_path = File.join(TZInfo::Data::LOCATION, 'tzinfo', 'data')
46
+ else
47
+ # Format 1
48
+ data_file = File.join('', 'tzinfo', 'data.rb')
49
+ path = $".reverse_each.detect {|p| p.end_with?(data_file) }
50
+ if path
51
+ @base_path = RubyCoreSupport.untaint(File.join(File.dirname(path), 'data'))
52
+ else
53
+ @base_path = 'tzinfo/data'
54
+ end
55
+ end
56
+
57
+ require_index('timezones')
58
+ require_index('countries')
59
+
60
+ @data_timezone_identifiers = Data::Indexes::Timezones.data_timezones
61
+ @linked_timezone_identifiers = Data::Indexes::Timezones.linked_timezones
62
+ @countries = Data::Indexes::Countries.countries
63
+ @country_codes = @countries.keys.sort!.freeze
64
+ end
65
+
66
+ # (see DataSource#to_s)
67
+ def to_s
68
+ "Ruby DataSource: #{version_info}"
69
+ end
70
+
71
+ # (see DataSource#inspect)
72
+ def inspect
73
+ "#<TZInfo::DataSources::RubyDataSource: #{version_info}>"
74
+ end
75
+
76
+ protected
77
+
78
+ # Returns a {TimezoneInfo} instance for the given time zone identifier.
79
+ # The result will either be a {ConstantOffsetDataTimezoneInfo}, a
80
+ # {TransitionsDataTimezoneInfo} or a {LinkedTimezoneInfo} depending on the
81
+ # type of time zone.
82
+ #
83
+ # @param identifier [String] A time zone identifier.
84
+ # @return [TimezoneInfo] a {TimezoneInfo} instance for the given time zone
85
+ # identifier.
86
+ # @raise [InvalidTimezoneIdentifier] if the time zone is not found or the
87
+ # identifier is invalid.
88
+ def load_timezone_info(identifier)
89
+ valid_identifier = validate_timezone_identifier(identifier)
90
+ split_identifier = valid_identifier.gsub(/-/, '__m__').gsub(/\+/, '__p__').split('/')
91
+
92
+ begin
93
+ require_definition(split_identifier)
94
+
95
+ m = Data::Definitions
96
+ split_identifier.each {|part| m = m.const_get(part) }
97
+ m.get
98
+ rescue LoadError, NameError => e
99
+ raise InvalidTimezoneIdentifier, "#{e.message.encode(Encoding::UTF_8)} (loading #{valid_identifier})"
100
+ end
101
+ end
102
+
103
+ # (see DataSource#load_country_info)
104
+ def load_country_info(code)
105
+ lookup_country_info(@countries, code)
106
+ end
107
+
108
+ private
109
+
110
+ # Requires a zone definition by its identifier (split on /).
111
+ #
112
+ # @param identifier [Array<string>] the component parts of a time zone
113
+ # identifier (split on /). This must have already been validated.
114
+ def require_definition(identifier)
115
+ require_data('definitions', *identifier)
116
+ end
117
+
118
+ # Requires an index by its name.
119
+ #
120
+ # @param name [String] an index name.
121
+ def require_index(name)
122
+ require_data('indexes', name)
123
+ end
124
+
125
+ # Requires a file from tzinfo/data.
126
+ #
127
+ # @param file [Array<String>] a relative path to a file to be required.
128
+ def require_data(*file)
129
+ require(File.join(@base_path, *file))
130
+ end
131
+
132
+ # @return [String] a `String` containing TZInfo::Data version infomation
133
+ # for inclusion in the #to_s and #inspect output.
134
+ def version_info
135
+ # The TZInfo::Data::VERSION constant is only available from v1.2014.8
136
+ # onwards.
137
+ "tzdb v#{TZInfo::Data::Version::TZDATA}#{TZInfo::Data.const_defined?(:VERSION) ? ", tzinfo-data v#{TZInfo::Data::VERSION}" : ''}"
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,47 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ module TZInfo
5
+ module DataSources
6
+ # Represents a time zone defined by a data source.
7
+ #
8
+ # @abstract Data sources return instances of {TimezoneInfo} subclasses.
9
+ class TimezoneInfo
10
+ # @return [String] the identifier of the time zone.
11
+ attr_reader :identifier
12
+
13
+ # Initializes a new TimezoneInfo. The passed in `identifier` instance will
14
+ # be frozen.
15
+ #
16
+ # @param identifier [String] the identifier of the time zone.
17
+ # @raise [ArgumentError] if `identifier` is `nil`.
18
+ def initialize(identifier)
19
+ raise ArgumentError, 'identifier must be specified' unless identifier
20
+ @identifier = identifier.freeze
21
+ end
22
+
23
+ # @return [String] the internal object state as a programmer-readable
24
+ # `String`.
25
+ def inspect
26
+ "#<#{self.class}: #@identifier>"
27
+ end
28
+
29
+ # @return [Timezone] a new {Timezone} instance for the time zone
30
+ # represented by this {TimezoneInfo}.
31
+ def create_timezone
32
+ raise_not_implemented('create_timezone')
33
+ end
34
+
35
+ private
36
+
37
+ # Raises a {NotImplementedError}.
38
+ #
39
+ # @param method_name [String] the name of the method that must be
40
+ # overridden.
41
+ # @raise NotImplementedError always.
42
+ def raise_not_implemented(method_name)
43
+ raise NotImplementedError, "Subclasses must override #{method_name}"
44
+ end
45
+ end
46
+ end
47
+ end