et-orbi 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (8) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/README.md +48 -0
  4. data/lib/et-orbi.rb +300 -243
  5. metadata +14 -17
  6. data/cases.txt +0 -15
  7. data/out.txt +0 -2502
  8. data/out2.txt +0 -2487
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b59057ba3391277520b52cedaaca8c8beca1805f
4
- data.tar.gz: 8c2100802014372e3b7cf7c8a1ca9a4c4baff5fe
3
+ metadata.gz: 4c7c982477f7068d7be62fbb84846f81de496a87
4
+ data.tar.gz: ec767fc4ce7efd5413d17b5cd90f69f9cc1dc6cd
5
5
  SHA512:
6
- metadata.gz: 573c6898345ba811080c7975045ddd87ca8ddc5a65d0c2893475d15eb829c60e343ced79dc20823ea676bc440999c61b6d8e395d5c10aa9ee35f085a73ecfda0
7
- data.tar.gz: c48b752e6e0ccc717d9a1103b63f075f2e80cd0837d3494fb1e03d3083c6c3db637919cda3d85ae3b05b72a10f30d266ec97bc7fef78fb6c16b3acfb1d84fc1f
6
+ metadata.gz: ea3c1d4c24633d9820a1e9c58b30e9b0e418c91c9eda5308c20782210c4b041500df882a466e327e6110be1323966e64eaaf291aa12288e87cd96619f88bba91
7
+ data.tar.gz: ee248987a931f7ec23c58048b45f6970d082046656d790a2eb8c4fccec14ee1c58ff56484fba0e9cd0e18ae51ed452579e7909582de9d51c9e664f63b6fda090
data/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
  # et-orbi CHANGELOG.md
3
3
 
4
4
 
5
+ ## et-orbi 1.0.2 released 2017-03-24
6
+
7
+ - Enhance no zone ArgumentError data
8
+ - Separate module methods from EoTime methods
9
+
10
+
5
11
  ## et-orbi 1.0.1 released 2017-03-22
6
12
 
7
13
  - Detail Rails and Active Support info in nozone err
data/README.md CHANGED
@@ -6,6 +6,54 @@
6
6
 
