time_crisis 0.1.8 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (145) hide show
  1. data/VERSION.yml +2 -2
  2. data/lib/time_crisis.rb +5 -12
  3. data/lib/time_crisis/ext.rb +60 -0
  4. data/lib/time_crisis/support/conversions.rb +4 -0
  5. data/lib/time_crisis/tzinfo.rb +3 -0
  6. data/lib/time_crisis/tzinfo/LICENSE +21 -0
  7. data/lib/time_crisis/tzinfo/data_timezone.rb +27 -0
  8. data/lib/time_crisis/tzinfo/data_timezone_info.rb +208 -0
  9. data/lib/time_crisis/tzinfo/definitions/Africa/Algiers.rb +55 -0
  10. data/lib/time_crisis/tzinfo/definitions/Africa/Cairo.rb +219 -0
  11. data/lib/time_crisis/tzinfo/definitions/Africa/Casablanca.rb +42 -0
  12. data/lib/time_crisis/tzinfo/definitions/Africa/Harare.rb +18 -0
  13. data/lib/time_crisis/tzinfo/definitions/Africa/Johannesburg.rb +25 -0
  14. data/lib/time_crisis/tzinfo/definitions/Africa/Monrovia.rb +22 -0
  15. data/lib/time_crisis/tzinfo/definitions/Africa/Nairobi.rb +23 -0
  16. data/lib/time_crisis/tzinfo/definitions/America/Argentina/Buenos_Aires.rb +84 -0
  17. data/lib/time_crisis/tzinfo/definitions/America/Argentina/San_Juan.rb +86 -0
  18. data/lib/time_crisis/tzinfo/definitions/America/Bogota.rb +23 -0
  19. data/lib/time_crisis/tzinfo/definitions/America/Caracas.rb +23 -0
  20. data/lib/time_crisis/tzinfo/definitions/America/Chicago.rb +283 -0
  21. data/lib/time_crisis/tzinfo/definitions/America/Chihuahua.rb +136 -0
  22. data/lib/time_crisis/tzinfo/definitions/America/Denver.rb +204 -0
  23. data/lib/time_crisis/tzinfo/definitions/America/Godthab.rb +161 -0
  24. data/lib/time_crisis/tzinfo/definitions/America/Guatemala.rb +27 -0
  25. data/lib/time_crisis/tzinfo/definitions/America/Halifax.rb +274 -0
  26. data/lib/time_crisis/tzinfo/definitions/America/Indiana/Indianapolis.rb +149 -0
  27. data/lib/time_crisis/tzinfo/definitions/America/Juneau.rb +194 -0
  28. data/lib/time_crisis/tzinfo/definitions/America/La_Paz.rb +22 -0
  29. data/lib/time_crisis/tzinfo/definitions/America/Lima.rb +35 -0
  30. data/lib/time_crisis/tzinfo/definitions/America/Los_Angeles.rb +232 -0
  31. data/lib/time_crisis/tzinfo/definitions/America/Mazatlan.rb +139 -0
  32. data/lib/time_crisis/tzinfo/definitions/America/Mexico_City.rb +144 -0
  33. data/lib/time_crisis/tzinfo/definitions/America/Monterrey.rb +131 -0
  34. data/lib/time_crisis/tzinfo/definitions/America/New_York.rb +282 -0
  35. data/lib/time_crisis/tzinfo/definitions/America/Phoenix.rb +30 -0
  36. data/lib/time_crisis/tzinfo/definitions/America/Regina.rb +74 -0
  37. data/lib/time_crisis/tzinfo/definitions/America/Santiago.rb +205 -0
  38. data/lib/time_crisis/tzinfo/definitions/America/Sao_Paulo.rb +171 -0
  39. data/lib/time_crisis/tzinfo/definitions/America/St_Johns.rb +288 -0
  40. data/lib/time_crisis/tzinfo/definitions/America/Tijuana.rb +196 -0
  41. data/lib/time_crisis/tzinfo/definitions/Asia/Almaty.rb +67 -0
  42. data/lib/time_crisis/tzinfo/definitions/Asia/Baghdad.rb +73 -0
  43. data/lib/time_crisis/tzinfo/definitions/Asia/Baku.rb +161 -0
  44. data/lib/time_crisis/tzinfo/definitions/Asia/Bangkok.rb +20 -0
  45. data/lib/time_crisis/tzinfo/definitions/Asia/Chongqing.rb +33 -0
  46. data/lib/time_crisis/tzinfo/definitions/Asia/Colombo.rb +30 -0
  47. data/lib/time_crisis/tzinfo/definitions/Asia/Dhaka.rb +29 -0
  48. data/lib/time_crisis/tzinfo/definitions/Asia/Hong_Kong.rb +87 -0
  49. data/lib/time_crisis/tzinfo/definitions/Asia/Irkutsk.rb +165 -0
  50. data/lib/time_crisis/tzinfo/definitions/Asia/Jakarta.rb +30 -0
  51. data/lib/time_crisis/tzinfo/definitions/Asia/Jerusalem.rb +163 -0
  52. data/lib/time_crisis/tzinfo/definitions/Asia/Kabul.rb +20 -0
  53. data/lib/time_crisis/tzinfo/definitions/Asia/Kamchatka.rb +163 -0
  54. data/lib/time_crisis/tzinfo/definitions/Asia/Karachi.rb +114 -0
  55. data/lib/time_crisis/tzinfo/definitions/Asia/Kathmandu.rb +20 -0
  56. data/lib/time_crisis/tzinfo/definitions/Asia/Kolkata.rb +25 -0
  57. data/lib/time_crisis/tzinfo/definitions/Asia/Krasnoyarsk.rb +163 -0
  58. data/lib/time_crisis/tzinfo/definitions/Asia/Kuala_Lumpur.rb +31 -0
  59. data/lib/time_crisis/tzinfo/definitions/Asia/Kuwait.rb +18 -0
  60. data/lib/time_crisis/tzinfo/definitions/Asia/Magadan.rb +163 -0
  61. data/lib/time_crisis/tzinfo/definitions/Asia/Muscat.rb +18 -0
  62. data/lib/time_crisis/tzinfo/definitions/Asia/Novosibirsk.rb +164 -0
  63. data/lib/time_crisis/tzinfo/definitions/Asia/Rangoon.rb +24 -0
  64. data/lib/time_crisis/tzinfo/definitions/Asia/Riyadh.rb +18 -0
  65. data/lib/time_crisis/tzinfo/definitions/Asia/Seoul.rb +34 -0
  66. data/lib/time_crisis/tzinfo/definitions/Asia/Shanghai.rb +35 -0
  67. data/lib/time_crisis/tzinfo/definitions/Asia/Singapore.rb +33 -0
  68. data/lib/time_crisis/tzinfo/definitions/Asia/Taipei.rb +59 -0
  69. data/lib/time_crisis/tzinfo/definitions/Asia/Tashkent.rb +47 -0
  70. data/lib/time_crisis/tzinfo/definitions/Asia/Tbilisi.rb +78 -0
  71. data/lib/time_crisis/tzinfo/definitions/Asia/Tehran.rb +121 -0
  72. data/lib/time_crisis/tzinfo/definitions/Asia/Tokyo.rb +30 -0
  73. data/lib/time_crisis/tzinfo/definitions/Asia/Ulaanbaatar.rb +65 -0
  74. data/lib/time_crisis/tzinfo/definitions/Asia/Urumqi.rb +33 -0
  75. data/lib/time_crisis/tzinfo/definitions/Asia/Vladivostok.rb +164 -0
  76. data/lib/time_crisis/tzinfo/definitions/Asia/Yakutsk.rb +163 -0
  77. data/lib/time_crisis/tzinfo/definitions/Asia/Yekaterinburg.rb +165 -0
  78. data/lib/time_crisis/tzinfo/definitions/Asia/Yerevan.rb +165 -0
  79. data/lib/time_crisis/tzinfo/definitions/Atlantic/Azores.rb +270 -0
  80. data/lib/time_crisis/tzinfo/definitions/Atlantic/Cape_Verde.rb +23 -0
  81. data/lib/time_crisis/tzinfo/definitions/Atlantic/South_Georgia.rb +18 -0
  82. data/lib/time_crisis/tzinfo/definitions/Australia/Adelaide.rb +187 -0
  83. data/lib/time_crisis/tzinfo/definitions/Australia/Brisbane.rb +35 -0
  84. data/lib/time_crisis/tzinfo/definitions/Australia/Darwin.rb +29 -0
  85. data/lib/time_crisis/tzinfo/definitions/Australia/Hobart.rb +193 -0
  86. data/lib/time_crisis/tzinfo/definitions/Australia/Melbourne.rb +185 -0
  87. data/lib/time_crisis/tzinfo/definitions/Australia/Perth.rb +37 -0
  88. data/lib/time_crisis/tzinfo/definitions/Australia/Sydney.rb +185 -0
  89. data/lib/time_crisis/tzinfo/definitions/Etc/UTC.rb +16 -0
  90. data/lib/time_crisis/tzinfo/definitions/Europe/Amsterdam.rb +228 -0
  91. data/lib/time_crisis/tzinfo/definitions/Europe/Athens.rb +185 -0
  92. data/lib/time_crisis/tzinfo/definitions/Europe/Belgrade.rb +163 -0
  93. data/lib/time_crisis/tzinfo/definitions/Europe/Berlin.rb +188 -0
  94. data/lib/time_crisis/tzinfo/definitions/Europe/Bratislava.rb +13 -0
  95. data/lib/time_crisis/tzinfo/definitions/Europe/Brussels.rb +232 -0
  96. data/lib/time_crisis/tzinfo/definitions/Europe/Bucharest.rb +181 -0
  97. data/lib/time_crisis/tzinfo/definitions/Europe/Budapest.rb +197 -0
  98. data/lib/time_crisis/tzinfo/definitions/Europe/Copenhagen.rb +179 -0
  99. data/lib/time_crisis/tzinfo/definitions/Europe/Dublin.rb +276 -0
  100. data/lib/time_crisis/tzinfo/definitions/Europe/Helsinki.rb +163 -0
  101. data/lib/time_crisis/tzinfo/definitions/Europe/Istanbul.rb +218 -0
  102. data/lib/time_crisis/tzinfo/definitions/Europe/Kiev.rb +168 -0
  103. data/lib/time_crisis/tzinfo/definitions/Europe/Lisbon.rb +268 -0
  104. data/lib/time_crisis/tzinfo/definitions/Europe/Ljubljana.rb +13 -0
  105. data/lib/time_crisis/tzinfo/definitions/Europe/London.rb +288 -0
  106. data/lib/time_crisis/tzinfo/definitions/Europe/Madrid.rb +211 -0
  107. data/lib/time_crisis/tzinfo/definitions/Europe/Minsk.rb +170 -0
  108. data/lib/time_crisis/tzinfo/definitions/Europe/Moscow.rb +181 -0
  109. data/lib/time_crisis/tzinfo/definitions/Europe/Paris.rb +232 -0
  110. data/lib/time_crisis/tzinfo/definitions/Europe/Prague.rb +187 -0
  111. data/lib/time_crisis/tzinfo/definitions/Europe/Riga.rb +176 -0
  112. data/lib/time_crisis/tzinfo/definitions/Europe/Rome.rb +215 -0
  113. data/lib/time_crisis/tzinfo/definitions/Europe/Sarajevo.rb +13 -0
  114. data/lib/time_crisis/tzinfo/definitions/Europe/Skopje.rb +13 -0
  115. data/lib/time_crisis/tzinfo/definitions/Europe/Sofia.rb +173 -0
  116. data/lib/time_crisis/tzinfo/definitions/Europe/Stockholm.rb +165 -0
  117. data/lib/time_crisis/tzinfo/definitions/Europe/Tallinn.rb +172 -0
  118. data/lib/time_crisis/tzinfo/definitions/Europe/Vienna.rb +183 -0
  119. data/lib/time_crisis/tzinfo/definitions/Europe/Vilnius.rb +170 -0
  120. data/lib/time_crisis/tzinfo/definitions/Europe/Warsaw.rb +212 -0
  121. data/lib/time_crisis/tzinfo/definitions/Europe/Zagreb.rb +13 -0
  122. data/lib/time_crisis/tzinfo/definitions/Pacific/Auckland.rb +202 -0
  123. data/lib/time_crisis/tzinfo/definitions/Pacific/Fiji.rb +23 -0
  124. data/lib/time_crisis/tzinfo/definitions/Pacific/Guam.rb +22 -0
  125. data/lib/time_crisis/tzinfo/definitions/Pacific/Honolulu.rb +28 -0
  126. data/lib/time_crisis/tzinfo/definitions/Pacific/Majuro.rb +20 -0
  127. data/lib/time_crisis/tzinfo/definitions/Pacific/Midway.rb +25 -0
  128. data/lib/time_crisis/tzinfo/definitions/Pacific/Noumea.rb +25 -0
  129. data/lib/time_crisis/tzinfo/definitions/Pacific/Pago_Pago.rb +26 -0
  130. data/lib/time_crisis/tzinfo/definitions/Pacific/Port_Moresby.rb +20 -0
  131. data/lib/time_crisis/tzinfo/definitions/Pacific/Tongatapu.rb +27 -0
  132. data/lib/time_crisis/tzinfo/info_timezone.rb +32 -0
  133. data/lib/time_crisis/tzinfo/linked_timezone.rb +32 -0
  134. data/lib/time_crisis/tzinfo/linked_timezone_info.rb +24 -0
  135. data/lib/time_crisis/tzinfo/offset_rationals.rb +79 -0
  136. data/lib/time_crisis/tzinfo/ruby_core_support.rb +33 -0
  137. data/lib/time_crisis/tzinfo/time_or_datetime.rb +309 -0
  138. data/lib/time_crisis/tzinfo/timezone.rb +486 -0
  139. data/lib/time_crisis/tzinfo/timezone_definition.rb +36 -0
  140. data/lib/time_crisis/tzinfo/timezone_info.rb +20 -0
  141. data/lib/time_crisis/tzinfo/timezone_offset_info.rb +75 -0
  142. data/lib/time_crisis/tzinfo/timezone_period.rb +179 -0
  143. data/lib/time_crisis/tzinfo/timezone_transition_info.rb +109 -0
  144. data/time_crisis.gemspec +142 -2
  145. metadata +142 -2
