rufus-scheduler 3.2.2 → 3.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.txt +8 -0
- data/CREDITS.txt +4 -0
- data/README.md +9 -1
- data/fail18.txt +12 -0
- data/lib/rufus/scheduler.rb +9 -8
- data/lib/rufus/scheduler/cronline.rb +71 -71
- data/lib/rufus/scheduler/job_array.rb +7 -3
- data/lib/rufus/scheduler/jobs.rb +50 -34
- data/lib/rufus/scheduler/util.rb +19 -20
- data/lib/rufus/scheduler/zotime.rb +347 -65
- data/pics.txt +15 -0
- data/rufus-scheduler.gemspec +1 -2
- metadata +28 -27
- data/lib/rufus/scheduler/zones.rb +0 -175
data/lib/rufus/scheduler/jobs.rb
CHANGED
@@ -86,7 +86,7 @@ module Rufus
|
|
86
86
|
nil
|
87
87
|
end
|
88
88
|
|
89
|
-
@scheduled_at =
|
89
|
+
@scheduled_at = Rufus::Scheduler::ZoTime.now
|
90
90
|
@unscheduled_at = nil
|
91
91
|
@last_time = nil
|
92
92
|
|
@@ -124,27 +124,22 @@ module Rufus
|
|
124
124
|
@previous_time = @next_time
|
125
125
|
set_next_time(time)
|
126
126
|
|
127
|
-
|
128
|
-
|
129
|
-
running?
|
130
|
-
)
|
131
|
-
return if (
|
132
|
-
callback(:confirm_lock, time) &&
|
133
|
-
callback(:on_pre_trigger, time)
|
134
|
-
) == false
|
127
|
+
do_trigger(time)
|
128
|
+
end
|
135
129
|
|
136
|
-
|
130
|
+
# Trigger the job right now, off of its schedule.
|
131
|
+
#
|
132
|
+
# Done in collaboration with Piavka in
|
133
|
+
# https://github.com/jmettraux/rufus-scheduler/issues/214
|
134
|
+
#
|
135
|
+
def trigger_off_schedule(time=Rufus::Scheduler::ZoTime.now)
|
137
136
|
|
138
|
-
|
139
|
-
do_trigger(time)
|
140
|
-
else
|
141
|
-
do_trigger_in_thread(time)
|
142
|
-
end
|
137
|
+
do_trigger(time)
|
143
138
|
end
|
144
139
|
|
145
140
|
def unschedule
|
146
141
|
|
147
|
-
@unscheduled_at =
|
142
|
+
@unscheduled_at = Rufus::Scheduler::ZoTime.now
|
148
143
|
end
|
149
144
|
|
150
145
|
def threads
|
@@ -204,7 +199,7 @@ module Rufus
|
|
204
199
|
#
|
205
200
|
def call(do_rescue=false)
|
206
201
|
|
207
|
-
do_call(
|
202
|
+
do_call(Rufus::Scheduler::ZoTime.now, do_rescue)
|
208
203
|
end
|
209
204
|
|
210
205
|
protected
|
@@ -251,7 +246,27 @@ module Rufus
|
|
251
246
|
|
252
247
|
def do_trigger(time)
|
253
248
|
|
254
|
-
|
249
|
+
return if (
|
250
|
+
opts[:overlap] == false &&
|
251
|
+
running?
|
252
|
+
)
|
253
|
+
return if (
|
254
|
+
callback(:confirm_lock, time) &&
|
255
|
+
callback(:on_pre_trigger, time)
|
256
|
+
) == false
|
257
|
+
|
258
|
+
@count += 1
|
259
|
+
|
260
|
+
if opts[:blocking]
|
261
|
+
trigger_now(time)
|
262
|
+
else
|
263
|
+
trigger_queue(time)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
def trigger_now(time)
|
268
|
+
|
269
|
+
t = Rufus::Scheduler::ZoTime.now
|
255
270
|
# if there are mutexes, t might be really bigger than time
|
256
271
|
|
257
272
|
Thread.current[:rufus_scheduler_job] = self
|
@@ -265,7 +280,7 @@ module Rufus
|
|
265
280
|
ensure
|
266
281
|
|
267
282
|
@last_work_time =
|
268
|
-
|
283
|
+
Rufus::Scheduler::ZoTime.now - Thread.current[:rufus_scheduler_time]
|
269
284
|
@mean_work_time =
|
270
285
|
((@count - 1) * @mean_work_time + @last_work_time) / @count
|
271
286
|
|
@@ -302,7 +317,7 @@ module Rufus
|
|
302
317
|
begin
|
303
318
|
|
304
319
|
(job.opts[:mutex] || []).reduce(
|
305
|
-
lambda { job.
|
320
|
+
lambda { job.trigger_now(time) }
|
306
321
|
) do |b, m|
|
307
322
|
lambda { mutex(m).synchronize { b.call } }
|
308
323
|
end.call
|
@@ -321,9 +336,11 @@ module Rufus
|
|
321
336
|
# but since it has to be done as quickly as possible.
|
322
337
|
# So, whoever is running first (scheduler thread vs job thread)
|
323
338
|
# sets this information
|
339
|
+
|
340
|
+
thread
|
324
341
|
end
|
325
342
|
|
326
|
-
def
|
343
|
+
def trigger_queue(time)
|
327
344
|
|
328
345
|
threads = @scheduler.work_threads
|
329
346
|
|
@@ -423,12 +440,12 @@ module Rufus
|
|
423
440
|
|
424
441
|
return (@first_at = nil) if first == nil
|
425
442
|
|
426
|
-
n0 =
|
443
|
+
n0 = Rufus::Scheduler::ZoTime.now
|
427
444
|
n1 = n0 + 0.003
|
428
445
|
|
429
446
|
first = n0 if first == :now || first == :immediately || first == 0
|
430
447
|
|
431
|
-
@first_at =
|
448
|
+
@first_at = ZoTime.make(first)
|
432
449
|
@first_at = n1 if @first_at >= n0 && @first_at < n1
|
433
450
|
|
434
451
|
fail ArgumentError.new(
|
@@ -441,12 +458,13 @@ module Rufus
|
|
441
458
|
|
442
459
|
def last_at=(last)
|
443
460
|
|
444
|
-
|
461
|
+
#@last_at = last ? Rufus::Scheduler.parse_to_time(last) : nil
|
462
|
+
@last_at = last ? ZoTime.make(last) : nil
|
445
463
|
|
446
464
|
fail ArgumentError.new(
|
447
465
|
"cannot set last[_at|_in] in the past: " +
|
448
466
|
"#{last.inspect} -> #{@last_at.inspect}"
|
449
|
-
) if last && @last_at <
|
467
|
+
) if last && @last_at < Rufus::Scheduler::ZoTime.now
|
450
468
|
|
451
469
|
@last_at
|
452
470
|
end
|
@@ -458,7 +476,7 @@ module Rufus
|
|
458
476
|
return (@next_time = nil) if @times && @times < 1
|
459
477
|
return (@next_time = nil) if @last_at && time >= @last_at
|
460
478
|
#
|
461
|
-
#
|
479
|
+
# It keeps jobs one step too much in @jobs, but it's OK
|
462
480
|
|
463
481
|
super
|
464
482
|
|
@@ -467,7 +485,7 @@ module Rufus
|
|
467
485
|
|
468
486
|
def pause
|
469
487
|
|
470
|
-
@paused_at =
|
488
|
+
@paused_at = Rufus::Scheduler::ZoTime.now
|
471
489
|
end
|
472
490
|
|
473
491
|
def resume
|
@@ -518,9 +536,7 @@ module Rufus
|
|
518
536
|
|
519
537
|
def first_at=(first)
|
520
538
|
|
521
|
-
super
|
522
|
-
|
523
|
-
@next_time = @first_at
|
539
|
+
@next_time = super
|
524
540
|
end
|
525
541
|
end
|
526
542
|
|
@@ -548,10 +564,10 @@ module Rufus
|
|
548
564
|
|
549
565
|
return if is_post
|
550
566
|
|
551
|
-
n =
|
567
|
+
n = Rufus::Scheduler::ZoTime.now
|
552
568
|
|
553
569
|
@next_time =
|
554
|
-
if @first_at == nil || @first_at < n
|
570
|
+
if @first_at == nil || @first_at < (n - @scheduler.frequency)
|
555
571
|
nt = (@next_time || trigger_time || n) + @frequency
|
556
572
|
nt > n ? nt : (trigger_time || n) + @frequency
|
557
573
|
else
|
@@ -589,10 +605,10 @@ module Rufus
|
|
589
605
|
|
590
606
|
@next_time =
|
591
607
|
if is_post
|
592
|
-
|
608
|
+
Rufus::Scheduler::ZoTime.now + @interval
|
593
609
|
elsif trigger_time.nil?
|
594
610
|
if @first_at == nil || @first_at < Time.now
|
595
|
-
|
611
|
+
Rufus::Scheduler::ZoTime.now + @interval
|
596
612
|
else
|
597
613
|
@first_at
|
598
614
|
end
|
data/lib/rufus/scheduler/util.rb
CHANGED
@@ -38,27 +38,27 @@ module Rufus
|
|
38
38
|
parse_cron(o, opts) ||
|
39
39
|
parse_in(o, opts) || # covers 'every' schedule strings
|
40
40
|
parse_at(o, opts) ||
|
41
|
-
fail(ArgumentError.new("couldn't parse
|
41
|
+
fail(ArgumentError.new("couldn't parse #{o.inspect} (#{o.class})"))
|
42
42
|
end
|
43
43
|
|
44
|
-
def self.
|
45
|
-
|
46
|
-
o.is_a?(String) ? parse_duration(o, opts) : o
|
47
|
-
end
|
48
|
-
|
49
|
-
def self.parse_at(o, opts={})
|
44
|
+
def self.parse_cron(o, opts)
|
50
45
|
|
51
|
-
|
46
|
+
CronLine.new(o)
|
52
47
|
|
53
|
-
rescue
|
48
|
+
rescue ArgumentError => ae
|
54
49
|
|
55
50
|
return nil if opts[:no_error]
|
56
|
-
fail
|
51
|
+
fail ae
|
57
52
|
end
|
58
53
|
|
59
|
-
def self.
|
54
|
+
def self.parse_in(o, opts={})
|
60
55
|
|
61
|
-
|
56
|
+
#o.is_a?(String) ? parse_duration(o, opts) : o
|
57
|
+
|
58
|
+
return parse_duration(o, opts) if o.is_a?(String)
|
59
|
+
return o if o.is_a?(Numeric)
|
60
|
+
|
61
|
+
fail ArgumentError.new("couldn't parse time point in #{o.inspect}")
|
62
62
|
|
63
63
|
rescue ArgumentError => ae
|
64
64
|
|
@@ -66,17 +66,16 @@ module Rufus
|
|
66
66
|
fail ae
|
67
67
|
end
|
68
68
|
|
69
|
-
def self.
|
69
|
+
def self.parse_at(o, opts={})
|
70
70
|
|
71
|
-
|
72
|
-
|
73
|
-
|
71
|
+
return o if o.is_a?(Rufus::Scheduler::ZoTime)
|
72
|
+
return Rufus::Scheduler::ZoTime.make(o) if o.is_a?(Time)
|
73
|
+
Rufus::Scheduler::ZoTime.parse(o, opts)
|
74
74
|
|
75
|
-
|
76
|
-
"cannot turn #{o.inspect} to a point in time, doesn't make sense"
|
77
|
-
) unless t.is_a?(Time)
|
75
|
+
rescue StandardError => se
|
78
76
|
|
79
|
-
|
77
|
+
return nil if opts[:no_error]
|
78
|
+
fail se
|
80
79
|
end
|
81
80
|
|
82
81
|
DURATIONS2M = [
|
@@ -22,8 +22,6 @@
|
|
22
22
|
# Made in Japan.
|
23
23
|
#++
|
24
24
|
|
25
|
-
require 'rufus/scheduler/zones'
|
26
|
-
|
27
25
|
|
28
26
|
class Rufus::Scheduler
|
29
27
|
|
@@ -32,52 +30,72 @@ class Rufus::Scheduler
|
|
32
30
|
#
|
33
31
|
class ZoTime
|
34
32
|
|
35
|
-
|
36
|
-
|
33
|
+
attr_reader :seconds
|
34
|
+
attr_reader :zone
|
37
35
|
|
38
36
|
def initialize(s, zone)
|
39
37
|
|
40
38
|
@seconds = s.to_f
|
41
|
-
@zone = zone
|
42
|
-
end
|
39
|
+
@zone = self.class.get_tzone(zone || :current)
|
43
40
|
|
44
|
-
|
41
|
+
fail ArgumentError.new(
|
42
|
+
"cannot determine timezone from #{zone.inspect}"
|
43
|
+
) unless @zone
|
45
44
|
|
46
|
-
|
45
|
+
@time = nil # cache for #to_time result
|
46
|
+
end
|
47
47
|
|
48
|
-
|
48
|
+
def seconds=(f)
|
49
49
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
# # ambiguous TZ (getting out of DST)
|
54
|
-
#else
|
55
|
-
# t.hour # force t to compute itself
|
56
|
-
#end
|
57
|
-
#
|
58
|
-
# jump out of DST as soon as possible, jumps 1h as seen from UTC
|
50
|
+
@time = nil
|
51
|
+
@seconds = f
|
52
|
+
end
|
59
53
|
|
60
|
-
|
61
|
-
#
|
62
|
-
# stay in DST as long as possible, no jump seen from UTC
|
54
|
+
def zone=(z)
|
63
55
|
|
64
|
-
|
65
|
-
|
56
|
+
@time = nil
|
57
|
+
@zone = self.class.get_tzone(zone || :current)
|
66
58
|
end
|
67
59
|
|
68
60
|
def utc
|
69
61
|
|
70
|
-
|
62
|
+
Time.utc(1970, 1, 1) + @seconds
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns a Ruby Time instance.
|
66
|
+
#
|
67
|
+
# Warning: the timezone of that Time instance will be UTC.
|
68
|
+
#
|
69
|
+
def to_time
|
70
|
+
|
71
|
+
@time ||= begin; u = utc; @zone.period_for_utc(u).to_local(u); end
|
72
|
+
end
|
73
|
+
|
74
|
+
%w[
|
75
|
+
year month day wday hour min sec usec asctime
|
76
|
+
].each do |m|
|
77
|
+
define_method(m) { to_time.send(m) }
|
71
78
|
end
|
79
|
+
def iso8601(fraction_digits=0); to_time.iso8601(fraction_digits); end
|
72
80
|
|
73
|
-
def
|
81
|
+
def ==(o)
|
74
82
|
|
75
|
-
@seconds
|
83
|
+
o.is_a?(ZoTime) && o.seconds == @seconds && o.zone == @zone
|
76
84
|
end
|
85
|
+
#alias eq? == # FIXME see Object#== (ri)
|
86
|
+
|
87
|
+
def >(o); @seconds > _to_f(o); end
|
88
|
+
def >=(o); @seconds >= _to_f(o); end
|
89
|
+
def <(o); @seconds < _to_f(o); end
|
90
|
+
def <=(o); @seconds <= _to_f(o); end
|
91
|
+
def <=>(o); @seconds <=> _to_f(o); end
|
92
|
+
|
93
|
+
alias getutc utc
|
94
|
+
alias getgm utc
|
77
95
|
|
78
|
-
def
|
96
|
+
def to_i
|
79
97
|
|
80
|
-
@seconds
|
98
|
+
@seconds.to_i
|
81
99
|
end
|
82
100
|
|
83
101
|
def to_f
|
@@ -85,82 +103,346 @@ class Rufus::Scheduler
|
|
85
103
|
@seconds
|
86
104
|
end
|
87
105
|
|
88
|
-
|
106
|
+
def is_dst?
|
107
|
+
|
108
|
+
@zone.period_for_utc(utc).std_offset != 0
|
109
|
+
end
|
110
|
+
alias isdst is_dst?
|
111
|
+
|
112
|
+
def utc_offset
|
113
|
+
|
114
|
+
#@zone.period_for_utc(utc).utc_offset
|
115
|
+
#@zone.period_for_utc(utc).utc_total_offset
|
116
|
+
#@zone.period_for_utc(utc).std_offset
|
117
|
+
@zone.period_for_utc(utc).utc_offset
|
118
|
+
end
|
119
|
+
|
120
|
+
def strftime(format)
|
121
|
+
|
122
|
+
format = format.gsub(/%(\/?Z|:{0,2}z)/) { |f| strfz(f) }
|
123
|
+
|
124
|
+
to_time.strftime(format)
|
125
|
+
end
|
126
|
+
|
127
|
+
def add(t); @time = nil; @seconds += t.to_f; end
|
128
|
+
def substract(t); @time = nil; @seconds -= t.to_f; end
|
129
|
+
|
130
|
+
def +(t); inc(t, 1); end
|
131
|
+
def -(t); inc(t, -1); end
|
132
|
+
|
133
|
+
WEEK_S = 7 * 24 * 3600
|
134
|
+
|
135
|
+
def monthdays
|
136
|
+
|
137
|
+
date = to_time
|
138
|
+
|
139
|
+
pos = 1
|
140
|
+
d = self.dup
|
141
|
+
|
142
|
+
loop do
|
143
|
+
d.add(-WEEK_S)
|
144
|
+
break if d.month != date.month
|
145
|
+
pos = pos + 1
|
146
|
+
end
|
147
|
+
|
148
|
+
neg = -1
|
149
|
+
d = self.dup
|
150
|
+
|
151
|
+
loop do
|
152
|
+
d.add(WEEK_S)
|
153
|
+
break if d.month != date.month
|
154
|
+
neg = neg - 1
|
155
|
+
end
|
156
|
+
|
157
|
+
[ "#{date.wday}##{pos}", "#{date.wday}##{neg}" ]
|
158
|
+
end
|
159
|
+
|
160
|
+
def to_s
|
161
|
+
|
162
|
+
strftime('%Y-%m-%d %H:%M:%S %z')
|
163
|
+
end
|
164
|
+
|
165
|
+
def to_debug_s
|
166
|
+
|
167
|
+
uo = self.utc_offset
|
168
|
+
uos = uo < 0 ? '-' : '+'
|
169
|
+
uo = uo.abs
|
170
|
+
uoh, uom = [ uo / 3600, uo % 3600 ]
|
171
|
+
|
172
|
+
[
|
173
|
+
'zt',
|
174
|
+
self.strftime('%Y-%m-%d %H:%M:%S'),
|
175
|
+
"%s%02d:%02d" % [ uos, uoh, uom ],
|
176
|
+
"dst:#{self.isdst}"
|
177
|
+
].join(' ')
|
178
|
+
end
|
179
|
+
|
180
|
+
# Debug current time by showing local time / delta / utc time
|
181
|
+
# for example: "0120-7(0820)"
|
182
|
+
#
|
183
|
+
def to_utc_comparison_s
|
184
|
+
|
185
|
+
per = @zone.period_for_utc(utc)
|
186
|
+
off = per.utc_total_offset
|
187
|
+
|
188
|
+
off = off / 3600
|
189
|
+
off = off >= 0 ? "+#{off}" : off.to_s
|
190
|
+
|
191
|
+
strftime('%H%M') + off + utc.strftime('(%H%M)')
|
192
|
+
end
|
193
|
+
|
194
|
+
def self.now(zone=nil)
|
195
|
+
|
196
|
+
ZoTime.new(Time.now.to_f, zone)
|
197
|
+
end
|
198
|
+
|
199
|
+
# https://en.wikipedia.org/wiki/ISO_8601
|
200
|
+
# Postel's law applies
|
201
|
+
#
|
202
|
+
def self.extract_iso8601_zone(s)
|
203
|
+
|
204
|
+
m = s.match(
|
205
|
+
/[0-2]\d(?::?[0-6]\d(?::?[0-6]\d))?\s*([+-]\d\d(?::?\d\d)?)\s*\z/)
|
206
|
+
return nil unless m
|
207
|
+
|
208
|
+
zs = m[1].split(':')
|
209
|
+
zs << '00' if zs.length < 2
|
89
210
|
|
90
|
-
|
211
|
+
zh = zs[0].to_i.abs
|
91
212
|
|
92
|
-
|
213
|
+
return nil if zh > 24
|
214
|
+
return nil if zh == 24 && zs[1].to_i != 0
|
215
|
+
|
216
|
+
zs.join(':')
|
93
217
|
end
|
94
218
|
|
95
219
|
def self.parse(str, opts={})
|
96
220
|
|
97
221
|
if defined?(::Chronic) && t = ::Chronic.parse(str, opts)
|
98
|
-
return ZoTime.new(t,
|
222
|
+
return ZoTime.new(t, nil)
|
99
223
|
end
|
100
224
|
|
225
|
+
#rold = RUBY_VERSION < '1.9.0'
|
226
|
+
#rold = RUBY_VERSION < '2.0.0'
|
227
|
+
|
101
228
|
begin
|
102
229
|
DateTime.parse(str)
|
103
230
|
rescue
|
104
|
-
fail ArgumentError, "no time information in #{
|
105
|
-
end if
|
231
|
+
fail ArgumentError, "no time information in #{str.inspect}"
|
232
|
+
end #if rold
|
233
|
+
#
|
234
|
+
# is necessary since Time.parse('xxx') in Ruby < 1.9 yields `now`
|
106
235
|
|
107
236
|
zone = nil
|
108
237
|
|
109
238
|
s =
|
110
|
-
str.gsub(/\S+/)
|
111
|
-
if
|
112
|
-
zone ||=
|
239
|
+
str.gsub(/\S+/) do |w|
|
240
|
+
if z = get_tzone(w)
|
241
|
+
zone ||= z
|
113
242
|
''
|
114
243
|
else
|
115
|
-
|
244
|
+
w
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
local = Time.parse(s)
|
249
|
+
izone = extract_iso8601_zone(s)
|
250
|
+
|
251
|
+
zone ||=
|
252
|
+
if s.match(/\dZ\b/)
|
253
|
+
get_tzone('Zulu')
|
254
|
+
#elsif rold && izone
|
255
|
+
elsif izone
|
256
|
+
get_tzone(izone)
|
257
|
+
elsif local.zone.nil? && izone
|
258
|
+
get_tzone(local.strftime('%:z'))
|
259
|
+
else
|
260
|
+
get_tzone(:local)
|
261
|
+
end
|
262
|
+
|
263
|
+
secs =
|
264
|
+
#if rold && izone
|
265
|
+
if izone
|
266
|
+
local.to_f
|
267
|
+
else
|
268
|
+
zone.period_for_local(local).to_utc(local).to_f
|
269
|
+
end
|
270
|
+
|
271
|
+
ZoTime.new(secs, zone)
|
272
|
+
end
|
273
|
+
|
274
|
+
def self.get_tzone(str)
|
275
|
+
|
276
|
+
return str if str.is_a?(::TZInfo::Timezone)
|
277
|
+
|
278
|
+
# discard quickly when it's certainly not a timezone
|
279
|
+
|
280
|
+
return nil if str == nil
|
281
|
+
return nil if str == '*'
|
282
|
+
|
283
|
+
# ok, it's a timezone then
|
284
|
+
|
285
|
+
str = Time.now.zone if str == :current || str == :local
|
286
|
+
|
287
|
+
# utc_offset
|
288
|
+
|
289
|
+
if str.is_a?(Numeric)
|
290
|
+
i = str.to_i
|
291
|
+
sn = i < 0 ? '-' : '+'; i = i.abs
|
292
|
+
hr = i / 3600; mn = i % 3600; sc = i % 60
|
293
|
+
str = (sc > 0 ? "%s%02d:%02d:%02d" : "%s%02d:%02d") % [ sn, hr, mn, sc ]
|
294
|
+
end
|
295
|
+
|
296
|
+
return nil if str.index('#')
|
297
|
+
# counters "sun#2", etc... On OSX would go all the way to true
|
298
|
+
|
299
|
+
# vanilla time zones
|
300
|
+
|
301
|
+
z = (::TZInfo::Timezone.get(str) rescue nil)
|
302
|
+
return z if z
|
303
|
+
|
304
|
+
# time zone abbreviations
|
305
|
+
|
306
|
+
if str.match(/\A[A-Z0-9-]{3,6}\z/)
|
307
|
+
|
308
|
+
twin = Time.utc(Time.now.year, 1, 1)
|
309
|
+
tsum = Time.utc(Time.now.year, 7, 1)
|
310
|
+
|
311
|
+
z =
|
312
|
+
::TZInfo::Timezone.all.find do |tz|
|
313
|
+
tz.period_for_utc(twin).abbreviation.to_s == str ||
|
314
|
+
tz.period_for_utc(tsum).abbreviation.to_s == str
|
116
315
|
end
|
117
|
-
|
316
|
+
return z if z
|
317
|
+
end
|
318
|
+
|
319
|
+
# some time zone aliases
|
320
|
+
|
321
|
+
return ::TZInfo::Timezone.get('Zulu') if %w[ Z ].include?(str)
|
118
322
|
|
119
|
-
|
323
|
+
# custom timezones, no DST, just an offset, like "+08:00" or "-01:30"
|
324
|
+
|
325
|
+
tz = (@custom_tz_cache ||= {})[str]
|
326
|
+
return tz if tz
|
327
|
+
|
328
|
+
if m = str.match(/\A([+-][0-1][0-9]):?([0-5][0-9])\z/)
|
329
|
+
|
330
|
+
hr = m[1].to_i
|
331
|
+
mn = m[2].to_i
|
332
|
+
|
333
|
+
hr = nil if hr.abs > 11
|
334
|
+
hr = nil if mn > 59
|
335
|
+
mn = -mn if hr && hr < 0
|
336
|
+
|
337
|
+
return (
|
338
|
+
@custom_tz_cache[str] =
|
339
|
+
begin
|
340
|
+
tzi = TZInfo::TransitionDataTimezoneInfo.new(str)
|
341
|
+
tzi.offset(str, hr * 3600 + mn * 60, 0, str)
|
342
|
+
tzi.create_timezone
|
343
|
+
end
|
344
|
+
) if hr
|
345
|
+
end
|
120
346
|
|
121
|
-
|
122
|
-
zt.in_zone { zt.seconds = Time.parse(s).to_f }
|
347
|
+
# so it's not a timezone.
|
123
348
|
|
124
|
-
|
349
|
+
nil
|
125
350
|
end
|
126
351
|
|
127
|
-
def self.
|
352
|
+
def self.local_tzone
|
128
353
|
|
129
|
-
|
130
|
-
|
354
|
+
get_tzone(:local)
|
355
|
+
end
|
131
356
|
|
132
|
-
|
133
|
-
|
357
|
+
def self.make(o)
|
358
|
+
|
359
|
+
zt =
|
360
|
+
case o
|
361
|
+
when Time
|
362
|
+
ZoTime.new(o.to_f, o.zone)
|
363
|
+
when Date
|
364
|
+
t =
|
365
|
+
o.respond_to?(:to_time) ?
|
366
|
+
o.to_time :
|
367
|
+
Time.parse(o.strftime('%Y-%m-%d %H:%M:%S'))
|
368
|
+
ZoTime.new(t.to_f, t.zone)
|
369
|
+
when String
|
370
|
+
Rufus::Scheduler.parse_in(o, :no_error => true) || self.parse(o)
|
371
|
+
else
|
372
|
+
o
|
373
|
+
end
|
134
374
|
|
135
|
-
|
375
|
+
zt = ZoTime.new(Time.now.to_f + zt, nil) if zt.is_a?(Numeric)
|
136
376
|
|
137
|
-
|
377
|
+
fail ArgumentError.new(
|
378
|
+
"cannot turn #{o.inspect} to a ZoTime instance"
|
379
|
+
) unless zt.is_a?(ZoTime)
|
138
380
|
|
139
|
-
|
140
|
-
|
381
|
+
zt
|
382
|
+
end
|
141
383
|
|
142
|
-
|
384
|
+
# def in_zone(&block)
|
385
|
+
#
|
386
|
+
# current_timezone = ENV['TZ']
|
387
|
+
# ENV['TZ'] = @zone
|
388
|
+
#
|
389
|
+
# block.call
|
390
|
+
#
|
391
|
+
# ensure
|
392
|
+
#
|
393
|
+
# ENV['TZ'] = current_timezone
|
394
|
+
# end
|
395
|
+
|
396
|
+
protected
|
397
|
+
|
398
|
+
def inc(t, dir)
|
399
|
+
|
400
|
+
if t.is_a?(Numeric)
|
401
|
+
nt = self.dup
|
402
|
+
nt.seconds += dir * t.to_f
|
403
|
+
nt
|
404
|
+
elsif t.respond_to?(:to_f)
|
405
|
+
@seconds + dir * t.to_f
|
406
|
+
else
|
407
|
+
fail ArgumentError.new(
|
408
|
+
"cannot call ZoTime #- or #+ with arg of class #{t.class}")
|
409
|
+
end
|
410
|
+
end
|
143
411
|
|
144
|
-
|
145
|
-
return false if t.zone == 'UTC'
|
146
|
-
return false if t.utc_offset == 0 && str.start_with?(t.zone)
|
147
|
-
# 3 common fallbacks...
|
412
|
+
def _to_f(o)
|
148
413
|
|
149
|
-
|
414
|
+
fail ArgumentError(
|
415
|
+
"comparison of ZoTime with #{o.inspect} failed"
|
416
|
+
) unless o.is_a?(ZoTime) || o.is_a?(Time)
|
150
417
|
|
151
|
-
|
418
|
+
o.to_f
|
152
419
|
end
|
153
420
|
|
154
|
-
def
|
421
|
+
def strfz(code)
|
422
|
+
|
423
|
+
return @zone.name if code == '%/Z'
|
424
|
+
|
425
|
+
per = @zone.period_for_utc(utc)
|
155
426
|
|
156
|
-
|
157
|
-
ENV['TZ'] = @zone
|
427
|
+
return per.abbreviation.to_s if code == '%Z'
|
158
428
|
|
159
|
-
|
429
|
+
off = per.utc_total_offset
|
430
|
+
#
|
431
|
+
sn = off < 0 ? '-' : '+'; off = off.abs
|
432
|
+
hr = off / 3600
|
433
|
+
mn = (off % 3600) / 60
|
434
|
+
sc = 0
|
160
435
|
|
161
|
-
|
436
|
+
fmt =
|
437
|
+
if code == '%z'
|
438
|
+
"%s%02d%02d"
|
439
|
+
elsif code == '%:z'
|
440
|
+
"%s%02d:%02d"
|
441
|
+
else
|
442
|
+
"%s%02d:%02d:%02d"
|
443
|
+
end
|
162
444
|
|
163
|
-
|
445
|
+
fmt % [ sn, hr, mn, sc ]
|
164
446
|
end
|
165
447
|
end
|
166
448
|
end
|