7
7
  Time zones for [fugit](https://github.com/floraison/fugit) and for [rufus-scheduler](https://github.com/jmettraux/rufus-scheduler). Urbi et Orbi.
8
8
 
9
+ `EtOrbi::EoTime` instances quack like Ruby `Time` instances, but their `#zone` returns a `TZInfo::TimeZone` instance.
10
+
11
+ Getting `EoTime` instances:
12
+ ```ruby
13
+ require 'et-orbi'
14
+
15
+ EtOrbi.now
16
+ # => #<EtOrbi::EoTime:0x007f94d94 ...>
17
+ EtOrbi.parse('2017-12-13 13:00:00 America/Jamaica')
18
+ # => #<EtOrbi::EoTime:0x007f94d90 @zone=#<TZInfo::DataTimezone: America/Jamaica>...>
19
+ EtOrbi.make_time(Time.now)
20
+ # => #<EtOrbi::EoTime:0x007f94d91 ...>
21
+
22
+ EtOrbi::EoTime.new(0, 'UTC').to_s
23
+ # => "1970-01-01 00:00:00 +0000"
24
+ EtOrbi::EoTime.new(0, 'Europe/Moscow').to_s
25
+ # => "1970-01-01 03:00:00 +0300"
26
+ ```
27
+
28
+ Helper methods:
29
+ ```ruby
30
+ require 'et-orbi'
31
+
32
+ EtOrbi.get_tzone('Europe/Vilnius')
33
+ # => #<TZInfo::DataTimezone: Europe/Vilnius>
34
+ EtOrbi.local_tzone
35
+ # => #<TZInfo::TimezoneProxy: Asia/Tokyo>
36
+
37
+ EtOrbi.platform_info
38
+ # => "(etz:nil,tnz:\"JST\",tzid:nil,rv:\"2.2.6\",rp:\"x86_64-darwin14\",eov:\"1.0.1\",
39
+ # rorv:nil,astz:nil,debian:nil,centos:nil,osx:\"Asia/Tokyo\")"
40
+ #
41
+ # etz: ENV['TZ']
42
+ # tnz: Time.now.zone
43
+ # tzid: defined?(TZInfo::Data)
44
+ # rv: RUBY_VERSION
45
+ # rp: RUBY_PLATFORM
46
+ # eov: EtOrbi::VERSION
47
+ # rorv: Rails::VERSION::STRING
48
+ # astz: ActiveSupport provided Time.zone
49
+ ```
50
+
51
+ ### Rails?
52
+
53
+ If Rails is present, `Time.zone` is provided and EtOrbi will use it.
54
+
55
+ Rails sets its timezone under `config/application.rb`.
56
+
9
57
 
10
58
  ## Related projects
11
59
 
data/lib/et-orbi.rb CHANGED
@@ -7,270 +7,178 @@ require 'tzinfo'
7
7
 
8
8
  module EtOrbi
9
9
 
10
- VERSION = '1.0.1'
10
+ VERSION = '1.0.2'
11
11
 
12
- class EoTime
12
+ #
13
+ # module methods
13
14
 
14
- #
15
- # class methods
16
-
17
- def self.now(zone=nil)
15
+ def self.now(zone=nil)
18
16
 
19
- EoTime.new(Time.now.to_f, zone)
20
- end
17
+ EoTime.new(Time.now.to_f, zone)
18
+ end
21
19
 
22
- def self.parse(str, opts={})
20
+ def self.parse(str, opts={})
23
21
 
24
- if defined?(::Chronic) && t = ::Chronic.parse(str, opts)
25
- return EoTime.new(t, nil)
26
- end
22
+ if defined?(::Chronic) && t = ::Chronic.parse(str, opts)
23
+ return EoTime.new(t, nil)
24
+ end
27
25
 
28
- #rold = RUBY_VERSION < '1.9.0'
29
- #rold = RUBY_VERSION < '2.0.0'
26
+ #rold = RUBY_VERSION < '1.9.0'
27
+ #rold = RUBY_VERSION < '2.0.0'
30
28
 
31
29
  #p [ '---', str ]
32
- begin
33
- DateTime.parse(str)
34
- rescue
35
- fail ArgumentError, "no time information in #{str.inspect}"
36
- end #if rold
37
- #
38
- # is necessary since Time.parse('xxx') in Ruby < 1.9 yields `now`
30
+ begin
31
+ DateTime.parse(str)
32
+ rescue
33
+ fail ArgumentError, "no time information in #{str.inspect}"
34
+ end #if rold
35
+ #
36
+ # is necessary since Time.parse('xxx') in Ruby < 1.9 yields `now`
39
37
 
40
- zone = izone = get_tzone(list_iso8601_zones(str).last)
38
+ zone = izone = get_tzone(list_iso8601_zones(str).last)
41
39
 
42
- list_olson_zones(str).each { |s| break if zone; zone = get_tzone(s) }
40
+ list_olson_zones(str).each { |s| break if zone; zone = get_tzone(s) }
43
41
 
44
- zone ||= local_tzone
42
+ zone ||= local_tzone
45
43
 
46
- str = str.sub(zone.name, '') unless zone.name.match(/\A[-+]/)
47
- #
48
- # for 'Sun Nov 18 16:01:00 Asia/Singapore 2012',
49
- # although where does rufus-scheduler have it from?
44
+ str = str.sub(zone.name, '') unless zone.name.match(/\A[-+]/)
45
+ #
46
+ # for 'Sun Nov 18 16:01:00 Asia/Singapore 2012',
47
+ # although where does rufus-scheduler have it from?
50
48
 
51
- local = Time.parse(str)
49
+ local = Time.parse(str)
52
50
 
53
- secs =
54
- if izone
55
- local.to_f
56
- else
57
- zone.period_for_local(local).to_utc(local).to_f
58
- end
51
+ secs =
52
+ if izone
53
+ local.to_f
54
+ else
55
+ zone.period_for_local(local).to_utc(local).to_f
56
+ end
59
57
 
60
- EoTime.new(secs, zone)
61
- end
58
+ EoTime.new(secs, zone)
59
+ end
62
60
 
63
- def self.time_to_eo_time(t)
61
+ def self.make_time(o)
62
+
63
+ ot =
64
+ case o
65
+ when Time
66
+ time_to_eo_time(
67
+ o)
68
+ when Date
69
+ time_to_eo_time(
70
+ o.respond_to?(:to_time) ?
71
+ o.to_time :
72
+ Time.parse(o.strftime('%Y-%m-%d %H:%M:%S')))
73
+ when String
74
+ #Rufus::Scheduler.parse_in(o, :no_error => true) || self.parse(o)
75
+ parse(o)
76
+ else
77
+ o
78
+ end
64
79
 
65
- z =
66
- get_tzone(t.zone) ||
67
- (
68
- local_tzone.period_for_local(t).abbreviation.to_s == t.zone &&
69
- local_tzone
70
- ) ||
71
- t.zone
72
-
73
- EoTime.new(t.to_f, z)
74
- end
80
+ ot = EoTime.new(Time.now.to_f + ot, nil) if ot.is_a?(Numeric)
75
81
 
76
- def self.make(o)
82
+ fail ArgumentError.new(
83
+ "cannot turn #{o.inspect} to a EoTime instance"
84
+ ) unless ot.is_a?(EoTime)
77
85
 
78
- ot =
79
- case o
80
- when Time
81
- time_to_eo_time(
82
- o)
83
- when Date
84
- time_to_eo_time(
85
- o.respond_to?(:to_time) ?
86
- o.to_time :
87
- Time.parse(o.strftime('%Y-%m-%d %H:%M:%S')))
88
- when String
89
- #Rufus::Scheduler.parse_in(o, :no_error => true) || self.parse(o)
90
- parse(o)
91
- else
92
- o
93
- end
86
+ ot
87
+ end
94
88
 
95
- ot = EoTime.new(Time.now.to_f + ot, nil) if ot.is_a?(Numeric)
89
+ def self.get_tzone(o)
96
90
 
97
- fail ArgumentError.new(
98
- "cannot turn #{o.inspect} to a EoTime instance"
99
- ) unless ot.is_a?(EoTime)
91
+ #p [ :gtz, o ]
92
+ return nil if o == nil
93
+ return local_tzone if o == :local
94
+ return o if o.is_a?(::TZInfo::Timezone)
95
+ return ::TZInfo::Timezone.get('Zulu') if o == 'Z'
100
96
 
101
- ot
102
- end
97
+ o = to_offset(o) if o.is_a?(Numeric)
103
98
 
104
- def self.to_offset(n)
99
+ return nil unless o.is_a?(String)
105
100
 
106
- i = n.to_i
107
- sn = i < 0 ? '-' : '+'; i = i.abs
108
- hr = i / 3600; mn = i % 3600; sc = i % 60
109
- (sc > 0 ? "%s%02d:%02d:%02d" : "%s%02d:%02d") % [ sn, hr, mn, sc ]
110
- end
111
-
112
- def self.get_tzone(o)
101
+ (@custom_tz_cache ||= {})[o] ||
102
+ get_offset_tzone(o) ||
103
+ (::TZInfo::Timezone.get(o) rescue nil)
104
+ end
113
105
 
114
- #p [ :gtz, o ]
115
- return nil if o == nil
116
- return local_tzone if o == :local
117
- return o if o.is_a?(::TZInfo::Timezone)
118
- return ::TZInfo::Timezone.get('Zulu') if o == 'Z'
106
+ def self.local_tzone
119
107
 
120
- o = to_offset(o) if o.is_a?(Numeric)
108
+ @local_tzone = nil \
109
+ if @local_tzone_loaded_at && (Time.now > @local_tzone_loaded_at + 1800)
110
+ @local_tzone = nil \
111
+ if @local_tzone_tz != ENV['TZ']
121
112
 
122
- return nil unless o.is_a?(String)
113
+ @local_tzone ||=
114
+ begin
115
+ @local_tzone_tz = ENV['TZ']
116
+ @local_tzone_loaded_at = Time.now
117
+ determine_local_tzone
118
+ end
119
+ end
123
120
 
124
- (@custom_tz_cache ||= {})[o] ||
125
- get_offset_tzone(o) ||
126
- (::TZInfo::Timezone.get(o) rescue nil)
127
- end
121
+ def self.platform_info
122
+
123
+ etos = Proc.new { |k, v| "#{k}:#{v.inspect}" }
124
+
125
+ '(' +
126
+ {
127
+ 'etz' => ENV['TZ'],
128
+ 'tnz' => Time.now.zone,
129
+ 'tzid' => defined?(TZInfo::Data),
130
+ 'rv' => RUBY_VERSION,
131
+ 'rp' => RUBY_PLATFORM,
132
+ 'eov' => EtOrbi::VERSION,
133
+ 'rorv' => (Rails::VERSION::STRING rescue nil),
134
+ 'astz' => Time.respond_to?(:zone) ? Time.zone.name : nil,
135
+ # Active Support Time.zone
136
+ }.collect(&etos).join(',') + ',' +
137
+ gather_tzs.collect(&etos).join(',') +
138
+ ')'
139
+ end
128
140
 
129
- def self.get_offset_tzone(str)
141
+ class << self
130
142
 
131
- # custom timezones, no DST, just an offset, like "+08:00" or "-01:30"
143
+ alias make make_time
144
+ end
132
145
 
133
- m = str.match(/\A([+-][0-1][0-9]):?([0-5][0-9])?\z/)
134
- return nil unless m
146
+ #
147
+ # our EoTime class (which quacks like a ::Time)
135
148
 
136
- hr = m[1].to_i
137
- mn = m[2].to_i
149
+ class EoTime
138
150
 
139
- hr = nil if hr.abs > 11
140
- hr = nil if mn > 59
141
- mn = -mn if hr && hr < 0
151
+ #
152
+ # class methods
142
153
 
143
- return (
144
- @custom_tz_cache[str] =
145
- begin
146
- tzi = TZInfo::TransitionDataTimezoneInfo.new(str)
147
- tzi.offset(str, hr * 3600 + mn * 60, 0, str)
148
- tzi.create_timezone
149
- end
150
- ) if hr
154
+ def self.now(zone=nil)
151
155
 
152
- nil
156
+ EtOrbi.now(zone)
153
157
  end
154
158
 
155
- def self.local_tzone
156
-
157
- @local_tzone = nil \
158
- if @local_tzone_loaded_at && (Time.now > @local_tzone_loaded_at + 1800)
159
- @local_tzone = nil \
160
- if @local_tzone_tz != ENV['TZ']
159
+ def self.parse(str, opts={})
161
160
 
162
- @local_tzone ||=
163
- begin
164
- @local_tzone_tz = ENV['TZ']
165
- @local_tzone_loaded_at = Time.now
166
- determine_local_tzone
167
- end
161
+ EtOrbi.parse(str, opts)
168
162
  end
169
163
 
170
- def self.determine_local_tzone
171
-
172
- etz = ENV['TZ']
173
-
174
- tz = ::TZInfo::Timezone.get(etz) rescue nil
175
- return tz if tz
176
-
177
- tz = Time.zone.tzinfo \
178
- if Time.respond_to?(:zone) && Time.zone.respond_to?(:tzinfo)
179
- return tz if tz
180
-
181
- tzs = determine_local_tzones
164
+ def self.get_tzone(o)
182
165
 
183
- (etz && tzs.find { |z| z.name == etz }) || tzs.first
166
+ EtOrbi.get_tzone(o)
184
167
  end
185
168
 
186
- def self.determine_local_tzones
187
-
188
- tabbs = (-6..5)
189
- .collect { |i| (Time.now + i * 30 * 24 * 3600).zone }
190
- .uniq
191
- .sort
192
-
193
- t = Time.now
194
- tu = t.dup.utc # /!\ dup is necessary, #utc modifies its target
195
-
196
- twin = Time.utc(t.year, 1, 1) # winter
197
- tsum = Time.utc(t.year, 7, 1) # summer
198
-
199
- ::TZInfo::Timezone.all.select do |tz|
200
-
201
- pabbs =
202
- [
203
- tz.period_for_utc(twin).abbreviation.to_s,
204
- tz.period_for_utc(tsum).abbreviation.to_s
205
- ].uniq.sort
206
-
207
- pabbs == tabbs
208
- end
209
- end
169
+ def self.local_tzone
210
170
 
211
- # https://en.wikipedia.org/wiki/ISO_8601
212
- # Postel's law applies
213
- #
214
- def self.list_iso8601_zones(s)
215
-
216
- s.scan(
217
- %r{
218
- (?<=:\d\d)
219
- \s*
220
- (?:
221
- [-+]
222
- (?:[0-1][0-9]|2[0-4])
223
- (?:(?::)?(?:[0-5][0-9]|60))?
224
- (?![-+])
225
- |
226
- Z
227
- )
228
- }x
229
- ).collect(&:strip)
171
+ EtOrbi.local_tzone
230
172
  end
231
173
 
232
- def self.list_olson_zones(s)
174
+ def self.platform_info
233
175
 
234
- s.scan(
235
- %r{
236
- (?<=\s|\A)
237
- (?:[A-Za-z][A-Za-z0-9+_-]+)
238
- (?:\/(?:[A-Za-z][A-Za-z0-9+_-]+)){0,2}
239
- }x)
176
+ EtOrbi.platform_info
240
177
  end
241
178
 
242
- #def in_zone(&block)
243
- #
244
- # current_timezone = ENV['TZ']
245
- # ENV['TZ'] = @zone
246
- #
247
- # block.call
248
- #
249
- #ensure
250
- #
251
- # ENV['TZ'] = current_timezone
252
- #end
253
- #
254
- # kept around as a (thread-unsafe) relic
255
-
256
- def self.platform_info
179
+ def self.make(o)
257
180
 
258
- etos = Proc.new { |k, v| "#{k}:#{v.inspect}" }
259
-
260
- '(' +
261
- {
262
- 'etz' => ENV['TZ'],
263
- 'tnz' => Time.now.zone,
264
- 'tzid' => defined?(TZInfo::Data),
265
- 'rv' => RUBY_VERSION,
266
- 'rp' => RUBY_PLATFORM,
267
- 'eov' => EtOrbi::VERSION,
268
- 'rorv' => (Rails::VERSION::STRING rescue nil),
269
- 'astz' => Time.respond_to?(:zone) ? Time.zone.name : nil,
270
- # Active Support Time.zone
271
- }.collect(&etos).join(',') + ',' +
272
- gather_tzs.collect(&etos).join(',') +
273
- ')'
181
+ EtOrbi.make_time(o)
274
182
  end
275
183
 
276
184
  #
@@ -286,7 +194,8 @@ module EtOrbi
286
194
 
287
195
  fail ArgumentError.new(
288
196
  "cannot determine timezone from #{zone.inspect}" +
289
- "\n#{self.class.platform_info}" +
197
+ "\n#{render_nozone_time(s)}" +
198
+ "\n#{self.class.platform_info.sub(',debian:', ",\ndebian:")}" +
290
199
  "\nTry setting `ENV['TZ'] = 'Continent/City'` in your script " +
291
200
  "(see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)" +
292
201
  (defined?(TZInfo::Data) ? '' : "\nand adding gem 'tzinfo-data'")
@@ -446,8 +355,20 @@ module EtOrbi
446
355
  strftime("%H:%M:%S.#{'%06d' % usec}")
447
356
  end
448
357
 
449
- #
450
- # protected
358
+ protected
359
+
360
+ def render_nozone_time(seconds)
361
+
362
+ t =
363
+ Time.utc(0) + seconds
364
+ ts =
365
+ t.strftime('%Y-%m-%d %H:%M:%S') +
366
+ ".#{(seconds % 1).to_s.split('.').last}"
367
+ z =
368
+ EtOrbi.local_tzone.period_for_local(t).abbreviation.to_s
369
+
370
+ "(secs:#{seconds},utc~:#{ts.inspect},zo~:#{z.inspect})"
371
+ end
451
372
 
452
373
  def strfz(code)
453
374
 
@@ -498,48 +419,184 @@ module EtOrbi
498
419
 
499
420
  o.to_f
500
421
  end
422
+ end
501
423
 
502
- #
503
- # system tz determination
424
+ #
425
+ # not so public module methods
426
+
427
+ def self.time_to_eo_time(t)
428
+
429
+ z =
430
+ get_tzone(t.zone) ||
431
+ (
432
+ local_tzone.period_for_local(t).abbreviation.to_s == t.zone &&
433
+ local_tzone
434
+ ) ||
435
+ t.zone
436
+
437
+ EoTime.new(t.to_f, z)
438
+ end
504
439
 
505
- def self.debian_tz
440
+ def self.to_offset(n)
506
441
 
507
- path = '/etc/timezone'
442
+ i = n.to_i
443
+ sn = i < 0 ? '-' : '+'; i = i.abs
444
+ hr = i / 3600; mn = i % 3600; sc = i % 60
445
+ (sc > 0 ? "%s%02d:%02d:%02d" : "%s%02d:%02d") % [ sn, hr, mn, sc ]
446
+ end
447
+
448
+ def self.get_offset_tzone(str)
508
449
 
509
- File.exist?(path) ? File.read(path).strip : nil
510
- rescue; nil; end
450
+ # custom timezones, no DST, just an offset, like "+08:00" or "-01:30"
511
451
 
512
- def self.centos_tz
452
+ m = str.match(/\A([+-][0-1][0-9]):?([0-5][0-9])?\z/)
453
+ return nil unless m
513
454
 
514
- path = '/etc/sysconfig/clock'
455
+ hr = m[1].to_i
456
+ mn = m[2].to_i
515
457
 
516
- File.open(path, 'rb') do |f|
517
- until f.eof?
518
- if m = f.readline.match(/ZONE="([^"]+)"/); return m[1]; end
458
+ hr = nil if hr.abs > 11
459
+ hr = nil if mn > 59
460
+ mn = -mn if hr && hr < 0
461
+
462
+ return (
463
+ @custom_tz_cache[str] =
464
+ begin
465
+ tzi = TZInfo::TransitionDataTimezoneInfo.new(str)
466
+ tzi.offset(str, hr * 3600 + mn * 60, 0, str)
467
+ tzi.create_timezone
519
468
  end
520
- end if File.exist?(path)
469
+ ) if hr
521
470
 
522
- nil
523
- rescue; nil; end
471
+ nil
472
+ end
524
473
 
525
- def self.osx_tz
474
+ def self.determine_local_tzone
526
475
 
527
- path = '/etc/localtime'
476
+ etz = ENV['TZ']
528
477
 
529
- File.symlink?(path) ?
530
- File.readlink(path).split('/')[4..-1].join('/') :
531
- nil
532
- rescue; nil; end
478
+ tz = ::TZInfo::Timezone.get(etz) rescue nil
479
+ return tz if tz
533
480
 
534
- # def self.find_tz
535
- #
536
- # debian_tz || centos_tz || osx_tz
537
- # end
481
+ tz = Time.zone.tzinfo \
482
+ if Time.respond_to?(:zone) && Time.zone.respond_to?(:tzinfo)
483
+ return tz if tz
484
+
485
+ tzs = determine_local_tzones
538
486
 
539
- def self.gather_tzs
487
+ (etz && tzs.find { |z| z.name == etz }) || tzs.first
488
+ end
489
+
490
+ def self.determine_local_tzones
491
+
492
+ tabbs = (-6..5)
493
+ .collect { |i| (Time.now + i * 30 * 24 * 3600).zone }
494
+ .uniq
495
+ .sort
496
+
497
+ t = Time.now
498
+ tu = t.dup.utc # /!\ dup is necessary, #utc modifies its target
540
499
 
541
- { :debian => debian_tz, :centos => centos_tz, :osx => osx_tz }
500
+ twin = Time.utc(t.year, 1, 1) # winter
501
+ tsum = Time.utc(t.year, 7, 1) # summer
502
+
503
+ ::TZInfo::Timezone.all.select do |tz|
504
+
505
+ pabbs =
506
+ [
507
+ tz.period_for_utc(twin).abbreviation.to_s,
508
+ tz.period_for_utc(tsum).abbreviation.to_s
509
+ ].uniq.sort
510
+
511
+ pabbs == tabbs
542
512
  end
543
513
  end
514
+
515
+ # https://en.wikipedia.org/wiki/ISO_8601
516
+ # Postel's law applies
517
+ #
518
+ def self.list_iso8601_zones(s)
519
+
520
+ s.scan(
521
+ %r{
522
+ (?<=:\d\d)
523
+ \s*
524
+ (?:
525
+ [-+]
526
+ (?:[0-1][0-9]|2[0-4])
527
+ (?:(?::)?(?:[0-5][0-9]|60))?
528
+ (?![-+])
529
+ |
530
+ Z
531
+ )
532
+ }x
533
+ ).collect(&:strip)
534
+ end
535
+
536
+ def self.list_olson_zones(s)
537
+
538
+ s.scan(
539
+ %r{
540
+ (?<=\s|\A)
541
+ (?:[A-Za-z][A-Za-z0-9+_-]+)
542
+ (?:\/(?:[A-Za-z][A-Za-z0-9+_-]+)){0,2}
543
+ }x)
544
+ end
545
+
546
+ #def in_zone(&block)
547
+ #
548
+ # current_timezone = ENV['TZ']
549
+ # ENV['TZ'] = @zone
550
+ #
551
+ # block.call
552
+ #
553
+ #ensure
554
+ #
555
+ # ENV['TZ'] = current_timezone
556
+ #end
557
+ #
558
+ # kept around as a (thread-unsafe) relic
559
+
560
+ #
561
+ # system tz determination
562
+
563
+ def self.debian_tz
564
+
565
+ path = '/etc/timezone'
566
+
567
+ File.exist?(path) ? File.read(path).strip : nil
568
+ rescue; nil; end
569
+
570
+ def self.centos_tz
571
+
572
+ path = '/etc/sysconfig/clock'
573
+
574
+ File.open(path, 'rb') do |f|
575
+ until f.eof?
576
+ if m = f.readline.match(/ZONE="([^"]+)"/); return m[1]; end
577
+ end
578
+ end if File.exist?(path)
579
+
580
+ nil
581
+ rescue; nil; end
582
+
583
+ def self.osx_tz
584
+
585
+ path = '/etc/localtime'
586
+
587
+ File.symlink?(path) ?
588
+ File.readlink(path).split('/')[4..-1].join('/') :
589
+ nil
590
+ rescue; nil; end
591
+
592
+ # def self.find_tz
593
+ #
594
+ # debian_tz || centos_tz || osx_tz
595
+ # end
596
+
597
+ def self.gather_tzs
598
+
599
+ { :debian => debian_tz, :centos => centos_tz, :osx => osx_tz }
600
+ end
544
601
  end
545
602