@@ -0,0 +1,486 @@
1
+ require 'time_crisis/tzinfo/time_or_datetime'
2
+ require 'time_crisis/tzinfo/timezone_period'
3
+
4
+ module TimeCrisis
5
+ module TZInfo
6
+ # Indicate a specified time in a local timezone has more than one
7
+ # possible time in UTC. This happens when switching from daylight savings time
8
+ # to normal time where the clocks are rolled back. Thrown by period_for_local
9
+ # and local_to_utc when using an ambiguous time and not specifying any
10
+ # means to resolve the ambiguity.
11
+ class AmbiguousTime < StandardError
12
+ end
13
+
14
+ # Thrown to indicate that no TimezonePeriod matching a given time could be found.
15
+ class PeriodNotFound < StandardError
16
+ end
17
+
18
+ # Thrown by Timezone#get if the identifier given is not valid.
19
+ class InvalidTimezoneIdentifier < StandardError
20
+ end
21
+
22
+ # Thrown if an attempt is made to use a timezone created with Timezone.new(nil).
23
+ class UnknownTimezone < StandardError
24
+ end
25
+
26
+ # Timezone is the base class of all timezones. It provides a factory method
27
+ # get to access timezones by identifier. Once a specific Timezone has been
28
+ # retrieved, DateTimes, Times and timestamps can be converted between the UTC
29
+ # and the local time for the zone. For example:
30
+ #
31
+ # tz = TZInfo::Timezone.get('America/New_York')
32
+ # puts tz.utc_to_local(DateTime.new(2005,8,29,15,35,0)).to_s
33
+ # puts tz.local_to_utc(Time.utc(2005,8,29,11,35,0)).to_s
34
+ # puts tz.utc_to_local(1125315300).to_s
35
+ #
36
+ # Each time conversion method returns an object of the same type it was
37
+ # passed.
38
+ #
39
+ # The timezone information all comes from the tz database
40
+ # (see http://www.twinsun.com/tz/tz-link.htm)
41
+ class Timezone
42
+ include Comparable
43
+
44
+ # Cache of loaded zones by identifier to avoid using require if a zone
45
+ # has already been loaded.
46
+ @@loaded_zones = {}
47
+
48
+ # Whether the timezones index has been loaded yet.
49
+ @@index_loaded = false
50
+
51
+ # Returns a timezone by its identifier (e.g. "Europe/London",
52
+ # "America/Chicago" or "UTC").
53
+ #
54
+ # Raises InvalidTimezoneIdentifier if the timezone couldn't be found.
55
+ def self.get(identifier)
56
+ instance = @@loaded_zones[identifier]
57
+ unless instance
58
+ raise InvalidTimezoneIdentifier, 'Invalid identifier' if identifier !~ /^[A-z0-9\+\-_]+(\/[A-z0-9\+\-_]+)*$/
59
+ identifier = identifier.gsub(/-/, '__m__').gsub(/\+/, '__p__')
60
+ begin
61
+ # Use a temporary variable to avoid an rdoc warning
62
+ file = "time_crisis/tzinfo/definitions/#{identifier}".untaint
63
+ require file
64
+
65
+ m = Definitions
66
+ identifier.split(/\//).each {|part|
67
+ m = m.const_get(part)
68
+ }
69
+
70
+ info = m.get
71
+
72
+ # Could make Timezone subclasses register an interest in an info
73
+ # type. Since there are currently only two however, there isn't
74
+ # much point.
75
+ if info.kind_of?(DataTimezoneInfo)
76
+ instance = DataTimezone.new(info)
77
+ elsif info.kind_of?(LinkedTimezoneInfo)
78
+ instance = LinkedTimezone.new(info)
79
+ else
80
+ raise InvalidTimezoneIdentifier, "No handler for info type #{info.class}"
81
+ end
82
+
83
+ @@loaded_zones[instance.identifier] = instance
84
+ rescue LoadError, NameError => e
85
+ raise InvalidTimezoneIdentifier, e.message
86
+ end
87
+ end
88
+
89
+ instance
90
+ end
91
+
92
+ # Returns a proxy for the Timezone with the given identifier. The proxy
93
+ # will cause the real timezone to be loaded when an attempt is made to
94
+ # find a period or convert a time. get_proxy will not validate the
95
+ # identifier. If an invalid identifier is specified, no exception will be
96
+ # raised until the proxy is used.
97
+ def self.get_proxy(identifier)
98
+ TimezoneProxy.new(identifier)
99
+ end
100
+
101
+ # If identifier is nil calls super(), otherwise calls get. An identfier
102
+ # should always be passed in when called externally.
103
+ def self.new(identifier = nil)
104
+ if identifier
105
+ get(identifier)
106
+ else
107
+ super()
108
+ end
109
+ end
110
+
111
+ # Returns an array containing all the available Timezones.
112
+ #
113
+ # Returns TimezoneProxy objects to avoid the overhead of loading Timezone
114
+ # definitions until a conversion is actually required.
115
+ def self.all
116
+ get_proxies(all_identifiers)
117
+ end
118
+
119
+ # Returns an array containing the identifiers of all the available
120
+ # Timezones.
121
+ def self.all_identifiers
122
+ load_index
123
+ Indexes::Timezones.timezones
124
+ end
125
+
126
+ # Returns an array containing all the available Timezones that are based
127
+ # on data (are not links to other Timezones).
128
+ #
129
+ # Returns TimezoneProxy objects to avoid the overhead of loading Timezone
130
+ # definitions until a conversion is actually required.
131
+ def self.all_data_zones
132
+ get_proxies(all_data_zone_identifiers)
133
+ end
134
+
135
+ # Returns an array containing the identifiers of all the available
136
+ # Timezones that are based on data (are not links to other Timezones)..
137
+ def self.all_data_zone_identifiers
138
+ load_index
139
+ Indexes::Timezones.data_timezones
140
+ end
141
+
142
+ # Returns an array containing all the available Timezones that are links
143
+ # to other Timezones.
144
+ #
145
+ # Returns TimezoneProxy objects to avoid the overhead of loading Timezone
146
+ # definitions until a conversion is actually required.
147
+ def self.all_linked_zones
148
+ get_proxies(all_linked_zone_identifiers)
149
+ end
150
+
151
+ # Returns an array containing the identifiers of all the available
152
+ # Timezones that are links to other Timezones.
153
+ def self.all_linked_zone_identifiers
154
+ load_index
155
+ Indexes::Timezones.linked_timezones
156
+ end
157
+
158
+ # Returns all the Timezones defined for all Countries. This is not the
159
+ # complete set of Timezones as some are not country specific (e.g.
160
+ # 'Etc/GMT').
161
+ #
162
+ # Returns TimezoneProxy objects to avoid the overhead of loading Timezone
163
+ # definitions until a conversion is actually required.
164
+ def self.all_country_zones
165
+ Country.all_codes.inject([]) {|zones, country|
166
+ zones += Country.get(country).zones
167
+ }
168
+ end
169
+
170
+ # Returns all the zone identifiers defined for all Countries. This is not the
171
+ # complete set of zone identifiers as some are not country specific (e.g.
172
+ # 'Etc/GMT'). You can obtain a Timezone instance for a given identifier
173
+ # with the get method.
174
+ def self.all_country_zone_identifiers
175
+ Country.all_codes.inject([]) {|zones, country|
176
+ zones += Country.get(country).zone_identifiers
177
+ }
178
+ end
179
+
180
+ # Returns all US Timezone instances. A shortcut for
181
+ # TZInfo::Country.get('US').zones.
182
+ #
183
+ # Returns TimezoneProxy objects to avoid the overhead of loading Timezone
184
+ # definitions until a conversion is actually required.
185
+ def self.us_zones
186
+ Country.get('US').zones
187
+ end
188
+
189
+ # Returns all US zone identifiers. A shortcut for
190
+ # TZInfo::Country.get('US').zone_identifiers.
191
+ def self.us_zone_identifiers
192
+ Country.get('US').zone_identifiers
193
+ end
194
+
195
+ # The identifier of the timezone, e.g. "Europe/Paris".
196
+ def identifier
197
+ raise UnknownTimezone, 'TZInfo::Timezone constructed directly'
198
+ end
199
+
200
+ # An alias for identifier.
201
+ def name
202
+ # Don't use alias, as identifier gets overridden.
203
+ identifier
204
+ end
205
+
206
+ # Returns a friendlier version of the identifier.
207
+ def to_s
208
+ friendly_identifier
209
+ end
210
+
211
+ # Returns internal object state as a programmer-readable string.
212
+ def inspect
213
+ "#<#{self.class}: #{identifier}>"
214
+ end
215
+
216
+ # Returns a friendlier version of the identifier. Set skip_first_part to
217
+ # omit the first part of the identifier (typically a region name) where
218
+ # there is more than one part.
219
+ #
220
+ # For example:
221
+ #
222
+ # Timezone.get('Europe/Paris').friendly_identifier(false) #=> "Europe - Paris"
223
+ # Timezone.get('Europe/Paris').friendly_identifier(true) #=> "Paris"
224
+ # Timezone.get('America/Indiana/Knox').friendly_identifier(false) #=> "America - Knox, Indiana"
225
+ # Timezone.get('America/Indiana/Knox').friendly_identifier(true) #=> "Knox, Indiana"
226
+ def friendly_identifier(skip_first_part = false)
227
+ parts = identifier.split('/')
228
+ if parts.empty?
229
+ # shouldn't happen
230
+ identifier
231
+ elsif parts.length == 1
232
+ parts[0]
233
+ else
234
+ if skip_first_part
235
+ result = ''
236
+ else
237
+ result = parts[0] + ' - '
238
+ end
239
+
240
+ parts[1, parts.length - 1].reverse_each {|part|
241
+ part.gsub!(/_/, ' ')
242
+
243
+ if part.index(/[a-z]/)
244
+ # Missing a space if a lower case followed by an upper case and the
245
+ # name isn't McXxxx.
246
+ part.gsub!(/([^M][a-z])([A-Z])/, '\1 \2')
247
+ part.gsub!(/([M][a-bd-z])([A-Z])/, '\1 \2')
248
+
249
+ # Missing an apostrophe if two consecutive upper case characters.
250
+ part.gsub!(/([A-Z])([A-Z])/, '\1\'\2')
251
+ end
252
+
253
+ result << part
254
+ result << ', '
255
+ }
256
+
257
+ result.slice!(result.length - 2, 2)
258
+ result
259
+ end
260
+ end
261
+
262
+ # Returns the TimezonePeriod for the given UTC time. utc can either be
263
+ # a DateTime, Time or integer timestamp (Time.to_i). Any timezone
264
+ # information in utc is ignored (it is treated as a UTC time).
265
+ def period_for_utc(utc)
266
+ raise UnknownTimezone, 'TZInfo::Timezone constructed directly'
267
+ end
268
+
269
+ # Returns the set of TimezonePeriod instances that are valid for the given
270
+ # local time as an array. If you just want a single period, use
271
+ # period_for_local instead and specify how ambiguities should be resolved.
272
+ # Returns an empty array if no periods are found for the given time.
273
+ def periods_for_local(local)
274
+ raise UnknownTimezone, 'TZInfo::Timezone constructed directly'
275
+ end
276
+
277
+ # Returns the TimezonePeriod for the given local time. local can either be
278
+ # a DateTime, Time or integer timestamp (Time.to_i). Any timezone
279
+ # information in local is ignored (it is treated as a time in the current
280
+ # timezone).
281
+ #
282
+ # Warning: There are local times that have no equivalent UTC times (e.g.
283
+ # in the transition from standard time to daylight savings time). There are
284
+ # also local times that have more than one UTC equivalent (e.g. in the
285
+ # transition from daylight savings time to standard time).
286
+ #
287
+ # In the first case (no equivalent UTC time), a PeriodNotFound exception
288
+ # will be raised.
289
+ #
290
+ # In the second case (more than one equivalent UTC time), an AmbiguousTime
291
+ # exception will be raised unless the optional dst parameter or block
292
+ # handles the ambiguity.
293
+ #
294
+ # If the ambiguity is due to a transition from daylight savings time to
295
+ # standard time, the dst parameter can be used to select whether the
296
+ # daylight savings time or local time is used. For example,
297
+ #
298
+ # Timezone.get('America/New_York').period_for_local(DateTime.new(2004,10,31,1,30,0))
299
+ #
300
+ # would raise an AmbiguousTime exception.
301
+ #
302
+ # Specifying dst=true would the daylight savings period from April to
303
+ # October 2004. Specifying dst=false would return the standard period
304
+ # from October 2004 to April 2005.
305
+ #
306
+ # If the dst parameter does not resolve the ambiguity, and a block is
307
+ # specified, it is called. The block must take a single parameter - an
308
+ # array of the periods that need to be resolved. The block can select and
309
+ # return a single period or return nil or an empty array
310
+ # to cause an AmbiguousTime exception to be raised.
311
+ def period_for_local(local, dst = nil)
312
+ results = periods_for_local(local)
313
+
314
+ if results.empty?
315
+ raise PeriodNotFound
316
+ elsif results.size < 2
317
+ results.first
318
+ else
319
+ # ambiguous result try to resolve
320
+
321
+ if !dst.nil?
322
+ matches = results.find_all {|period| period.dst? == dst}
323
+ results = matches if !matches.empty?
324
+ end
325
+
326
+ if results.size < 2
327
+ results.first
328
+ else
329
+ # still ambiguous, try the block
330
+
331
+ if block_given?
332
+ results = yield results
333
+ end
334
+
335
+ if results.is_a?(TimezonePeriod)
336
+ results
337
+ elsif results && results.size == 1
338
+ results.first
339
+ else
340
+ raise AmbiguousTime, "#{local} is an ambiguous local time."
341
+ end
342
+ end
343
+ end
344
+ end
345
+
346
+ # Converts a time in UTC to the local timezone. utc can either be
347
+ # a DateTime, Time or timestamp (Time.to_i). The returned time has the same
348
+ # type as utc. Any timezone information in utc is ignored (it is treated as
349
+ # a UTC time).
350
+ def utc_to_local(utc)
351
+ TimeOrDateTime.wrap(utc) {|wrapped|
352
+ period_for_utc(wrapped).to_local(wrapped)
353
+ }
354
+ end
355
+
356
+ # Converts a time in the local timezone to UTC. local can either be
357
+ # a DateTime, Time or timestamp (Time.to_i). The returned time has the same
358
+ # type as local. Any timezone information in local is ignored (it is treated
359
+ # as a local time).
360
+ #
361
+ # Warning: There are local times that have no equivalent UTC times (e.g.
362
+ # in the transition from standard time to daylight savings time). There are
363
+ # also local times that have more than one UTC equivalent (e.g. in the
364
+ # transition from daylight savings time to standard time).
365
+ #
366
+ # In the first case (no equivalent UTC time), a PeriodNotFound exception
367
+ # will be raised.
368
+ #
369
+ # In the second case (more than one equivalent UTC time), an AmbiguousTime
370
+ # exception will be raised unless the optional dst parameter or block
371
+ # handles the ambiguity.
372
+ #
373
+ # If the ambiguity is due to a transition from daylight savings time to
374
+ # standard time, the dst parameter can be used to select whether the
375
+ # daylight savings time or local time is used. For example,
376
+ #
377
+ # Timezone.get('America/New_York').local_to_utc(DateTime.new(2004,10,31,1,30,0))
378
+ #
379
+ # would raise an AmbiguousTime exception.
380
+ #
381
+ # Specifying dst=true would return 2004-10-31 5:30:00. Specifying dst=false
382
+ # would return 2004-10-31 6:30:00.
383
+ #
384
+ # If the dst parameter does not resolve the ambiguity, and a block is
385
+ # specified, it is called. The block must take a single parameter - an
386
+ # array of the periods that need to be resolved. The block can return a
387
+ # single period to use to convert the time or return nil or an empty array
388
+ # to cause an AmbiguousTime exception to be raised.
389
+ def local_to_utc(local, dst = nil)
390
+ TimeOrDateTime.wrap(local) {|wrapped|
391
+ if block_given?
392
+ period = period_for_local(wrapped, dst) {|periods| yield periods }
393
+ else
394
+ period = period_for_local(wrapped, dst)
395
+ end
396
+
397
+ period.to_utc(wrapped)
398
+ }
399
+ end
400
+
401
+ # Returns the current time in the timezone as a Time.
402
+ def now
403
+ utc_to_local(Time.now.utc)
404
+ end
405
+
406
+ # Returns the TimezonePeriod for the current time.
407
+ def current_period
408
+ period_for_utc(Time.now.utc)
409
+ end
410
+
411
+ # Returns the current Time and TimezonePeriod as an array. The first element
412
+ # is the time, the second element is the period.
413
+ def current_period_and_time
414
+ utc = Time.now.utc
415
+ period = period_for_utc(utc)
416
+ [period.to_local(utc), period]
417
+ end
418
+
419
+ alias :current_time_and_period :current_period_and_time
420
+
421
+ # Converts a time in UTC to local time and returns it as a string
422
+ # according to the given format. The formatting is identical to
423
+ # Time.strftime and DateTime.strftime, except %Z is replaced with the
424
+ # timezone abbreviation for the specified time (for example, EST or EDT).
425
+ def strftime(format, utc = Time.now.utc)
426
+ period = period_for_utc(utc)
427
+ local = period.to_local(utc)
428
+ local = Time.at(local).utc unless local.kind_of?(Time) || local.kind_of?(DateTime)
429
+ abbreviation = period.abbreviation.to_s.gsub(/%/, '%%')
430
+
431
+ format = format.gsub(/(.?)%Z/) do
432
+ if $1 == '%'
433
+ # return %%Z so the real strftime treats it as a literal %Z too
434
+ '%%Z'
435
+ else
436
+ "#$1#{abbreviation}"
437
+ end
438
+ end
439
+
440
+ local.strftime(format)
441
+ end
442
+
443
+ # Compares two Timezones based on their identifier. Returns -1 if tz is less
444
+ # than self, 0 if tz is equal to self and +1 if tz is greater than self.
445
+ def <=>(tz)
446
+ identifier <=> tz.identifier
447
+ end
448
+
449
+ # Returns true if and only if the identifier of tz is equal to the
450
+ # identifier of this Timezone.
451
+ def eql?(tz)
452
+ self == tz
453
+ end
454
+
455
+ # Returns a hash of this Timezone.
456
+ def hash
457
+ identifier.hash
458
+ end
459
+
460
+ # Dumps this Timezone for marshalling.
461
+ def _dump(limit)
462
+ identifier
463
+ end
464
+
465
+ # Loads a marshalled Timezone.
466
+ def self._load(data)
467
+ Timezone.get(data)
468
+ end
469
+
470
+ private
471
+ # Loads in the index of timezones if it hasn't already been loaded.
472
+ def self.load_index
473
+ unless @@index_loaded
474
+ require 'time_crisis/tzinfo/indexes/timezones'
475
+ @@index_loaded = true
476
+ end
477
+ end
478
+
479
+ # Returns an array of proxies corresponding to the given array of
480
+ # identifiers.
481
+ def self.get_proxies(identifiers)
482
+ identifiers.collect {|identifier| get_proxy(identifier)}
483
+ end
484
+ end
485
+ end
486
+ end