tztime 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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