Changesets can be listed by changeset number.
The Git repository is here.
- Revision:
- 373
- Log:
Initial import of Radiant 0.9.1, which is now packaged as a gem. This is an
import of the tagged 0.9.1 source checked out from GitHub, which isn't quite
the same as the gem distribution - but it doesn't seem to be available in an
archived form and the installed gem already has modifications, so this is
the closest I can get.
- Author:
- rool
- Date:
- Mon Mar 21 13:40:05 +0000 2011
- Size:
- 19640 Bytes
1 | # The TimeZone class serves as a wrapper around TZInfo::Timezone instances. It allows us to do the following: |
2 | # |
3 | # * Limit the set of zones provided by TZInfo to a meaningful subset of 142 zones. |
4 | # * Retrieve and display zones with a friendlier name (e.g., "Eastern Time (US & Canada)" instead of "America/New_York"). |
5 | # * Lazily load TZInfo::Timezone instances only when they're needed. |
6 | # * Create ActiveSupport::TimeWithZone instances via TimeZone's +local+, +parse+, +at+ and +now+ methods. |
7 | # |
8 | # If you set <tt>config.time_zone</tt> in the Rails Initializer, you can access this TimeZone object via <tt>Time.zone</tt>: |
9 | # |
10 | # # environment.rb: |
11 | # Rails::Initializer.run do |config| |
12 | # config.time_zone = "Eastern Time (US & Canada)" |
13 | # end |
14 | # |
15 | # Time.zone # => #<TimeZone:0x514834...> |
16 | # Time.zone.name # => "Eastern Time (US & Canada)" |
17 | # Time.zone.now # => Sun, 18 May 2008 14:30:44 EDT -04:00 |
18 | # |
19 | # The version of TZInfo bundled with Active Support only includes the definitions necessary to support the zones |
20 | # defined by the TimeZone class. If you need to use zones that aren't defined by TimeZone, you'll need to install the TZInfo gem |
21 | # (if a recent version of the gem is installed locally, this will be used instead of the bundled version.) |
22 | module ActiveSupport |
23 | class TimeZone |
24 | unless const_defined?(:MAPPING) |
25 | # Keys are Rails TimeZone names, values are TZInfo identifiers |
26 | MAPPING = { |
27 | "International Date Line West" => "Pacific/Midway", |
28 | "Midway Island" => "Pacific/Midway", |
29 | "Samoa" => "Pacific/Pago_Pago", |
30 | "Hawaii" => "Pacific/Honolulu", |
31 | "Alaska" => "America/Juneau", |
32 | "Pacific Time (US & Canada)" => "America/Los_Angeles", |
33 | "Tijuana" => "America/Tijuana", |
34 | "Mountain Time (US & Canada)" => "America/Denver", |
35 | "Arizona" => "America/Phoenix", |
36 | "Chihuahua" => "America/Chihuahua", |
37 | "Mazatlan" => "America/Mazatlan", |
38 | "Central Time (US & Canada)" => "America/Chicago", |
39 | "Saskatchewan" => "America/Regina", |
40 | "Guadalajara" => "America/Mexico_City", |
41 | "Mexico City" => "America/Mexico_City", |
42 | "Monterrey" => "America/Monterrey", |
43 | "Central America" => "America/Guatemala", |
44 | "Eastern Time (US & Canada)" => "America/New_York", |
45 | "Indiana (East)" => "America/Indiana/Indianapolis", |
46 | "Bogota" => "America/Bogota", |
47 | "Lima" => "America/Lima", |
48 | "Quito" => "America/Lima", |
49 | "Atlantic Time (Canada)" => "America/Halifax", |
50 | "Caracas" => "America/Caracas", |
51 | "La Paz" => "America/La_Paz", |
52 | "Santiago" => "America/Santiago", |
53 | "Newfoundland" => "America/St_Johns", |
54 | "Brasilia" => "America/Sao_Paulo", |
55 | "Buenos Aires" => "America/Argentina/Buenos_Aires", |
56 | "Georgetown" => "America/Argentina/San_Juan", |
57 | "Greenland" => "America/Godthab", |
58 | "Mid-Atlantic" => "Atlantic/South_Georgia", |
59 | "Azores" => "Atlantic/Azores", |
60 | "Cape Verde Is." => "Atlantic/Cape_Verde", |
61 | "Dublin" => "Europe/Dublin", |
62 | "Edinburgh" => "Europe/Dublin", |
63 | "Lisbon" => "Europe/Lisbon", |
64 | "London" => "Europe/London", |
65 | "Casablanca" => "Africa/Casablanca", |
66 | "Monrovia" => "Africa/Monrovia", |
67 | "UTC" => "Etc/UTC", |
68 | "Belgrade" => "Europe/Belgrade", |
69 | "Bratislava" => "Europe/Bratislava", |
70 | "Budapest" => "Europe/Budapest", |
71 | "Ljubljana" => "Europe/Ljubljana", |
72 | "Prague" => "Europe/Prague", |
73 | "Sarajevo" => "Europe/Sarajevo", |
74 | "Skopje" => "Europe/Skopje", |
75 | "Warsaw" => "Europe/Warsaw", |
76 | "Zagreb" => "Europe/Zagreb", |
77 | "Brussels" => "Europe/Brussels", |
78 | "Copenhagen" => "Europe/Copenhagen", |
79 | "Madrid" => "Europe/Madrid", |
80 | "Paris" => "Europe/Paris", |
81 | "Amsterdam" => "Europe/Amsterdam", |
82 | "Berlin" => "Europe/Berlin", |
83 | "Bern" => "Europe/Berlin", |
84 | "Rome" => "Europe/Rome", |
85 | "Stockholm" => "Europe/Stockholm", |
86 | "Vienna" => "Europe/Vienna", |
87 | "West Central Africa" => "Africa/Algiers", |
88 | "Bucharest" => "Europe/Bucharest", |
89 | "Cairo" => "Africa/Cairo", |
90 | "Helsinki" => "Europe/Helsinki", |
91 | "Kyev" => "Europe/Kiev", |
92 | "Riga" => "Europe/Riga", |
93 | "Sofia" => "Europe/Sofia", |
94 | "Tallinn" => "Europe/Tallinn", |
95 | "Vilnius" => "Europe/Vilnius", |
96 | "Athens" => "Europe/Athens", |
97 | "Istanbul" => "Europe/Istanbul", |
98 | "Minsk" => "Europe/Minsk", |
99 | "Jerusalem" => "Asia/Jerusalem", |
100 | "Harare" => "Africa/Harare", |
101 | "Pretoria" => "Africa/Johannesburg", |
102 | "Moscow" => "Europe/Moscow", |
103 | "St. Petersburg" => "Europe/Moscow", |
104 | "Volgograd" => "Europe/Moscow", |
105 | "Kuwait" => "Asia/Kuwait", |
106 | "Riyadh" => "Asia/Riyadh", |
107 | "Nairobi" => "Africa/Nairobi", |
108 | "Baghdad" => "Asia/Baghdad", |
109 | "Tehran" => "Asia/Tehran", |
110 | "Abu Dhabi" => "Asia/Muscat", |
111 | "Muscat" => "Asia/Muscat", |
112 | "Baku" => "Asia/Baku", |
113 | "Tbilisi" => "Asia/Tbilisi", |
114 | "Yerevan" => "Asia/Yerevan", |
115 | "Kabul" => "Asia/Kabul", |
116 | "Ekaterinburg" => "Asia/Yekaterinburg", |
117 | "Islamabad" => "Asia/Karachi", |
118 | "Karachi" => "Asia/Karachi", |
119 | "Tashkent" => "Asia/Tashkent", |
120 | "Chennai" => "Asia/Kolkata", |
121 | "Kolkata" => "Asia/Kolkata", |
122 | "Mumbai" => "Asia/Kolkata", |
123 | "New Delhi" => "Asia/Kolkata", |
124 | "Kathmandu" => "Asia/Katmandu", |
125 | "Astana" => "Asia/Dhaka", |
126 | "Dhaka" => "Asia/Dhaka", |
127 | "Sri Jayawardenepura" => "Asia/Colombo", |
128 | "Almaty" => "Asia/Almaty", |
129 | "Novosibirsk" => "Asia/Novosibirsk", |
130 | "Rangoon" => "Asia/Rangoon", |
131 | "Bangkok" => "Asia/Bangkok", |
132 | "Hanoi" => "Asia/Bangkok", |
133 | "Jakarta" => "Asia/Jakarta", |
134 | "Krasnoyarsk" => "Asia/Krasnoyarsk", |
135 | "Beijing" => "Asia/Shanghai", |
136 | "Chongqing" => "Asia/Chongqing", |
137 | "Hong Kong" => "Asia/Hong_Kong", |
138 | "Urumqi" => "Asia/Urumqi", |
139 | "Kuala Lumpur" => "Asia/Kuala_Lumpur", |
140 | "Singapore" => "Asia/Singapore", |
141 | "Taipei" => "Asia/Taipei", |
142 | "Perth" => "Australia/Perth", |
143 | "Irkutsk" => "Asia/Irkutsk", |
144 | "Ulaan Bataar" => "Asia/Ulaanbaatar", |
145 | "Seoul" => "Asia/Seoul", |
146 | "Osaka" => "Asia/Tokyo", |
147 | "Sapporo" => "Asia/Tokyo", |
148 | "Tokyo" => "Asia/Tokyo", |
149 | "Yakutsk" => "Asia/Yakutsk", |
150 | "Darwin" => "Australia/Darwin", |
151 | "Adelaide" => "Australia/Adelaide", |
152 | "Canberra" => "Australia/Melbourne", |
153 | "Melbourne" => "Australia/Melbourne", |
154 | "Sydney" => "Australia/Sydney", |
155 | "Brisbane" => "Australia/Brisbane", |
156 | "Hobart" => "Australia/Hobart", |
157 | "Vladivostok" => "Asia/Vladivostok", |
158 | "Guam" => "Pacific/Guam", |
159 | "Port Moresby" => "Pacific/Port_Moresby", |
160 | "Magadan" => "Asia/Magadan", |
161 | "Solomon Is." => "Asia/Magadan", |
162 | "New Caledonia" => "Pacific/Noumea", |
163 | "Fiji" => "Pacific/Fiji", |
164 | "Kamchatka" => "Asia/Kamchatka", |
165 | "Marshall Is." => "Pacific/Majuro", |
166 | "Auckland" => "Pacific/Auckland", |
167 | "Wellington" => "Pacific/Auckland", |
168 | "Nuku'alofa" => "Pacific/Tongatapu" |
169 | }.each { |name, zone| name.freeze; zone.freeze } |
170 | MAPPING.freeze |
171 | end |
172 | |
173 | include Comparable |
174 | attr_reader :name |
175 | attr_reader :tzinfo |
176 | |
177 | # Create a new TimeZone object with the given name and offset. The |
178 | # offset is the number of seconds that this time zone is offset from UTC |
179 | # (GMT). Seconds were chosen as the offset unit because that is the unit that |
180 | # Ruby uses to represent time zone offsets (see Time#utc_offset). |
181 | def initialize(name, utc_offset = nil, tzinfo = nil) |
182 | @name = name |
183 | @utc_offset = utc_offset |
184 | @tzinfo = tzinfo || TimeZone.find_tzinfo(name) |
185 | @current_period = nil |
186 | end |
187 | |
188 | def utc_offset |
189 | if @utc_offset |
190 | @utc_offset |
191 | else |
192 | @current_period ||= tzinfo.current_period |
193 | @current_period.utc_offset |
194 | end |
195 | end |
196 | |
197 | # Returns the offset of this time zone as a formatted string, of the |
198 | # format "+HH:MM". |
199 | def formatted_offset(colon=true, alternate_utc_string = nil) |
200 | utc_offset == 0 && alternate_utc_string || utc_offset.to_utc_offset_s(colon) |
201 | end |
202 | |
203 | # Compare this time zone to the parameter. The two are comapred first on |
204 | # their offsets, and then by name. |
205 | def <=>(zone) |
206 | result = (utc_offset <=> zone.utc_offset) |
207 | result = (name <=> zone.name) if result == 0 |
208 | result |
209 | end |
210 | |
211 | # Compare #name and TZInfo identifier to a supplied regexp, returning true |
212 | # if a match is found. |
213 | def =~(re) |
214 | return true if name =~ re || MAPPING[name] =~ re |
215 | end |
216 | |
217 | # Returns a textual representation of this time zone. |
218 | def to_s |
219 | "(GMT#{formatted_offset}) #{name}" |
220 | end |
221 | |
222 | # Method for creating new ActiveSupport::TimeWithZone instance in time zone of +self+ from given values. Example: |
223 | # |
224 | # Time.zone = "Hawaii" # => "Hawaii" |
225 | # Time.zone.local(2007, 2, 1, 15, 30, 45) # => Thu, 01 Feb 2007 15:30:45 HST -10:00 |
226 | def local(*args) |
227 | time = Time.utc_time(*args) |
228 | ActiveSupport::TimeWithZone.new(nil, self, time) |
229 | end |
230 | |
231 | # Method for creating new ActiveSupport::TimeWithZone instance in time zone of +self+ from number of seconds since the Unix epoch. Example: |
232 | # |
233 | # Time.zone = "Hawaii" # => "Hawaii" |
234 | # Time.utc(2000).to_f # => 946684800.0 |
235 | # Time.zone.at(946684800.0) # => Fri, 31 Dec 1999 14:00:00 HST -10:00 |
236 | def at(secs) |
237 | utc = Time.at(secs).utc rescue DateTime.civil(1970).since(secs) |
238 | utc.in_time_zone(self) |
239 | end |
240 | |
241 | # Method for creating new ActiveSupport::TimeWithZone instance in time zone of +self+ from parsed string. Example: |
242 | # |
243 | # Time.zone = "Hawaii" # => "Hawaii" |
244 | # Time.zone.parse('1999-12-31 14:00:00') # => Fri, 31 Dec 1999 14:00:00 HST -10:00 |
245 | # |
246 | # If upper components are missing from the string, they are supplied from TimeZone#now: |
247 | # |
248 | # Time.zone.now # => Fri, 31 Dec 1999 14:00:00 HST -10:00 |
249 | # Time.zone.parse('22:30:00') # => Fri, 31 Dec 1999 22:30:00 HST -10:00 |
250 | def parse(str, now=now) |
251 | date_parts = Date._parse(str) |
252 | return if date_parts.blank? |
253 | time = Time.parse(str, now) rescue DateTime.parse(str) |
254 | if date_parts[:offset].nil? |
255 | ActiveSupport::TimeWithZone.new(nil, self, time) |
256 | else |
257 | time.in_time_zone(self) |
258 | end |
259 | end |
260 | |
261 | # Returns an ActiveSupport::TimeWithZone instance representing the current time |
262 | # in the time zone represented by +self+. Example: |
263 | # |
264 | # Time.zone = 'Hawaii' # => "Hawaii" |
265 | # Time.zone.now # => Wed, 23 Jan 2008 20:24:27 HST -10:00 |
266 | def now |
267 | Time.now.utc.in_time_zone(self) |
268 | end |
269 | |
270 | # Return the current date in this time zone. |
271 | def today |
272 | tzinfo.now.to_date |
273 | end |
274 | |
275 | # Adjust the given time to the simultaneous time in the time zone represented by +self+. Returns a |
276 | # Time.utc() instance -- if you want an ActiveSupport::TimeWithZone instance, use Time#in_time_zone() instead. |
277 | def utc_to_local(time) |
278 | tzinfo.utc_to_local(time) |
279 | end |
280 | |
281 | # Adjust the given time to the simultaneous time in UTC. Returns a Time.utc() instance. |
282 | def local_to_utc(time, dst=true) |
283 | tzinfo.local_to_utc(time, dst) |
284 | end |
285 | |
286 | # Available so that TimeZone instances respond like TZInfo::Timezone instances |
287 | def period_for_utc(time) |
288 | tzinfo.period_for_utc(time) |
289 | end |
290 | |
291 | # Available so that TimeZone instances respond like TZInfo::Timezone instances |
292 | def period_for_local(time, dst=true) |
293 | tzinfo.period_for_local(time, dst) |
294 | end |
295 | |
296 | # TODO: Preload instead of lazy load for thread safety |
297 | def self.find_tzinfo(name) |
298 | require 'tzinfo' unless defined?(TZInfo) |
299 | ::TZInfo::Timezone.get(MAPPING[name] || name) |
300 | rescue TZInfo::InvalidTimezoneIdentifier |
301 | nil |
302 | end |
303 | |
304 | class << self |
305 | alias_method :create, :new |
306 | |
307 | # Return a TimeZone instance with the given name, or +nil+ if no |
308 | # such TimeZone instance exists. (This exists to support the use of |
309 | # this class with the +composed_of+ macro.) |
310 | def new(name) |
311 | self[name] |
312 | end |
313 | |
314 | # Return an array of all TimeZone objects. There are multiple |
315 | # TimeZone objects per time zone, in many cases, to make it easier |
316 | # for users to find their own time zone. |
317 | def all |
318 | @zones ||= zones_map.values.sort |
319 | end |
320 | |
321 | def zones_map |
322 | unless defined?(@zones_map) |
323 | @zones_map = {} |
324 | [[-39_600, "International Date Line West", "Midway Island", "Samoa" ], |
325 | [-36_000, "Hawaii" ], |
326 | [-32_400, "Alaska" ], |
327 | [-28_800, "Pacific Time (US & Canada)", "Tijuana" ], |
328 | [-25_200, "Mountain Time (US & Canada)", "Chihuahua", "Mazatlan", |
329 | "Arizona" ], |
330 | [-21_600, "Central Time (US & Canada)", "Saskatchewan", "Guadalajara", |
331 | "Mexico City", "Monterrey", "Central America" ], |
332 | [-18_000, "Eastern Time (US & Canada)", "Indiana (East)", "Bogota", |
333 | "Lima", "Quito" ], |
334 | [-16_200, "Caracas" ], |
335 | [-14_400, "Atlantic Time (Canada)", "La Paz", "Santiago" ], |
336 | [-12_600, "Newfoundland" ], |
337 | [-10_800, "Brasilia", "Buenos Aires", "Georgetown", "Greenland" ], |
338 | [ -7_200, "Mid-Atlantic" ], |
339 | [ -3_600, "Azores", "Cape Verde Is." ], |
340 | [ 0, "Dublin", "Edinburgh", "Lisbon", "London", "Casablanca", |
341 | "Monrovia", "UTC" ], |
342 | [ 3_600, "Belgrade", "Bratislava", "Budapest", "Ljubljana", "Prague", |
343 | "Sarajevo", "Skopje", "Warsaw", "Zagreb", "Brussels", |
344 | "Copenhagen", "Madrid", "Paris", "Amsterdam", "Berlin", |
345 | "Bern", "Rome", "Stockholm", "Vienna", |
346 | "West Central Africa" ], |
347 | [ 7_200, "Bucharest", "Cairo", "Helsinki", "Kyev", "Riga", "Sofia", |
348 | "Tallinn", "Vilnius", "Athens", "Istanbul", "Minsk", |
349 | "Jerusalem", "Harare", "Pretoria" ], |
350 | [ 10_800, "Moscow", "St. Petersburg", "Volgograd", "Kuwait", "Riyadh", |
351 | "Nairobi", "Baghdad" ], |
352 | [ 12_600, "Tehran" ], |
353 | [ 14_400, "Abu Dhabi", "Muscat", "Baku", "Tbilisi", "Yerevan" ], |
354 | [ 16_200, "Kabul" ], |
355 | [ 18_000, "Ekaterinburg", "Islamabad", "Karachi", "Tashkent" ], |
356 | [ 19_800, "Chennai", "Kolkata", "Mumbai", "New Delhi", "Sri Jayawardenepura" ], |
357 | [ 20_700, "Kathmandu" ], |
358 | [ 21_600, "Astana", "Dhaka", "Almaty", |
359 | "Novosibirsk" ], |
360 | [ 23_400, "Rangoon" ], |
361 | [ 25_200, "Bangkok", "Hanoi", "Jakarta", "Krasnoyarsk" ], |
362 | [ 28_800, "Beijing", "Chongqing", "Hong Kong", "Urumqi", |
363 | "Kuala Lumpur", "Singapore", "Taipei", "Perth", "Irkutsk", |
364 | "Ulaan Bataar" ], |
365 | [ 32_400, "Seoul", "Osaka", "Sapporo", "Tokyo", "Yakutsk" ], |
366 | [ 34_200, "Darwin", "Adelaide" ], |
367 | [ 36_000, "Canberra", "Melbourne", "Sydney", "Brisbane", "Hobart", |
368 | "Vladivostok", "Guam", "Port Moresby" ], |
369 | [ 39_600, "Magadan", "Solomon Is.", "New Caledonia" ], |
370 | [ 43_200, "Fiji", "Kamchatka", "Marshall Is.", "Auckland", |
371 | "Wellington" ], |
372 | [ 46_800, "Nuku'alofa" ]]. |
373 | each do |offset, *places| |
374 | places.each do |place| |
375 | @zones_map[place] = create(place, offset) |
376 | end |
377 | end |
378 | end |
379 | @zones_map |
380 | end |
381 | |
382 | # Locate a specific time zone object. If the argument is a string, it |
383 | # is interpreted to mean the name of the timezone to locate. If it is a |
384 | # numeric value it is either the hour offset, or the second offset, of the |
385 | # timezone to find. (The first one with that offset will be returned.) |
386 | # Returns +nil+ if no such time zone is known to the system. |
387 | def [](arg) |
388 | case arg |
389 | when String |
390 | zones_map[arg] ||= lookup(arg) |
391 | when Numeric, ActiveSupport::Duration |
392 | arg *= 3600 if arg.abs <= 13 |
393 | all.find { |z| z.utc_offset == arg.to_i } |
394 | else |
395 | raise ArgumentError, "invalid argument to TimeZone[]: #{arg.inspect}" |
396 | end |
397 | end |
398 | |
399 | # A convenience method for returning a collection of TimeZone objects |
400 | # for time zones in the USA. |
401 | def us_zones |
402 | @us_zones ||= all.find_all { |z| z.name =~ /US|Arizona|Indiana|Hawaii|Alaska/ } |
403 | end |
404 | |
405 | private |
406 | |
407 | def lookup(name) |
408 | (tzinfo = find_tzinfo(name)) && create(tzinfo.name.freeze) |
409 | end |
410 | end |
411 | end |
412 | end |