~michaelforrest/use-case-mapper/trunk

« back to all changes in this revision

Viewing changes to vendor/rails/activesupport/lib/active_support/values/time_zone.rb

  • Committer: Richard Lee (Canonical)
  • Date: 2010-10-15 15:17:58 UTC
  • mfrom: (190.1.3 use-case-mapper)
  • Revision ID: richard.lee@canonical.com-20101015151758-wcvmfxrexsongf9d
Merge

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
 
176
 
    # Create a new TimeZone object with the given name and offset. The
177
 
    # offset is the number of seconds that this time zone is offset from UTC
178
 
    # (GMT). Seconds were chosen as the offset unit because that is the unit that
179
 
    # Ruby uses to represent time zone offsets (see Time#utc_offset).
180
 
    def initialize(name, utc_offset, tzinfo = nil)
181
 
      @name = name
182
 
      @utc_offset = utc_offset
183
 
      @tzinfo = tzinfo
184
 
    end
185
 
 
186
 
    def utc_offset
187
 
      @utc_offset ||= tzinfo.current_period.utc_offset
188
 
    end
189
 
 
190
 
    # Returns the offset of this time zone as a formatted string, of the
191
 
    # format "+HH:MM".
192
 
    def formatted_offset(colon=true, alternate_utc_string = nil)
193
 
      utc_offset == 0 && alternate_utc_string || utc_offset.to_utc_offset_s(colon)
194
 
    end
195
 
 
196
 
    # Compare this time zone to the parameter. The two are comapred first on
197
 
    # their offsets, and then by name.
198
 
    def <=>(zone)
199
 
      result = (utc_offset <=> zone.utc_offset)
200
 
      result = (name <=> zone.name) if result == 0
201
 
      result
202
 
    end
203
 
 
204
 
    # Compare #name and TZInfo identifier to a supplied regexp, returning true
205
 
    # if a match is found.
206
 
    def =~(re)
207
 
      return true if name =~ re || MAPPING[name] =~ re
208
 
    end
209
 
 
210
 
    # Returns a textual representation of this time zone.
211
 
    def to_s
212
 
      "(GMT#{formatted_offset}) #{name}"
213
 
    end
214
 
 
215
 
    # Method for creating new ActiveSupport::TimeWithZone instance in time zone of +self+ from given values. Example:
216
 
    #
217
 
    #   Time.zone = "Hawaii"                      # => "Hawaii"
218
 
    #   Time.zone.local(2007, 2, 1, 15, 30, 45)   # => Thu, 01 Feb 2007 15:30:45 HST -10:00
219
 
    def local(*args)
220
 
      time = Time.utc_time(*args)
221
 
      ActiveSupport::TimeWithZone.new(nil, self, time)
222
 
    end
223
 
 
224
 
    # Method for creating new ActiveSupport::TimeWithZone instance in time zone of +self+ from number of seconds since the Unix epoch. Example:
225
 
    #
226
 
    #   Time.zone = "Hawaii"        # => "Hawaii"
227
 
    #   Time.utc(2000).to_f         # => 946684800.0
228
 
    #   Time.zone.at(946684800.0)   # => Fri, 31 Dec 1999 14:00:00 HST -10:00
229
 
    def at(secs)
230
 
      utc = Time.at(secs).utc rescue DateTime.civil(1970).since(secs)
231
 
      utc.in_time_zone(self)
232
 
    end
233
 
 
234
 
    # Method for creating new ActiveSupport::TimeWithZone instance in time zone of +self+ from parsed string. Example:
235
 
    #
236
 
    #   Time.zone = "Hawaii"                      # => "Hawaii"
237
 
    #   Time.zone.parse('1999-12-31 14:00:00')    # => Fri, 31 Dec 1999 14:00:00 HST -10:00
238
 
    #
239
 
    # If upper components are missing from the string, they are supplied from TimeZone#now:
240
 
    #
241
 
    #   Time.zone.now                 # => Fri, 31 Dec 1999 14:00:00 HST -10:00
242
 
    #   Time.zone.parse('22:30:00')   # => Fri, 31 Dec 1999 22:30:00 HST -10:00
243
 
    def parse(str, now=now)
244
 
      date_parts = Date._parse(str)
245
 
      return if date_parts.blank?
246
 
      time = Time.parse(str, now) rescue DateTime.parse(str)
