tztime 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,424 @@
1
+ # An instance of LocalTime::Builder is used to generate LocalTime instances
2
+ # based on the +time_zone+ of the Builder. The LocalTime instances wrap
3
+ # a Time instance that has properties reflective of +time_zone+. The Time
4
+ # values themselves are represented internally as UTC, but the time has
5
+ # been offset by the +time_zone+. This is due to a limitation of the Time
6
+ # class in Ruby.
7
+ #
8
+ # The Builder is created by passing in either a String that names a time
9
+ # zone, or a TZInfo time zone definition. The String name can be either
10
+ # the format used by TZInfo or Rails.
11
+ #
12
+ # === Usage
13
+ # Create a new Builder for the time zone in New York.
14
+ #
15
+ # builder = TZTime::LocalTime::Builder.new('America/New_York')
16
+ # puts builder.time_zone_name # => America/New_York
17
+ #
18
+ # Underneath, this will have the same TZInfo time zone definition as above.
19
+ #
20
+ # builder = TZTime::LocalTime::Builder.new('Eastern Time (US & Canada)')
21
+ # puts builder.time_zone_name # => Eastern Time (US & Canada)
22
+ #
23
+ # +now+ will create the current day and time, while +today+ will create the
24
+ # current day with time at 0:00. Both of these will be converted to the
25
+ # +time_zone+ of the Builder instance.
26
+ #
27
+ # puts builder.now # => 2007-12-12 22:28:09 EST
28
+ # puts builder.today # => 2007-12-12 00:00:00 EST
29
+ #
30
+ # The Builder can also create LocalTime instances at specific times using
31
+ # +local+ and +utc+. +local+ assumes the values passed in are expressed
32
+ # in the +time_zone+, while +utc+ assumes the values are in Univeral Time
33
+ # and need to be converted into the +time_zone+ time.
34
+ #
35
+ # puts builder.utc(2007, 12, 13, 3, 36, 26) # => 2007-12-12 22:36:26 EST
36
+ # puts builder.local(2007, 12, 13, 3, 36, 26) # => 2007-12-13 03:36:26 EST
37
+ # puts builder.local(2007, 12, 12, 22, 36, 26) # => 2007-12-12 22:36:26 EST
38
+ #
39
+ # Using, +at_local+ and +at_utc+, the Builder can convert existing time
40
+ # values into localized values. +at_local+ assumes time values represent
41
+ # the current +time_zone+, ignoring the time zone of the value. +at_utc+
42
+ # assumes the time values represent Universal Time values while still
43
+ # ignoring the time zone of the value.
44
+ #
45
+ # time = Time.utc(2007, 12, 13, 3, 36, 26)
46
+ # puts builder.at_utc(time) # => 2007-12-12 22:36:26 EST
47
+ # puts builder.at_local(time) # => 2007-12-13 03:36:26 EST
48
+ #
49
+ # time = Time.local(2007, 12, 13, 3, 36, 26)
50
+ # puts builder.at_utc(time) # => 2007-12-12 22:36:26 EST
51
+ # puts builder.at_local(time) # => 2007-12-13 03:36:26 EST
52
+ #
53
+ # +at+ is aliased to +at_local+, so it can be used instead.
54
+ #
55
+ # Converting from a seperate time zone into the builder's time zone can be done
56
+ # with the +convert+ method. This will convert from the time zone of the time
57
+ # value. Optionally, an explicit time zone can be specified:
58
+ #
59
+ # builder = TZTime::LocalTime::Builder.new('America/Los_Angeles')
60
+ # time = Time.now
61
+ # # time zone is EDT
62
+ # puts time # => Thu Apr 10 18:42:40 -0400 2008
63
+ # puts builder.convert(time) # => 2008-04-10 15:42:40 PDT
64
+ # puts builder.convert(time, 'America/Chicago') # => 2008-04-10 16:42:40 PDT
65
+ class TZTime::LocalTime::Builder
66
+ # Uses +get+ to acquire a Builder instance in the UTC time zone.
67
+ def self.utc
68
+ new('UTC')
69
+ end
70
+
71
+ # Gets a time zone instance for a given +name+. The +name+ can be either the
72
+ # names used by TZInfo or the TimeZone class in ActiveSupport.
73
+ def self.get_time_zone(name)
74
+ TZInfo::Timezone.get(RAILS_CONVERSIONS[name] || name)
75
+ end
76
+
77
+ # Creates a new LocalTime::Builder that can create LocalTime instances
78
+ # relative to the given time zone.
79
+ #
80
+ # The +time_zone+ must be either a String that names a time zone, or an
81
+ # instance of a TZInfo time zone definition. The String name can be either
82
+ # the format used by TZInfo or Rails.
83
+ #
84
+ # If a String name is passed in, this value will be retained and accessible
85
+ # by +time_zone_name+. If a TZInfo time zone definition value is passed,
86
+ # then +time_zone_name+ will return the +name+ attribute of the TZInfo time
87
+ # zone definition.
88
+ def initialize(time_zone)
89
+ if time_zone.is_a?(String)
90
+ @time_zone = self.class.get_time_zone(time_zone)
91
+ @time_zone_name = time_zone
92
+ else
93
+ @time_zone = time_zone
94
+ @time_zone_name = time_zone.name
95
+ end
96
+ freeze
97
+ end
98
+
99
+ # The name of the time zone. This will reflect the name that was passed to
100
+ # +new+. If a rails-style time zone name was used, this will return that
101
+ # value. Otherwise it will return the +name+ attribute from the TZInfo time
102
+ # zone definition.
103
+ def time_zone_name
104
+ @time_zone_name
105
+ end
106
+
107
+ # The TZInfo time zone instance
108
+ def time_zone
109
+ @time_zone
110
+ end
111
+
112
+ # Create a new LocalTime object representing the current day and time
113
+ # in the given +time_zone+.
114
+ def now
115
+ create(@time_zone.utc_to_local(Time.now.utc))
116
+ end
117
+
118
+ # Create a new LocalTime object representing the current day in the
119
+ # given +time_zone+.
120
+ def today
121
+ time = @time_zone.utc_to_local(Time.now.utc)
122
+ create(Time.utc(time.year, time.month, time.day))
123
+ end
124
+
125
+ # call-seq:
126
+ # today_at_local(hour [, min, sec, usec]) => LocalTime
127
+ # today_at(hour [, min, sec, usec]) => LocalTime
128
+ #
129
+ # Create a new LocalTime object representing the current day and the
130
+ # specified time. The time values are assumed to be in local +time_zone+.
131
+ def today_at_local(*args)
132
+ day_at_local(today, *args)
133
+ end
134
+
135
+ alias today_at today_at_local # :nodoc:
136
+
137
+ # call-seq:
138
+ # today_at_utc(hour [, min, sec, usec]) => LocalTime
139
+ # today_at_gm(hour [, min, sec, usec]) => LocalTime
140
+ #
141
+ # Create a new LocalTime object representing the current day and the
142
+ # specified time. The time values are assumed to be in Universal Time and
143
+ # will be converted to the +time_zone+.
144
+ def today_at_utc(*args)
145
+ day_at_utc(today, *args)
146
+ end
147
+
148
+ alias today_at_gm today_at_utc # :nodoc:
149
+
150
+ # call-seq:
151
+ # day_at_local(time [, min, sec, usec]) => LocalTime
152
+ # day_at(time [, min, sec, usec]) => LocalTime
153
+ #
154
+ # Create a new LocalTime object representing the day in +time+ and the
155
+ # specified time. The time values are assumed to be in local +time_zone+.
156
+ def day_at_local(time, *args)
157
+ local(time.year, time.month, time.day, *args)
158
+ end
159
+
160
+ alias day_at day_at_local # :nodoc:
161
+
162
+ # call-seq:
163
+ # day_at_utc(time [, min, sec, usec]) => LocalTime
164
+ # day_at_gm(time [, min, sec, usec]) => LocalTime
165
+ #
166
+ # Create a new LocalTime object representing the day in +time+ and the
167
+ # specified time. The time values are assumed to be in Universal Time and
168
+ # will be converted to the +time_zone+.
169
+ def day_at_utc(time, *args)
170
+ utc(time.year, time.month, time.day, *args)
171
+ end
172
+
173
+ alias day_at_gm day_at_utc
174
+
175
+ # call-seq:
176
+ # local(year [, month, day, hour, min, sec, usec]) => LocalTime
177
+ # local(sec, min, hour, day, month, year, wday, yday, isdst, tz) => LocalTime
178
+ #
179
+ # Create a LocalTime where the arguments are expressed locally to the +time_zone+.
180
+ def local(*args)
181
+ create(Time.utc(*args))
182
+ end
183
+
184
+ # call-seq:
185
+ # utc(year [, month, day, hour, min, sec, usec]) => LocalTime
186
+ # utc(sec, min, hour, day, month, year, wday, yday, isdst, tz) => LocalTime
187
+ # gm(year [, month, day, hour, min, sec, usec]) => LocalTime
188
+ # gm(sec, min, hour, day, month, year, wday, yday, isdst, tz) => LocalTime
189
+ #
190
+ # Create a LocalTime where the arguments are expressed in UTC. This will convert
191
+ # the time value into the local +time_zone+.
192
+ def utc(*args)
193
+ create(@time_zone.utc_to_local(Time.utc(*args)))
194
+ end
195
+
196
+ alias gm utc # :nodoc:
197
+
198
+ # call-seq:
199
+ # at_local(time) => LocalTime
200
+ # at_local(second [, microseconds]) => LocalTime
201
+ # at(time) => LocalTime
202
+ # at(second [, microseconds]) => LocalTime
203
+ #
204
+ # Takes a Time instance and returns a LocalTime instance. The time is read
205
+ # as if in the timezone of this Builder instance, regardless of the time
206
+ # zone value. However, if a LocalTime instance is passed in, the proper
207
+ # conversion will take place.
208
+ def at_local(*args)
209
+ extract_and_create_local_time(args, true)
210
+ end
211
+
212
+ alias at at_local # :nodoc:
213
+
214
+ # call-seq:
215
+ # at_utc(time) => LocalTime
216
+ # at_utc(second [, microseconds]) => LocalTime
217
+ # at_gm(time) => LocalTime
218
+ # at_gm(second [, microseconds]) => LocalTime
219
+ #
220
+ # Takes a Time instance and returns a LocalTime instance. The time is read
221
+ # as if in universal time and converted to the, regardless of the time zone
222
+ # value. However, if a LocalTime instance is passed in, the proper conversion
223
+ # will take place.
224
+ def at_utc(*args)
225
+ extract_and_create_local_time(args, false)
226
+ end
227
+
228
+ alias at_gm at_utc# :nodoc:
229
+
230
+ # Creates a new LocalTime instance converted from +local_time+'s time zone
231
+ # into this time zone. If +local_time+ is in the same time zone, then
232
+ # +local_time+ will be returned. If +local_time+ is not an instance of
233
+ # LocalTime, then +from_time_zone+ must be passed in. +from_time_zone+ will be ignored
234
+ # if +local_time+ is an instance of LocalTime.
235
+ def convert(local_time, from_time_zone=nil)
236
+ if from_time_zone
237
+ b = self.class.new(from_time_zone)
238
+ t = b.at(local_time).getutc
239
+ utc(t.year, t.month, t.day, t.hour, t.min, t.sec, t.usec)
240
+ elsif local_time.respond_to?(:getutc)
241
+ t = local_time.getutc
242
+ utc(t.year, t.month, t.day, t.hour, t.min, t.sec, t.usec)
243
+ else
244
+ nil
245
+ end
246
+ end
247
+
248
+ protected
249
+
250
+ def extract_and_create_local_time(time_args, local=true)
251
+ time = extract_time_from_args(time_args)
252
+ if time.is_a?(TZTime::LocalTime)
253
+ convert(time)
254
+ elsif local
255
+ local(time.year, time.month, time.day, time.hour, time.min, time.sec, time.usec)
256
+ else
257
+ utc(time.year, time.month, time.day, time.hour, time.min, time.sec, time.usec)
258
+ end
259
+ end
260
+
261
+ # Creates a LocalTime object from +time+ and the given +time_zone+.
262
+ # The time value does not get modified. In other words, it is assumed
263
+ # to be in the +time_zone+.
264
+ def create(time)
265
+ TZTime::LocalTime.new(time, @time_zone)
266
+ end
267
+
268
+ # Converts +args+ into a Time object.
269
+ def extract_time_from_args(args)
270
+ time = args.first
271
+ case time
272
+ when TZTime::LocalTime then args.first
273
+ when Time then args.first
274
+ when DateTime then Time.utc(time.year, time.month, time.day, time.hour, time.min, time.sec, 0)
275
+ when Date then Time.utc(time.year, time.month, time.day, 0, 0, 0, 0)
276
+ else Time.at(*args)
277
+ end
278
+ end
279
+
280
+ RAILS_CONVERSIONS = {
281
+ "International Date Line West" => "Pacific/Midway",
282
+ "Midway Island" => "Pacific/Midway",
283
+ "Samoa" => "Pacific/Pago_Pago",
284
+ "Hawaii" => "Pacific/Honolulu",
285
+ "Alaska" => "America/Juneau",
286
+ "Pacific Time (US & Canada)" => "America/Los_Angeles",
287
+ "Tijuana" => "America/Tijuana",
288
+ "Mountain Time (US & Canada)" => "America/Denver",
289
+ "Arizona" => "America/Phoenix",
290
+ "Chihuahua" => "America/Chihuahua",
291
+ "Mazatlan" => "America/Mazatlan",
292
+ "Central Time (US & Canada)" => "America/Chicago",
293
+ "Saskatchewan" => "America/Regina",
294
+ "Guadalajara" => "America/Mexico_City",
295
+ "Mexico City" => "America/Mexico_City",
296
+ "Monterrey" => "America/Monterrey",
297
+ "Central America" => "America/Guatemala",
298
+ "Eastern Time (US & Canada)" => "America/New_York",
299
+ "Indiana (East)" => "America/Indiana/Indianapolis",
300
+ "Bogota" => "America/Bogota",
301
+ "Lima" => "America/Lima",
302
+ "Quito" => "America/Lima",
303
+ "Atlantic Time (Canada)" => "America/Halifax",
304
+ "Caracas" => "America/Caracas",
305
+ "La Paz" => "America/La_Paz",
306
+ "Santiago" => "America/Santiago",
307
+ "Newfoundland" => "America/St_Johns",
308
+ "Brasilia" => "America/Argentina/Buenos_Aires",
309
+ "Buenos Aires" => "America/Argentina/Buenos_Aires",
310
+ "Georgetown" => "America/Argentina/San_Juan",
311
+ "Greenland" => "America/Godthab",
312
+ "Mid-Atlantic" => "Atlantic/South_Georgia",
313
+ "Azores" => "Atlantic/Azores",
314
+ "Cape Verde Is." => "Atlantic/Cape_Verde",
315
+ "Dublin" => "Europe/Dublin",
316
+ "Edinburgh" => "Europe/Dublin",
317
+ "Lisbon" => "Europe/Lisbon",
318
+ "London" => "Europe/London",
319
+ "Casablanca" => "Africa/Casablanca",
320
+ "Monrovia" => "Africa/Monrovia",
321
+ "Belgrade" => "Europe/Belgrade",
322
+ "Bratislava" => "Europe/Bratislava",
323
+ "Budapest" => "Europe/Budapest",
324
+ "Ljubljana" => "Europe/Ljubljana",
325
+ "Prague" => "Europe/Prague",
326
+ "Sarajevo" => "Europe/Sarajevo",
327
+ "Skopje" => "Europe/Skopje",
328
+ "Warsaw" => "Europe/Warsaw",
329
+ "Zagreb" => "Europe/Zagreb",
330
+ "Brussels" => "Europe/Brussels",
331
+ "Copenhagen" => "Europe/Copenhagen",
332
+ "Madrid" => "Europe/Madrid",
333
+ "Paris" => "Europe/Paris",
334
+ "Amsterdam" => "Europe/Amsterdam",
335
+ "Berlin" => "Europe/Berlin",
336
+ "Bern" => "Europe/Berlin",
337
+ "Rome" => "Europe/Rome",
338
+ "Stockholm" => "Europe/Stockholm",
339
+ "Vienna" => "Europe/Vienna",
340
+ "West Central Africa" => "Africa/Algiers",
341
+ "Bucharest" => "Europe/Bucharest",
342
+ "Cairo" => "Africa/Cairo",
343
+ "Helsinki" => "Europe/Helsinki",
344
+ "Kyev" => "Europe/Kiev",
345
+ "Riga" => "Europe/Riga",
346
+ "Sofia" => "Europe/Sofia",
347
+ "Tallinn" => "Europe/Tallinn",
348
+ "Vilnius" => "Europe/Vilnius",
349
+ "Athens" => "Europe/Athens",
350
+ "Istanbul" => "Europe/Istanbul",
351
+ "Minsk" => "Europe/Minsk",
352
+ "Jerusalem" => "Asia/Jerusalem",
353
+ "Harare" => "Africa/Harare",
354
+ "Pretoria" => "Africa/Johannesburg",
355
+ "Moscow" => "Europe/Moscow",
356
+ "St. Petersburg" => "Europe/Moscow",
357
+ "Volgograd" => "Europe/Moscow",
358
+ "Kuwait" => "Asia/Kuwait",
359
+ "Riyadh" => "Asia/Riyadh",
360
+ "Nairobi" => "Africa/Nairobi",
361
+ "Baghdad" => "Asia/Baghdad",
362
+ "Tehran" => "Asia/Tehran",
363
+ "Abu Dhabi" => "Asia/Muscat",
364
+ "Muscat" => "Asia/Muscat",
365
+ "Baku" => "Asia/Baku",
366
+ "Tbilisi" => "Asia/Tbilisi",
367
+ "Yerevan" => "Asia/Yerevan",
368
+ "Kabul" => "Asia/Kabul",
369
+ "Ekaterinburg" => "Asia/Yekaterinburg",
370
+ "Islamabad" => "Asia/Karachi",
371
+ "Karachi" => "Asia/Karachi",
372
+ "Tashkent" => "Asia/Tashkent",
373
+ "Chennai" => "Asia/Calcutta",
374
+ "Kolkata" => "Asia/Calcutta",
375
+ "Mumbai" => "Asia/Calcutta",
376
+ "New Delhi" => "Asia/Calcutta",
377
+ "Kathmandu" => "Asia/Katmandu",
378
+ "Astana" => "Asia/Dhaka",
379
+ "Dhaka" => "Asia/Dhaka",
380
+ "Sri Jayawardenepura" => "Asia/Dhaka",
381
+ "Almaty" => "Asia/Almaty",
382
+ "Novosibirsk" => "Asia/Novosibirsk",
383
+ "Rangoon" => "Asia/Rangoon",
384
+ "Bangkok" => "Asia/Bangkok",
385
+ "Hanoi" => "Asia/Bangkok",
386
+ "Jakarta" => "Asia/Jakarta",
387
+ "Krasnoyarsk" => "Asia/Krasnoyarsk",
388
+ "Beijing" => "Asia/Shanghai",
389
+ "Chongqing" => "Asia/Chongqing",
390
+ "Hong Kong" => "Asia/Hong_Kong",
391
+ "Urumqi" => "Asia/Urumqi",
392
+ "Kuala Lumpur" => "Asia/Kuala_Lumpur",
393
+ "Singapore" => "Asia/Singapore",
394
+ "Taipei" => "Asia/Taipei",
395
+ "Perth" => "Australia/Perth",
396
+ "Irkutsk" => "Asia/Irkutsk",
397
+ "Ulaan Bataar" => "Asia/Ulaanbaatar",
398
+ "Seoul" => "Asia/Seoul",
399
+ "Osaka" => "Asia/Tokyo",
400
+ "Sapporo" => "Asia/Tokyo",
401
+ "Tokyo" => "Asia/Tokyo",
402
+ "Yakutsk" => "Asia/Yakutsk",
403
+ "Darwin" => "Australia/Darwin",
404
+ "Adelaide" => "Australia/Adelaide",
405
+ "Canberra" => "Australia/Melbourne",
406
+ "Melbourne" => "Australia/Melbourne",
407
+ "Sydney" => "Australia/Sydney",
408
+ "Brisbane" => "Australia/Brisbane",
409
+ "Hobart" => "Australia/Hobart",
410
+ "Vladivostok" => "Asia/Vladivostok",
411
+ "Guam" => "Pacific/Guam",
412
+ "Port Moresby" => "Pacific/Port_Moresby",
413
+ "Magadan" => "Asia/Magadan",
414
+ "Solomon Is." => "Asia/Magadan",
415
+ "New Caledonia" => "Pacific/Noumea",
416
+ "Fiji" => "Pacific/Fiji",
417
+ "Kamchatka" => "Asia/Kamchatka",
418
+ "Marshall Is." => "Pacific/Majuro",
419
+ "Auckland" => "Pacific/Auckland",
420
+ "Wellington" => "Pacific/Auckland",
421
+ "Nuku'alofa" => "Pacific/Tongatapu"
422
+ }
423
+ end
424
+
@@ -0,0 +1,81 @@
1
+ # This module is intented to be included into classes that need to store and
2
+ # access time zones (TZInfo time zone definition) and local time builders
3
+ # (TZTime::LocalTime::Builder). It adds a method for assigning a time zone
4
+ # to the instance. A LocalTime::Builder is created when a time zone is set.
5
+ # The builder is used to generate LocalTime instances in the specified time zone.
6
+ #
7
+ # === Usage
8
+ # Add to any class:
9
+ # class SomeClass
10
+ # include TZTime::TimeZoneElement
11
+ # end
12
+ #
13
+ # Create an instance of the class and assign a time zone:
14
+ # sc = SomeClass.new
15
+ # sc.time_zone = 'America/New_York'
16
+ #
17
+ # or use Rails style time zone names:
18
+ # sc.time_zone = 'Eastern Time (US & Canada)
19
+ #
20
+ # or use a TZInfo::TimeZone instance:
21
+ # time_zone = TZInfo::Timezone.get('America/New_York')
22
+ # sc.time_zone = time_zone
23
+ #
24
+ # Now the instance can be used to generate times:
25
+ # puts sc.time_zone_builder.now # => 2008-03-31 21:27:19 EDT
26
+ # puts sc.time_zone_builder.today # => 2008-03-31 00:00:00 EDT
27
+ # puts sc.time_zone_builder.now.utc # => Tue Apr 01 01:27:19 UTC 2008
28
+ module TZTime::TimeZoneElement
29
+ # retrieves the TZTime::LocalTime::Builder instance or +nil+.
30
+ def local_time_builder
31
+ return @local_time_builder if defined?(@local_time_builder)
32
+ reset_local_time_builder!
33
+ end
34
+
35
+ # Assigns a new TZTime::LocalTime:Builder instance. This will also change
36
+ # the +time_zone+ and +time_zone_name+ values.
37
+ def local_time_builder=(value)
38
+ @local_time_builder = value
39
+ end
40
+
41
+ # Retrieves the full name of the time zone.
42
+ def time_zone_name
43
+ local_time_builder.time_zone_name
44
+ end
45
+
46
+ # Retrieves the current time zone definition.
47
+ def time_zone
48
+ local_time_builder.time_zone
49
+ end
50
+
51
+ # Sets the time zone for the parser. This is used to calculate offset values
52
+ # for the dates and times parsed from input. The value should be either a
53
+ # <tt>TZInfo::Timezone</tt> instance or a string that refers to a time zone
54
+ # when passed to <tt>TZInfo::Timezone#get</tt>. If the value is +nil+, then
55
+ # parsing will be assumed in UTC.
56
+ def time_zone=(value)
57
+ if value
58
+ @local_time_builder = TZTime::LocalTime::Builder.new(value)
59
+ else
60
+ reset_local_time_builder!
61
+ end
62
+ end
63
+
64
+ alias time_zone_name= time_zone=
65
+
66
+ # Resets the +local_time_builder+ to the default which is UTC.
67
+ def reset_local_time_builder!
68
+ @local_time_builder = TZTime::LocalTime::Builder.utc
69
+ end
70
+
71
+ # Selects a TZTime::LocalTime::Builder instance giving precedence to the
72
+ # +options+. If +options+ contains a <tt>:local_time_builder</tt> value, that
73
+ # will be returned. If +options+ contains a <tt>:time_zone</tt> value, then a
74
+ # builder will be instantiated for that time zone. Otherwise, the currentnly
75
+ # set +local_time_builder+ will be returned.
76
+ def select_local_time_builder(options={})
77
+ options[:local_time_builder] ||
78
+ (options[:time_zone] && TZTime::LocalTime::Builder.new(options[:time_zone])) ||
79
+ local_time_builder
80
+ end
81
+ end