et-orbi 1.0.1 → 1.0.2

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.
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