247
 
      if date_parts[:offset].nil?
248
 
        ActiveSupport::TimeWithZone.new(nil, self, time)
249
 
      else
250
 
        time.in_time_zone(self)
251
 
      end
252
 
    end
253
 
 
254
 
    # Returns an ActiveSupport::TimeWithZone instance representing the current time
255
 
    # in the time zone represented by +self+. Example:
256
 
    #
257
 
    #   Time.zone = 'Hawaii'  # => "Hawaii"
258
 
    #   Time.zone.now         # => Wed, 23 Jan 2008 20:24:27 HST -10:00
259
 
    def now
260
 
      Time.now.utc.in_time_zone(self)
261
 
    end
262
 
 
263
 
    # Return the current date in this time zone.
264
 
    def today
265
 
      tzinfo.now.to_date
266
 
    end
267
 
 
268
 
    # Adjust the given time to the simultaneous time in the time zone represented by +self+. Returns a
269
 
    # Time.utc() instance -- if you want an ActiveSupport::TimeWithZone instance, use Time#in_time_zone() instead.
270
 
    def utc_to_local(time)
271
 
      tzinfo.utc_to_local(time)
272
 
    end
273
 
 
274
 
    # Adjust the given time to the simultaneous time in UTC. Returns a Time.utc() instance.
275
 
    def local_to_utc(time, dst=true)
276
 
      tzinfo.local_to_utc(time, dst)
277
 
    end
278
 
 
279
 
    # Available so that TimeZone instances respond like TZInfo::Timezone instances
280
 
    def period_for_utc(time)
281
 
      tzinfo.period_for_utc(time)
282
 
    end
283
 
 
284
 
    # Available so that TimeZone instances respond like TZInfo::Timezone instances
285
 
    def period_for_local(time, dst=true)
286
 
      tzinfo.period_for_local(time, dst)
287
 
    end
288
 
 
289
 
    # TODO: Preload instead of lazy load for thread safety
290
 
    def tzinfo
291
 
      require 'tzinfo' unless defined?(TZInfo)
292
 
      @tzinfo ||= TZInfo::Timezone.get(MAPPING[name])
293
 
    end
294
 
 
295
 
    unless const_defined?(:ZONES)
296
 
      ZONES = []
297
 
      ZONES_MAP = {}
298
 
      [[-39_600, "International Date Line West", "Midway Island", "Samoa" ],
299
 
       [-36_000, "Hawaii" ],
300
 
       [-32_400, "Alaska" ],
301
 
       [-28_800, "Pacific Time (US & Canada)", "Tijuana" ],
302
 
       [-25_200, "Mountain Time (US & Canada)", "Chihuahua", "Mazatlan",
303
 
                 "Arizona" ],
304
 
       [-21_600, "Central Time (US & Canada)", "Saskatchewan", "Guadalajara",
305
 
                 "Mexico City", "Monterrey", "Central America" ],
306
 
       [-18_000, "Eastern Time (US & Canada)", "Indiana (East)", "Bogota",
307
 
                 "Lima", "Quito" ],
308
 
       [-16_200, "Caracas" ],
309
 
       [-14_400, "Atlantic Time (Canada)", "La Paz", "Santiago" ],
310
 
       [-12_600, "Newfoundland" ],
311
 
       [-10_800, "Brasilia", "Buenos Aires", "Georgetown", "Greenland" ],
312
 
       [ -7_200, "Mid-Atlantic" ],
313
 
       [ -3_600, "Azores", "Cape Verde Is." ],
314
 
       [      0, "Dublin", "Edinburgh", "Lisbon", "London", "Casablanca",
315
 
                 "Monrovia", "UTC" ],
316
 
       [  3_600, "Belgrade", "Bratislava", "Budapest", "Ljubljana", "Prague",
317
 
                 "Sarajevo", "Skopje", "Warsaw", "Zagreb", "Brussels",
318
 
                 "Copenhagen", "Madrid", "Paris", "Amsterdam", "Berlin",
319
 
                 "Bern", "Rome", "Stockholm", "Vienna",
320
 
                 "West Central Africa" ],
321
 
       [  7_200, "Bucharest", "Cairo", "Helsinki", "Kyev", "Riga", "Sofia",
322
 
                 "Tallinn", "Vilnius", "Athens", "Istanbul", "Minsk",
323
 
                 "Jerusalem", "Harare", "Pretoria" ],
324
 
       [ 10_800, "Moscow", "St. Petersburg", "Volgograd", "Kuwait", "Riyadh",
325
 
                 "Nairobi", "Baghdad" ],
326
 
       [ 12_600, "Tehran" ],
327
 
       [ 14_400, "Abu Dhabi", "Muscat", "Baku", "Tbilisi", "Yerevan" ],
328
 
       [ 16_200, "Kabul" ],
329
 
       [ 18_000, "Ekaterinburg", "Islamabad", "Karachi", "Tashkent" ],
330
 
       [ 19_800, "Chennai", "Kolkata", "Mumbai", "New Delhi", "Sri Jayawardenepura" ],
331
 
       [ 20_700, "Kathmandu" ],
332
 
       [ 21_600, "Astana", "Dhaka", "Almaty",
333
 
                 "Novosibirsk" ],
334
 
       [ 23_400, "Rangoon" ],
335
 
       [ 25_200, "Bangkok", "Hanoi", "Jakarta", "Krasnoyarsk" ],
336
 
       [ 28_800, "Beijing", "Chongqing", "Hong Kong", "Urumqi",
337
 
                 "Kuala Lumpur", "Singapore", "Taipei", "Perth", "Irkutsk",
338
 
                 "Ulaan Bataar" ],
339
 
       [ 32_400, "Seoul", "Osaka", "Sapporo", "Tokyo", "Yakutsk" ],
340
 
       [ 34_200, "Darwin", "Adelaide" ],
341
 
       [ 36_000, "Canberra", "Melbourne", "Sydney", "Brisbane", "Hobart",
342
 
                 "Vladivostok", "Guam", "Port Moresby" ],
343
 
       [ 39_600, "Magadan", "Solomon Is.", "New Caledonia" ],
344
 
       [ 43_200, "Fiji", "Kamchatka", "Marshall Is.", "Auckland",
345
 
                 "Wellington" ],
346
 
       [ 46_800, "Nuku'alofa" ]].
347
 
      each do |offset, *places|
348
 
        places.each do |place|
349
 
          place.freeze
350
 
          zone = new(place, offset)
351
 
          ZONES << zone
352
 
          ZONES_MAP[place] = zone
353
 
        end
354
 
      end
355
 
      ZONES.sort!
356
 
      ZONES.freeze
357
 
      ZONES_MAP.freeze
358
 
 
359
 
      US_ZONES = ZONES.find_all { |z| z.name =~ /US|Arizona|Indiana|Hawaii|Alaska/ }
360
 
      US_ZONES.freeze
361
 
    end
362
 
 
363
 
    class << self
364
 
      alias_method :create, :new
365
 
 
366
 
      # Return a TimeZone instance with the given name, or +nil+ if no
367
 
      # such TimeZone instance exists. (This exists to support the use of
368
 
      # this class with the +composed_of+ macro.)
369
 
      def new(name)
370
 
        self[name]
371
 
      end
372
 
 
373
 
      # Return an array of all TimeZone objects. There are multiple
374
 
      # TimeZone objects per time zone, in many cases, to make it easier
375
 
      # for users to find their own time zone.
376
 
      def all
377
 
        ZONES
378
 
      end
379
 
 
380
 
      # Locate a specific time zone object. If the argument is a string, it
381
 
      # is interpreted to mean the name of the timezone to locate. If it is a
382
 
      # numeric value it is either the hour offset, or the second offset, of the
383
 
      # timezone to find. (The first one with that offset will be returned.)
384
 
      # Returns +nil+ if no such time zone is known to the system.
385
 
      def [](arg)
386
 
        case arg
387
 
          when String
388
 
            ZONES_MAP[arg]
389
 
          when Numeric, ActiveSupport::Duration
390
 
            arg *= 3600 if arg.abs <= 13
391
 
            all.find { |z| z.utc_offset == arg.to_i }
392
 
          else
393
 
            raise ArgumentError, "invalid argument to TimeZone[]: #{arg.inspect}"
394
 
        end
395
 
      end
396
 
 
397
 
      # A convenience method for returning a collection of TimeZone objects
398
 
      # for time zones in the USA.
399
 
      def us_zones
400
 
        US_ZONES
401
 
      end
402
 
    end
403
 
  end
404
 
end