fugit 1.3.6 → 1.4.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of fugit might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +28 -0
- data/CREDITS.md +3 -0
- data/README.md +21 -5
- data/fugit.gemspec +2 -2
- data/lib/fugit.rb +2 -1
- data/lib/fugit/at.rb +1 -0
- data/lib/fugit/cron.rb +28 -13
- data/lib/fugit/duration.rb +3 -3
- data/lib/fugit/misc.rb +1 -0
- data/lib/fugit/nat.rb +506 -347
- data/lib/fugit/parse.rb +1 -0
- metadata +10 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e0c9e5123de2ba25d006290c43946a4028fc3467ec3f424bf57b23ce5c41cd65
|
4
|
+
data.tar.gz: c9f6a5bdc8d40e7b6c585dca633066df98ff22b298c9158c333c54c2fbb2bab3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9135ff881b272674fb1768d2548efca04541f1aab790b04c6a9e060e172c722df7c73bd9dc98a221f7ab4e566b4600cb7e3f667ced5fbc92d7683667c7ab1c21
|
7
|
+
data.tar.gz: 0f6e3113b06d2bc287a608f6215c3a902e3eedf410f6c83e5e7d9da79122b6562f9d400d3cf19f1cc45b662b72e1d740115aedf0502f9552e8af6bdb39db750f
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,34 @@
|
|
2
2
|
# CHANGELOG.md
|
3
3
|
|
4
4
|
|
5
|
+
## fugit 1.4.1 released 2020-11-25
|
6
|
+
|
7
|
+
* Suppress warning, gh-46, thanks @amatsuda
|
8
|
+
|
9
|
+
|
10
|
+
## fugit 1.4.0 released 2020-10-27
|
11
|
+
|
12
|
+
* Ensure cron accepts "25-L" for monthday, gh-45
|
13
|
+
* Allow for "every weekday 8am to 5pm", gh-44
|
14
|
+
* Allow "every day from the 25th to the last", gh-45
|
15
|
+
* Rework nat parser
|
16
|
+
|
17
|
+
|
18
|
+
## fugit 1.3.9 released 2020-09-17
|
19
|
+
|
20
|
+
* Prevent "New York skip", gh-43, thanks @honglooker
|
21
|
+
|
22
|
+
|
23
|
+
## fugit 1.3.8 released 2020-08-06
|
24
|
+
|
25
|
+
* Parse 'every day at 8:30' and ' at 8:30 pm', gh-42
|
26
|
+
|
27
|
+
|
28
|
+
## fugit 1.3.7 released 2020-08-05
|
29
|
+
|
30
|
+
* Parse 'every 12 hours at minute 50', gh-41
|
31
|
+
|
32
|
+
|
5
33
|
## fugit 1.3.6 released 2020-06-01
|
6
34
|
|
7
35
|
* Introduce new nat syntaxed, gh-38
|
data/CREDITS.md
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
|
2
2
|
# fugit credits
|
3
3
|
|
4
|
+
* Matsuda Akira https://github.com/amatsuda gh-46, warning suppression
|
5
|
+
* Honglooker https://github.com/honglooker gh-43, New York cron skip
|
6
|
+
* Jérôme Dalbert https://github.com/jeromedalbert gh-41, gh-42
|
4
7
|
* Danny Ben Shitrit https://github.com/DannyBen nat variants, gh-38
|
5
8
|
* Dominik Sander https://github.com/dsander #rough_frequency 0, gh-36
|
6
9
|
* Milovan Zogovic https://github.com/assembler Cron#match? vs TZ, gh-31
|
data/README.md
CHANGED
@@ -9,7 +9,7 @@ Time tools for [flor](https://github.com/floraison/flor) and the floraison group
|
|
9
9
|
|
10
10
|
It uses [et-orbi](https://github.com/floraison/et-orbi) to represent time instances and [raabro](https://github.com/floraison/raabro) as a basis for its parsers.
|
11
11
|
|
12
|
-
Fugit is a core dependency of [rufus-scheduler](https://github.com/jmettraux/rufus-scheduler) 3.5.
|
12
|
+
Fugit is a core dependency of [rufus-scheduler](https://github.com/jmettraux/rufus-scheduler) >= 3.5.
|
13
13
|
|
14
14
|
|
15
15
|
## Related projects
|
@@ -321,19 +321,35 @@ Fugit.parse('every day at five') # ==> Fugit::Cron instance '0 5 * * *'
|
|
321
321
|
|
322
322
|
### Ambiguous nats
|
323
323
|
|
324
|
-
Not all strings result in a clean, single, cron expression.
|
324
|
+
Not all strings result in a clean, single, cron expression. The `multi: false|true|:fail` argument to `Fugit::Nat.parse` could help.
|
325
325
|
|
326
326
|
```ruby
|
327
|
+
Fugit::Nat.parse('every day at 16:00 and 18:00')
|
328
|
+
.to_cron_s
|
329
|
+
# ==> '0 16,18 * * *' (a single Fugit::Cron instances)
|
327
330
|
Fugit::Nat.parse('every day at 16:00 and 18:00', multi: true)
|
328
|
-
|
331
|
+
.collect(&:to_cron_s)
|
332
|
+
# ==> [ '0 16,18 * * *' ] (array of Fugit::Cron instances, here only one)
|
333
|
+
|
329
334
|
Fugit::Nat.parse('every day at 16:15 and 18:30')
|
330
|
-
|
335
|
+
.to_cron_s
|
336
|
+
# ==> '15 16 * * *' (a single of Fugit::Cron instances)
|
331
337
|
Fugit::Nat.parse('every day at 16:15 and 18:30', multi: true)
|
332
|
-
|
338
|
+
.collect(&:to_cron_s)
|
339
|
+
# ==> [ '15 16 * * *', '30 18 * * *' ] (two Fugit::Cron instances)
|
340
|
+
|
333
341
|
Fugit::Nat.parse('every day at 16:15 and 18:30', multi: :fail)
|
334
342
|
# ==> ArgumentError: multiple crons in "every day at 16:15 and 18:30" (15 16 * * * | 30 18 * * *)
|
343
|
+
Fugit::Nat.parse('every day at 16:15 nada 18:30', multi: true)
|
344
|
+
# ==> nil
|
335
345
|
```
|
336
346
|
|
347
|
+
`multi: true` indicates to `Fugit::Nat` that an array of `Fugit::Cron` instances is expected as a result.
|
348
|
+
|
349
|
+
`multi: :fail` tells `Fugit::Nat.parse` to fail if the result is more than 1 `Fugit::Cron` instances.
|
350
|
+
|
351
|
+
`multi: false` is the default behaviour, return a single `Fugit::Cron` instance or nil when it cannot parse.
|
352
|
+
|
337
353
|
|
338
354
|
## LICENSE
|
339
355
|
|
data/fugit.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.platform = Gem::Platform::RUBY
|
11
11
|
s.authors = [ 'John Mettraux' ]
|
12
12
|
s.email = [ 'jmettraux+flor@gmail.com' ]
|
13
|
-
s.homepage = '
|
13
|
+
s.homepage = 'https://github.com/floraison/fugit'
|
14
14
|
s.license = 'MIT'
|
15
15
|
s.summary = 'time tools for flor'
|
16
16
|
|
@@ -40,7 +40,7 @@ Time tools for flor and the floraison project. Cron parsing and occurrence compu
|
|
40
40
|
#s.add_runtime_dependency 'tzinfo'
|
41
41
|
# this dependency appears in 'et-orbi'
|
42
42
|
|
43
|
-
s.add_runtime_dependency 'raabro', '~> 1.
|
43
|
+
s.add_runtime_dependency 'raabro', '~> 1.4'
|
44
44
|
s.add_runtime_dependency 'et-orbi', '~> 1.1', '>= 1.1.8'
|
45
45
|
|
46
46
|
s.add_development_dependency 'rspec', '~> 3.8'
|
data/lib/fugit.rb
CHANGED
data/lib/fugit/at.rb
CHANGED
data/lib/fugit/cron.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
3
|
module Fugit
|
3
4
|
|
@@ -12,9 +13,9 @@ module Fugit
|
|
12
13
|
'@daily' => '0 0 * * *',
|
13
14
|
'@midnight' => '0 0 * * *',
|
14
15
|
'@noon' => '0 12 * * *',
|
15
|
-
'@hourly' => '0 * * * *' }
|
16
|
+
'@hourly' => '0 * * * *' }.freeze
|
16
17
|
MAXDAYS = [
|
17
|
-
nil, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]
|
18
|
+
nil, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ].freeze
|
18
19
|
|
19
20
|
attr_reader(
|
20
21
|
:original, :zone)
|
@@ -236,6 +237,8 @@ module Fugit
|
|
236
237
|
# the translation occurs in the timezone of
|
237
238
|
# this Fugit::Cron instance
|
238
239
|
|
240
|
+
zfrom = t.time.strftime('%z|%Z')
|
241
|
+
|
239
242
|
loop do
|
240
243
|
|
241
244
|
fail RuntimeError.new(
|
@@ -251,8 +254,14 @@ module Fugit
|
|
251
254
|
min_match?(t) || (t.inc_min; next)
|
252
255
|
sec_match?(t) || (t.inc_sec; next)
|
253
256
|
|
254
|
-
|
255
|
-
|
257
|
+
tt = t.time
|
258
|
+
st = tt.strftime('%F|%T')
|
259
|
+
zt = tt.strftime('%z|%Z')
|
260
|
+
#
|
261
|
+
if st == sfrom && zt != zfrom
|
262
|
+
from, sfrom, zfrom, ifrom = tt, st, zt, t.to_i
|
263
|
+
next
|
264
|
+
end
|
256
265
|
#
|
257
266
|
# when transitioning out of DST, this prevents #next_time from
|
258
267
|
# yielding the same literal time twice in a row, see gh-6
|
@@ -328,7 +337,7 @@ module Fugit
|
|
328
337
|
[ :seconds, 1, 60 ],
|
329
338
|
[ :minutes, 60, 60 ],
|
330
339
|
[ :hours, 3600, 24 ],
|
331
|
-
[ :days, 24 * 3600, 365 ] ]
|
340
|
+
[ :days, 24 * 3600, 365 ] ].freeze
|
332
341
|
|
333
342
|
def rough_frequency
|
334
343
|
|
@@ -487,7 +496,7 @@ module Fugit
|
|
487
496
|
|
488
497
|
sla = 1 if sla == nil
|
489
498
|
sta = min if sta == nil
|
490
|
-
edn = max if edn == nil
|
499
|
+
edn = max if edn == nil || edn < 0 && sta > 0
|
491
500
|
|
492
501
|
range(min, max, sta, edn, sla)
|
493
502
|
end
|
@@ -499,12 +508,10 @@ module Fugit
|
|
499
508
|
{ min: min, max: max, sta: sta, edn: edn, sla: sla }.inspect
|
500
509
|
) if (sta < 0 && edn > 0) || (edn < 0 && sta > 0)
|
501
510
|
|
502
|
-
#p({ min: min, max: max, sta: sta, edn: edn, sla: sla })
|
503
511
|
a = []
|
504
512
|
|
505
513
|
omin, omax = min, max
|
506
514
|
min, max = -max, -1 if sta < 0
|
507
|
-
#p({ min: min, max: max })
|
508
515
|
|
509
516
|
cur = sta
|
510
517
|
|
@@ -604,10 +611,18 @@ module Fugit
|
|
604
611
|
|
605
612
|
module Parser include Raabro
|
606
613
|
|
607
|
-
WEEKDAYS =
|
608
|
-
|
614
|
+
WEEKDAYS =
|
615
|
+
%w[ sunday monday tuesday wednesday thursday friday saturday ].freeze
|
616
|
+
|
617
|
+
WEEKDS =
|
618
|
+
WEEKDAYS.collect { |d| d[0, 3] }.freeze
|
619
|
+
DOW_REX =
|
620
|
+
/([0-7]|#{WEEKDS.join('|')})/i.freeze
|
609
621
|
|
610
|
-
MONTHS =
|
622
|
+
MONTHS =
|
623
|
+
%w[ - jan feb mar apr may jun jul aug sep oct nov dec ].freeze
|
624
|
+
MONTH_REX =
|
625
|
+
/(1[0-2]|0?[1-9]|#{MONTHS[1..-1].join('|')})/i.freeze
|
611
626
|
|
612
627
|
# piece parsers bottom to top
|
613
628
|
|
@@ -621,8 +636,8 @@ module Fugit
|
|
621
636
|
def mos(i); rex(:mos, i, /[0-5]?\d/); end # min or sec
|
622
637
|
def hou(i); rex(:hou, i, /(2[0-4]|[01]?[0-9])/); end
|
623
638
|
def dom(i); rex(:dom, i, /(-?(3[01]|[12][0-9]|0?[1-9])|last|l)/i); end
|
624
|
-
def mon(i); rex(:mon, i,
|
625
|
-
def dow(i); rex(:dow, i,
|
639
|
+
def mon(i); rex(:mon, i, MONTH_REX); end
|
640
|
+
def dow(i); rex(:dow, i, DOW_REX); end
|
626
641
|
|
627
642
|
def dow_hash(i); rex(:hash, i, /#(-?[1-5]|last|l)/i); end
|
628
643
|
|
data/lib/fugit/duration.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
3
|
module Fugit
|
3
4
|
|
@@ -66,10 +67,9 @@ module Fugit
|
|
66
67
|
day: { a: 'D', r: 'd', i: 'D', s: 24 * 3600, I: true, l: 'day' },
|
67
68
|
hou: { a: 'h', r: 'h', i: 'H', s: 3600, I: true, l: 'hour' },
|
68
69
|
min: { a: 'm', r: 'm', i: 'M', s: 60, I: true, l: 'minute' },
|
69
|
-
sec: { a: 's', r: 's', i: 'S', s: 1, I: true, l: 'second' }
|
70
|
-
}
|
70
|
+
sec: { a: 's', r: 's', i: 'S', s: 1, I: true, l: 'second' } }.freeze
|
71
71
|
INFLA_KEYS, NON_INFLA_KEYS =
|
72
|
-
KEYS.partition { |k, v| v[:I] }
|
72
|
+
KEYS.partition { |k, v| v[:I] }.freeze
|
73
73
|
|
74
74
|
def _to_s(key)
|
75
75
|
|
data/lib/fugit/misc.rb
CHANGED
data/lib/fugit/nat.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
3
|
module Fugit
|
3
4
|
|
@@ -16,7 +17,12 @@ module Fugit
|
|
16
17
|
|
17
18
|
#p s; Raabro.pp(Parser.parse(s, debug: 3), colours: true)
|
18
19
|
#(p s; Raabro.pp(Parser.parse(s, debug: 1), colours: true)) rescue nil
|
19
|
-
|
20
|
+
|
21
|
+
if slots = Parser.parse(s)
|
22
|
+
slots.to_crons(opts.merge(_s: s))
|
23
|
+
else
|
24
|
+
nil
|
25
|
+
end
|
20
26
|
end
|
21
27
|
|
22
28
|
def do_parse(s, opts={})
|
@@ -24,500 +30,653 @@ module Fugit
|
|
24
30
|
parse(s, opts) ||
|
25
31
|
fail(ArgumentError.new("could not parse a nat #{s.inspect}"))
|
26
32
|
end
|
33
|
+
end
|
27
34
|
|
28
|
-
|
35
|
+
module Parser include Raabro
|
29
36
|
|
30
|
-
|
37
|
+
one_to_nine =
|
38
|
+
%w[ one two three four five six seven eight nine ]
|
39
|
+
sixties =
|
40
|
+
%w[ zero ] + one_to_nine +
|
41
|
+
%w[ ten eleven twelve thirteen fourteen fifteen sixteen seventeen
|
42
|
+
eighteen nineteen ] +
|
43
|
+
%w[ twenty thirty fourty fifty ]
|
44
|
+
.collect { |a|
|
45
|
+
([ nil ] + one_to_nine)
|
46
|
+
.collect { |b| [ a, b ].compact.join('-') } }
|
47
|
+
.flatten
|
48
|
+
|
49
|
+
NHOURS = sixties[0, 13]
|
50
|
+
.each_with_index
|
51
|
+
.inject({}) { |h, (n, i)| h[n] = i; h }
|
52
|
+
.merge!(
|
53
|
+
'midnight' => 0, 'oh' => 0, 'noon' => 12)
|
54
|
+
.freeze
|
55
|
+
NMINUTES = sixties
|
56
|
+
.each_with_index
|
57
|
+
.inject({}) { |h, (n, i)| h[n] = i; h }
|
58
|
+
.merge!(
|
59
|
+
"o'clock" => 0, 'hundred' => 0)
|
60
|
+
.freeze
|
61
|
+
|
62
|
+
WEEKDAYS = (
|
63
|
+
Fugit::Cron::Parser::WEEKDAYS +
|
64
|
+
Fugit::Cron::Parser::WEEKDS).freeze
|
65
|
+
|
66
|
+
POINTS = %w[
|
67
|
+
minutes? mins? seconds? secs? hours? hou h ].freeze
|
68
|
+
|
69
|
+
INTERVALS = %w[
|
70
|
+
seconds? minutes? hours? days? months?
|
71
|
+
sec min
|
72
|
+
s m h d M ].freeze
|
31
73
|
|
32
|
-
|
33
|
-
|
74
|
+
oh = {
|
75
|
+
'1st' => 1, '2nd' => 2, '3rd' => 3, '21st' => 21, '22nd' => 22,
|
76
|
+
'23rd' => 23, '31st' => 31,
|
77
|
+
'last' => 'L' }
|
78
|
+
(4..30)
|
79
|
+
.each { |i| oh["#{i}th"] = i.to_i }
|
80
|
+
%w[
|
81
|
+
first second third fourth fifth sixth seventh eighth ninth tenth
|
82
|
+
eleventh twelfth thirteenth fourteenth fifteenth sixteenth seventeenth
|
83
|
+
eighteenth nineteenth twentieth twenty-first twenty-second twenty-third
|
84
|
+
twenty-fourth twenty-fifth twenty-sixth twenty-seventh twenty-eighth
|
85
|
+
twenty-ninth thirtieth thirty-first ]
|
86
|
+
.each_with_index { |e, i| oh[e] = i + 1 }
|
87
|
+
OMONTHDAYS = oh.freeze
|
34
88
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
#
|
89
|
+
OMONTHDAY_REX = /#{OMONTHDAYS.keys.join('|')}/i.freeze
|
90
|
+
MONTHDAY_REX = /3[0-1]|[0-2]?[0-9]/.freeze
|
91
|
+
WEEKDAY_REX = /(#{WEEKDAYS.join('|')})(?=($|[-, \t]))/i.freeze
|
92
|
+
# prevent "mon" from eating "monday"
|
93
|
+
NAMED_M_REX = /#{NMINUTES.keys.join('|')}/i.freeze
|
94
|
+
NAMED_H_REX = /#{NHOURS.keys.join('|')}/i.freeze
|
95
|
+
POINT_REX = /(#{POINTS.join('|')})[ \t]+/i.freeze
|
96
|
+
INTERVAL_REX = /[ \t]*(#{INTERVALS.join('|')})/.freeze
|
43
97
|
|
44
|
-
|
45
|
-
|
46
|
-
return nil
|
47
|
-
end
|
98
|
+
#
|
99
|
+
# parsers bottom to top #################################################
|
48
100
|
|
49
|
-
|
101
|
+
def _every(i); rex(nil, i, /[ \t]*every[ \t]+/i); end
|
102
|
+
def _from(i); rex(nil, i, /[ \t]*from[ \t]+/i); end
|
103
|
+
def _at(i); rex(nil, i, /[ \t]*at[ \t]+/i); end
|
104
|
+
def _on(i); rex(nil, i, /[ \t]*on[ \t]+/i); end
|
105
|
+
def _to(i); rex(nil, i, /[ \t]*to[ \t]+/i); end
|
50
106
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
.inject({}) { |r, (m, hs)| (r[hs.sort] ||= []) << m; r }
|
55
|
-
.to_a
|
56
|
-
.sort_by { |hs, ms| -hs.size }
|
57
|
-
if hours.empty?
|
58
|
-
hours << (h[:dom] ? [ [ '0' ], [ '0' ] ] : [ [ '*' ], [ '*' ] ])
|
59
|
-
end
|
107
|
+
def _and(i); rex(nil, i, /[ \t]*and[ \t]+/i); end
|
108
|
+
def _and_or_or(i); rex(nil, i, /[ \t]*(and|or)[ \t]+/i); end
|
109
|
+
def _in_or_on(i); rex(nil, i, /(in|on)[ \t]+/i); end
|
60
110
|
|
61
|
-
|
62
|
-
|
111
|
+
def _and_or_or_or_comma(i)
|
112
|
+
rex(nil, i, /[ \t]*(,[ \t]*)?((and|or)[ \t]+|,[ \t]*)/i); end
|
63
113
|
|
64
|
-
|
65
|
-
|
66
|
-
"(#{crons.collect(&:original).join(' | ')})"
|
67
|
-
) if opts[:multi] == :fail && crons.size > 1
|
114
|
+
def _to_or_dash(i);
|
115
|
+
rex(nil, i, /[ \t]*-[ \t]*|[ \t]+(to|through)[ \t]+/i); end
|
68
116
|
|
69
|
-
|
70
|
-
|
71
|
-
else
|
72
|
-
crons.first
|
73
|
-
end
|
74
|
-
end
|
117
|
+
def _day_s(i); rex(nil, i, /[ \t]*days?[ \t]+/i); end
|
118
|
+
def _the(i); rex(nil, i, /[ \t]*the[ \t]+/i); end
|
75
119
|
|
76
|
-
def
|
77
|
-
|
78
|
-
#puts "ac: " + h.inspect
|
79
|
-
s = []
|
80
|
-
s << h[:sec] if h[:sec]
|
81
|
-
s << h[:hms][1].join(',')
|
82
|
-
s << h[:hms][0].join(',')
|
83
|
-
s << (h[:dom] || '*') << (h[:mon] || '*') << (h[:dow] || '*')
|
84
|
-
s << h[:tz] if h[:tz]
|
85
|
-
|
86
|
-
Fugit::Cron.parse(s.join(' '))
|
87
|
-
end
|
88
|
-
|
89
|
-
def eone(e); e1 = e[1]; e1 == 1 ? '*' : "*/#{e1}"; end
|
90
|
-
|
91
|
-
def parse_interval_elt(e, opts, h)
|
92
|
-
|
93
|
-
e1 = e[1]
|
94
|
-
|
95
|
-
case e[2]
|
96
|
-
when 's', 'sec', 'second', 'seconds'
|
97
|
-
h[:sec] = eone(e)
|
98
|
-
when 'm', 'min', 'mins', 'minute', 'minutes'
|
99
|
-
#(h[:hms] ||= []) << [ '*', eone(e) ]
|
100
|
-
h[:hms] ||= [ [ '*', eone(e) ] ]
|
101
|
-
when 'h', 'hour', 'hours'
|
102
|
-
h[:hms] ||= [ [ eone(e), 0 ] ]
|
103
|
-
when 'd', 'day', 'days'
|
104
|
-
h[:dom] = "*/#{e1}" if e1 > 1
|
105
|
-
h[:hms] ||= [ [ 0, 0 ] ]
|
106
|
-
when 'w', 'week', 'weeks'
|
107
|
-
h[:_fail] = "cannot have crons for \"every #{e1} weeks\"" if e1 > 1
|
108
|
-
h[:hms] ||= [ [ 0, 0 ] ]
|
109
|
-
h[:dow] ||= 0
|
110
|
-
when 'M', 'month', 'months'
|
111
|
-
h[:_fail] = "cannot have crons for \"every #{e1} months\"" if e1 > 12
|
112
|
-
h[:hms] ||= [ [ 0, 0 ] ]
|
113
|
-
h[:dom] = 1
|
114
|
-
h[:mon] = eone(e)
|
115
|
-
when 'Y', 'y', 'year', 'years'
|
116
|
-
h[:_fail] = "cannot have crons for \"every #{e1} years\"" if e1 > 1
|
117
|
-
h[:hms] ||= [ [ 0, 0 ] ]
|
118
|
-
h[:dom] = 1
|
119
|
-
h[:mon] = 1
|
120
|
-
end
|
121
|
-
end
|
120
|
+
def _space(i); rex(nil, i, /[ \t]+/); end
|
121
|
+
def _sep(i); rex(nil, i, /([ \t]+|[ \t]*,[ \t]*)/); end
|
122
122
|
|
123
|
-
def
|
123
|
+
def count(i); rex(:count, i, /\d+/); end
|
124
124
|
|
125
|
-
|
126
|
-
|
125
|
+
def omonthday(i)
|
126
|
+
rex(:omonthday, i, OMONTHDAY_REX)
|
127
127
|
end
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
h[:hms] ||= [ [ 0, 0 ] ]
|
132
|
-
h[:dow] = e[1] == e[2] ? e[1] : "#{e[1]}-#{e[2]}"
|
128
|
+
def monthday(i)
|
129
|
+
rex(:monthday, i, MONTHDAY_REX)
|
133
130
|
end
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
h[:dom] = e[1..-1].join(',')
|
131
|
+
def weekday(i)
|
132
|
+
rex(:weekday, i, WEEKDAY_REX)
|
138
133
|
end
|
139
134
|
|
140
|
-
def
|
135
|
+
def omonthdays(i); jseq(nil, i, :omonthday, :_and_or_or_or_comma); end
|
136
|
+
def monthdays(i); jseq(nil, i, :monthday, :_and_or_or_or_comma); end
|
141
137
|
|
142
|
-
|
143
|
-
|
144
|
-
l = h[:hms].last
|
145
|
-
h[:sec] = l.pop if l.size > 2
|
146
|
-
end
|
138
|
+
def weekdays(i); jseq(:weekdays, i, :weekday, :_and_or_or_or_comma); end
|
147
139
|
|
148
|
-
def
|
140
|
+
def on_the(i); seq(nil, i, :_the, :omonthdays); end
|
149
141
|
|
150
|
-
|
151
|
-
h[:dow] = e1[0]
|
152
|
-
h[:hms] = [ e1[1] ]
|
142
|
+
def _minute(i); rex(nil, i, /[ \t]*minute[ \t]+/i) end
|
153
143
|
|
154
|
-
|
155
|
-
|
144
|
+
def _dmin(i)
|
145
|
+
rex(:dmin, i, /[0-5]?[0-9]/)
|
156
146
|
end
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
h[:tz] = e[1]
|
147
|
+
def and_dmin(i)
|
148
|
+
seq(nil, i, :_and_or_or_or_comma, :_minute, '?', :_dmin)
|
161
149
|
end
|
162
|
-
end
|
163
|
-
|
164
|
-
module Parser include Raabro
|
165
150
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
WEEKDAYS =
|
170
|
-
Fugit::Cron::Parser::WEEKDS + Fugit::Cron::Parser::WEEKDAYS
|
171
|
-
|
172
|
-
NHOURS = {
|
173
|
-
'noon' => [ 12, 0 ],
|
174
|
-
'midnight' => [ 0, 0 ], 'oh' => [ 0, 0 ] }
|
175
|
-
NMINUTES = {
|
176
|
-
"o'clock" => 0, 'five' => 5,
|
177
|
-
'ten' => 10, 'fifteen' => 15,
|
178
|
-
'twenty' => 20, 'twenty-five' => 25,
|
179
|
-
'thirty' => 30, 'thirty-five' => 35,
|
180
|
-
'fourty' => 40, 'fourty-five' => 45,
|
181
|
-
'fifty' => 50, 'fifty-five' => 55 }
|
182
|
-
|
183
|
-
oh = {
|
184
|
-
'1st' => 1, '2nd' => 2, '3rd' => 3, '21st' => 21, '22nd' => 22,
|
185
|
-
'23rd' => 23, '31st' => 31 }
|
186
|
-
%w[ 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 24 25 26 27 28 29 30 ]
|
187
|
-
.each { |i| oh["#{i}th"] = i.to_i }
|
188
|
-
%w[
|
189
|
-
first second third fourth fifth sixth seventh eighth ninth tenth
|
190
|
-
eleventh twelfth thirteenth fourteenth fifteenth sixteenth seventeenth
|
191
|
-
eighteenth nineteenth twentieth twenty-first twenty-second twenty-third
|
192
|
-
twenty-fourth twenty-fifth twenty-fifth twenty-sixth twenty-seventh
|
193
|
-
twenty-eighth twenty-ninth thirtieth thirty-first ]
|
194
|
-
.each_with_index { |e, i| oh[e] = i + 1 }
|
195
|
-
ORDINALS = oh
|
151
|
+
def on_minutes(i)
|
152
|
+
seq(:on_minutes, i, :_minute, :_dmin, :and_dmin, '*')
|
153
|
+
end
|
196
154
|
|
197
|
-
|
155
|
+
def on_thex(i);
|
156
|
+
rex(:on_thex, i, /[ \t]*the[ \t]+(hour|minute)[ \t]*/i);
|
157
|
+
end
|
198
158
|
|
199
|
-
def
|
200
|
-
def
|
201
|
-
def
|
202
|
-
def _in(i); rex(nil, i, /\s*(in|on)\s+/i); end
|
203
|
-
def _to(i); rex(nil, i, /\s*to\s+/i); end
|
204
|
-
def _dash(i); rex(nil, i, /-\s*/i); end
|
205
|
-
def _and(i); rex(nil, i, /\s*and\s+/i); end
|
206
|
-
def _on(i); rex(nil, i, /\s*on\s+/i); end
|
159
|
+
def on_thes(i); jseq(:on_thes, i, :on_the, :_and_or_or_or_comma); end
|
160
|
+
def on_days(i); seq(:on_days, i, :_day_s, :monthdays); end
|
161
|
+
def on_weekdays(i); ren(:on_weekdays, i, :weekdays); end
|
207
162
|
|
208
|
-
def
|
209
|
-
|
210
|
-
end
|
211
|
-
def _at_comma(i)
|
212
|
-
rex(nil, i, /\s*(at\s|,|)\s*/i)
|
163
|
+
def on_object(i)
|
164
|
+
alt(nil, i, :on_days, :on_weekdays, :on_minutes, :on_thes, :on_thex)
|
213
165
|
end
|
214
|
-
def
|
215
|
-
|
166
|
+
def on_objects(i)
|
167
|
+
jseq(nil, i, :on_object, :_and)
|
216
168
|
end
|
217
169
|
|
218
|
-
|
170
|
+
#'every month on day 2 at 10:00' => '0 10 2 * *',
|
171
|
+
#'every month on day 2 and 5 at 10:00' => '0 10 2,5 * *',
|
172
|
+
#'every month on days 1,15 at 10:00' => '0 10 1,15 * *',
|
173
|
+
#
|
174
|
+
#'every week on monday 18:23' => '23 18 * * 1',
|
175
|
+
#
|
176
|
+
# every month on the 1st
|
177
|
+
def on(i)
|
178
|
+
seq(:on, i, :_on, :on_objects)
|
179
|
+
end
|
219
180
|
|
220
|
-
def
|
221
|
-
rex(nil, i,
|
222
|
-
|
181
|
+
def city_tz(i)
|
182
|
+
rex(nil, i, /[A-Z][a-zA-Z0-9+\-]+(\/[A-Z][a-zA-Z0-9+\-_]+){0,2}/)
|
183
|
+
end
|
184
|
+
def named_tz(i)
|
185
|
+
rex(nil, i, /Z|UTC/)
|
223
186
|
end
|
224
|
-
def
|
225
|
-
rex(nil, i,
|
226
|
-
|
187
|
+
def delta_tz(i)
|
188
|
+
rex(nil, i, /[-+]([01][0-9]|2[0-4])(:?(00|15|30|45))?/)
|
189
|
+
end
|
190
|
+
def tz(i)
|
191
|
+
alt(:tz, i, :city_tz, :named_tz, :delta_tz)
|
227
192
|
end
|
228
193
|
def tzone(i)
|
229
|
-
|
194
|
+
seq(nil, i, :_in_or_on, '?', :tz)
|
230
195
|
end
|
231
196
|
|
232
|
-
def
|
233
|
-
rex(
|
197
|
+
def digital_hour(i)
|
198
|
+
rex(
|
199
|
+
:digital_hour, i,
|
200
|
+
/(2[0-4]|[0-1]?[0-9]):([0-5][0-9])([ \t]*(am|pm))?/i)
|
234
201
|
end
|
235
202
|
|
236
|
-
def
|
237
|
-
rex(:
|
238
|
-
end
|
239
|
-
def named_digit(i)
|
240
|
-
seq(:named_digit, i, :dname, :integer)
|
203
|
+
def ampm(i)
|
204
|
+
rex(:ampm, i, /[ \t]*(am|pm)/i)
|
241
205
|
end
|
242
|
-
def
|
243
|
-
|
206
|
+
def dark(i)
|
207
|
+
rex(:dark, i, /[ \t]*dark/i)
|
244
208
|
end
|
245
209
|
|
246
|
-
def
|
247
|
-
|
210
|
+
def simple_h(i)
|
211
|
+
rex(:simple_h, i, /#{(0..24).to_a.reverse.join('|')}/)
|
248
212
|
end
|
249
|
-
|
250
|
-
|
251
|
-
rex(:nminute, i, /(#{NMINUTES.keys.join('|')})\s*/i)
|
213
|
+
def simple_hour(i)
|
214
|
+
seq(:simple_hour, i, :simple_h, :ampm, '?')
|
252
215
|
end
|
253
|
-
|
254
|
-
|
216
|
+
|
217
|
+
def named_m(i)
|
218
|
+
rex(:named_m, i, NAMED_M_REX)
|
255
219
|
end
|
256
|
-
def
|
257
|
-
seq(
|
220
|
+
def named_min(i)
|
221
|
+
seq(nil, i, :_space, :named_m)
|
258
222
|
end
|
259
223
|
|
224
|
+
def named_h(i)
|
225
|
+
rex(:named_h, i, NAMED_H_REX)
|
226
|
+
end
|
260
227
|
def named_hour(i)
|
261
|
-
|
228
|
+
seq(:named_hour, i, :named_h, :dark, '?', :named_min, '?', :ampm, '?')
|
262
229
|
end
|
263
230
|
|
264
|
-
def
|
265
|
-
rex(:shour, i, /(2[0-4]|[01]?[0-9])/)
|
266
|
-
end
|
267
|
-
def simple_hour(i)
|
268
|
-
seq(:simple_hour, i, :shour, :am_pm, '?')
|
269
|
-
end
|
231
|
+
def _point(i); rex(:point, i, POINT_REX); end
|
270
232
|
|
271
|
-
def
|
272
|
-
|
233
|
+
def counts(i)
|
234
|
+
jseq(nil, i, :count, :_and_or_or_or_comma)
|
273
235
|
end
|
274
236
|
|
237
|
+
def at_p(i)
|
238
|
+
seq(:at_p, i, :_point, :counts)
|
239
|
+
end
|
275
240
|
def at_point(i)
|
276
|
-
|
277
|
-
:digital_hour, :simple_hour, :named_hour, :numeral_hour,
|
278
|
-
:named_digits)
|
241
|
+
jseq(nil, i, :at_p, :_and_or_or)
|
279
242
|
end
|
280
243
|
|
281
|
-
|
282
|
-
|
244
|
+
# at five
|
245
|
+
# at five pm
|
246
|
+
# at five o'clock
|
247
|
+
# at 16:30
|
248
|
+
# at noon
|
249
|
+
# at 18:00 UTC <-- ...tz
|
250
|
+
def at_object(i)
|
251
|
+
alt(nil, i, :named_hour, :digital_hour, :simple_hour, :at_point)
|
283
252
|
end
|
284
|
-
|
285
|
-
|
286
|
-
seq(nil, i, :_and_or_comma, :at_point)
|
253
|
+
def at_objects(i)
|
254
|
+
jseq(nil, i, :at_object, :_and_or_or_or_comma)
|
287
255
|
end
|
288
256
|
|
289
|
-
def
|
290
|
-
|
291
|
-
/(
|
292
|
-
y(ears?)?|months?|w(eeks?)?|d(ays?)?|
|
293
|
-
h(ours?)?|m(in(ute)?s?)?|s(ec(ond)?s?)?
|
294
|
-
)(\s+|$)/ix)
|
257
|
+
def at(i)
|
258
|
+
seq(:at, i, :_at, '?', :at_objects)
|
295
259
|
end
|
296
260
|
|
297
|
-
def
|
298
|
-
rex(:
|
299
|
-
/(year|month|week|day|hour|min(ute)?|sec(ond)?)(\s+|$)/i)
|
261
|
+
def interval(i)
|
262
|
+
rex(:interval, i, INTERVAL_REX)
|
300
263
|
end
|
301
|
-
|
302
|
-
|
264
|
+
|
265
|
+
# every day
|
266
|
+
# every 1 minute
|
267
|
+
def every_interval(i)
|
268
|
+
seq(:every_interval, i, :count, '?', :interval)
|
303
269
|
end
|
304
270
|
|
305
|
-
def
|
306
|
-
rex(:
|
271
|
+
def every_single_interval(i)
|
272
|
+
rex(:every_single_interval, i, /(1[ \t]+)?(week|year)/)
|
307
273
|
end
|
308
274
|
|
309
|
-
def
|
310
|
-
|
275
|
+
def to_weekday(i)
|
276
|
+
seq(:to_weekday, i, :weekday, :_to_or_dash, :weekday)
|
277
|
+
end
|
311
278
|
|
312
|
-
def
|
313
|
-
|
279
|
+
def weekday_range(i)
|
280
|
+
alt(nil, i, :to_weekday, :weekdays)
|
314
281
|
end
|
315
|
-
|
316
|
-
|
282
|
+
|
283
|
+
def to_omonthday(i)
|
284
|
+
seq(:to_omonthday, i,
|
285
|
+
:_the, '?', :omonthday, :_to, :_the, '?', :omonthday)
|
317
286
|
end
|
318
|
-
|
319
|
-
|
287
|
+
|
288
|
+
def to_hour(i)
|
289
|
+
seq(:to_hour, i, :at_object, :_to, :at_object)
|
320
290
|
end
|
321
291
|
|
322
|
-
def
|
323
|
-
|
292
|
+
def from_object(i)
|
293
|
+
alt(nil, i, :to_weekday, :to_omonthday, :to_hour)
|
324
294
|
end
|
325
|
-
def
|
326
|
-
|
295
|
+
def from_objects(i)
|
296
|
+
jseq(nil, i, :from_object, :_and_or_or)
|
327
297
|
end
|
328
|
-
def
|
329
|
-
|
298
|
+
def from(i)
|
299
|
+
seq(nil, i, :_from, '?', :from_objects)
|
330
300
|
end
|
331
301
|
|
332
|
-
|
333
|
-
|
302
|
+
# every monday
|
303
|
+
# every Fri-Sun
|
304
|
+
# every Monday and Tuesday
|
305
|
+
def every_weekday(i)
|
306
|
+
jseq(nil, i, :weekday_range, :_and_or_or)
|
334
307
|
end
|
335
308
|
|
336
|
-
def
|
337
|
-
|
309
|
+
def otm(i)
|
310
|
+
rex(nil, i, /[ \t]+of the month/)
|
338
311
|
end
|
339
|
-
|
340
|
-
|
312
|
+
|
313
|
+
# every 1st of the month
|
314
|
+
# every first of the month
|
315
|
+
# Every 2nd of the month
|
316
|
+
# Every second of the month
|
317
|
+
# every 15th of the month
|
318
|
+
def every_of_the_month(i)
|
319
|
+
seq(nil, i, :omonthdays, :otm)
|
341
320
|
end
|
342
|
-
|
343
|
-
|
321
|
+
|
322
|
+
def every_named(i)
|
323
|
+
rex(:every_named, i, /weekday/i)
|
344
324
|
end
|
345
325
|
|
346
|
-
def
|
347
|
-
|
326
|
+
def every_object(i)
|
327
|
+
alt(
|
328
|
+
nil, i,
|
329
|
+
:every_weekday, :every_of_the_month,
|
330
|
+
:every_interval, :every_named, :every_single_interval)
|
348
331
|
end
|
349
|
-
def
|
350
|
-
|
332
|
+
def every_objects(i)
|
333
|
+
jseq(nil, i, :every_object, :_and_or_or)
|
351
334
|
end
|
352
|
-
|
353
|
-
|
335
|
+
|
336
|
+
def every(i)
|
337
|
+
seq(:every, i, :_every, :every_objects)
|
354
338
|
end
|
355
339
|
|
356
|
-
def
|
357
|
-
alt(nil, i, :
|
340
|
+
def nat_elt(i)
|
341
|
+
alt(nil, i, :every, :from, :at, :tzone, :on)
|
342
|
+
end
|
343
|
+
def nat(i)
|
344
|
+
jseq(:nat, i, :nat_elt, :_sep)
|
358
345
|
end
|
359
346
|
|
360
|
-
|
361
|
-
|
347
|
+
#
|
348
|
+
# rewrite parsed tree ###################################################
|
349
|
+
|
350
|
+
def slot(key, data0, data1=nil, opts=nil)
|
351
|
+
Slot.new(key, data0, data1, opts)
|
362
352
|
end
|
363
353
|
|
364
|
-
def
|
365
|
-
|
354
|
+
def _rewrite_subs(t, key=nil)
|
355
|
+
t.subgather(key).collect { |ct| rewrite(ct) }
|
366
356
|
end
|
367
|
-
def
|
368
|
-
|
357
|
+
def _rewrite_sub(t, key=nil)
|
358
|
+
st = t.sublookup(key)
|
359
|
+
st ? rewrite(st) : nil
|
369
360
|
end
|
370
361
|
|
371
|
-
def
|
372
|
-
|
362
|
+
def rewrite_dmin(t)
|
363
|
+
t.strinp
|
373
364
|
end
|
374
|
-
|
375
|
-
|
365
|
+
|
366
|
+
def rewrite_on_minutes(t)
|
367
|
+
#Raabro.pp(t, colours: true)
|
368
|
+
mins = t.subgather(:dmin).collect(&:strinp)
|
369
|
+
#slot(:m, mins.join(','))
|
370
|
+
slot(:hm, '*', mins.join(','), strong: 1)
|
376
371
|
end
|
377
|
-
|
378
|
-
|
372
|
+
|
373
|
+
def rewrite_on_thex(t)
|
374
|
+
case s = t.string
|
375
|
+
#when /hour/i then slot(:h, 0)
|
376
|
+
#else slot(:m, '*')
|
377
|
+
when /hour/i then slot(:hm, 0, '*', strong: 0)
|
378
|
+
else slot(:hm, '*', '*', strong: 1)
|
379
|
+
end
|
379
380
|
end
|
380
|
-
|
381
|
-
|
381
|
+
|
382
|
+
def rewrite_on_thes(t)
|
383
|
+
_rewrite_subs(t, :omonthday)
|
382
384
|
end
|
383
|
-
def
|
384
|
-
|
385
|
+
def rewrite_on_days(t)
|
386
|
+
_rewrite_subs(t, :monthday)
|
385
387
|
end
|
386
388
|
|
387
|
-
def
|
388
|
-
|
389
|
-
end
|
390
|
-
def at_every(i)
|
391
|
-
seq(nil, i, :at, :every, :tz, '?')
|
389
|
+
def rewrite_on(t)
|
390
|
+
_rewrite_subs(t)
|
392
391
|
end
|
393
392
|
|
394
|
-
def
|
395
|
-
|
393
|
+
def rewrite_monthday(t)
|
394
|
+
slot(:monthday, t.string.to_i)
|
396
395
|
end
|
397
396
|
|
398
|
-
def
|
399
|
-
|
397
|
+
def rewrite_omonthday(t)
|
398
|
+
slot(:monthday, OMONTHDAYS[t.string.downcase])
|
400
399
|
end
|
401
|
-
|
402
|
-
|
400
|
+
|
401
|
+
def rewrite_at_p(t)
|
402
|
+
pt = t.sublookup(:point).strinpd
|
403
|
+
pt = pt.start_with?('mon') ? 'M' : pt[0, 1]
|
404
|
+
pts = t.subgather(:count).collect { |e| e.string.to_i }
|
405
|
+
#p [ pt, pts ]
|
406
|
+
case pt
|
407
|
+
#when 'm' then slot(:m, pts)
|
408
|
+
when 'm' then slot(:hm, '*', pts, strong: 1)
|
409
|
+
when 's' then slot(:second, pts)
|
410
|
+
else slot(pt.to_sym, pts)
|
411
|
+
end
|
403
412
|
end
|
404
|
-
|
405
|
-
|
413
|
+
|
414
|
+
def rewrite_every_single_interval(t)
|
415
|
+
case t.string
|
416
|
+
when /year/i then [ slot(:month, 1, :weak), slot(:monthday, 1, :weak) ]
|
417
|
+
#when /week/i then xxx...
|
418
|
+
else slot(:weekday, 0, :weak)
|
419
|
+
end
|
406
420
|
end
|
407
421
|
|
408
|
-
def
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
422
|
+
def rewrite_every_interval(t)
|
423
|
+
|
424
|
+
#Raabro.pp(t, colours: true)
|
425
|
+
ci = t.subgather(nil).collect(&:string)
|
426
|
+
i = ci.pop.strip[0, 3]
|
427
|
+
c = (ci.pop || '1').strip
|
428
|
+
i = (i == 'M' || i.downcase == 'mon') ? 'M' : i[0, 1].downcase
|
429
|
+
cc = c == '1' ? '*' : "*/#{c}"
|
430
|
+
|
431
|
+
case i
|
432
|
+
when 'M' then slot(:month, cc)
|
433
|
+
when 'd' then slot(:monthday, cc, :weak)
|
434
|
+
#when 'h' then slot(:hm, cc, 0, weak: :minute)
|
435
|
+
when 'h' then slot(:hm, cc, 0, weak: 1)
|
436
|
+
when 'm' then slot(:hm, '*', cc, strong: 1)
|
437
|
+
when 's' then slot(:second, cc)
|
438
|
+
else {}
|
439
|
+
end
|
413
440
|
end
|
414
441
|
|
415
|
-
|
442
|
+
def rewrite_every_named(t)
|
416
443
|
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
444
|
+
case s = t.string
|
445
|
+
when /weekday/i then slot(:weekday, '1-5', :weak)
|
446
|
+
when /week/i then slot(:weekday, '0', :weak)
|
447
|
+
else fail "cannot rewrite #{s.inspect}"
|
448
|
+
end
|
422
449
|
end
|
423
|
-
|
424
|
-
|
450
|
+
|
451
|
+
def rewrite_tz(t)
|
452
|
+
slot(:tz, t.string)
|
425
453
|
end
|
426
|
-
|
427
|
-
|
454
|
+
|
455
|
+
def rewrite_weekday(t)
|
456
|
+
Fugit::Cron::Parser::WEEKDS.index(t.string[0, 3].downcase)
|
428
457
|
end
|
429
458
|
|
430
|
-
def
|
459
|
+
def rewrite_weekdays(t)
|
460
|
+
#Raabro.pp(t, colours: true)
|
461
|
+
slot(:weekday, _rewrite_subs(t, :weekday))
|
462
|
+
end
|
463
|
+
alias rewrite_on_weekdays rewrite_weekdays
|
431
464
|
|
432
|
-
def
|
465
|
+
def rewrite_to_weekday(t)
|
466
|
+
wd0, wd1 = _rewrite_subs(t, :weekday)
|
467
|
+
#wd1 = 7 if wd1 == 0
|
468
|
+
slot(:weekday, "#{wd0}-#{wd1}")
|
469
|
+
end
|
433
470
|
|
434
|
-
|
471
|
+
def rewrite_to_omonthday(t)
|
472
|
+
md0, md1 = _rewrite_subs(t, :omonthday).collect(&:_data0)
|
473
|
+
slot(:monthday, "#{md0}-#{md1}")
|
435
474
|
end
|
436
475
|
|
437
|
-
def
|
476
|
+
def rewrite_digital_hour(t)
|
477
|
+
h, m, ap = t.strinpd.split(/[: \t]+/)
|
478
|
+
h, m = h.to_i, m.to_i
|
479
|
+
h += 12 if ap && ap == 'pm'
|
480
|
+
slot(:hm, h.to_i, m.to_i)
|
481
|
+
end
|
438
482
|
|
439
|
-
|
483
|
+
def rewrite_simple_hour(t)
|
484
|
+
h, ap = t.subgather(nil).collect(&:strinpd)
|
485
|
+
h = h.to_i
|
486
|
+
h = h + 12 if ap == 'pm'
|
487
|
+
slot(:hm, h, 0)
|
440
488
|
end
|
441
489
|
|
442
|
-
def
|
490
|
+
def rewrite_named_hour(t)
|
443
491
|
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
end
|
492
|
+
ht = t.sublookup(:named_h)
|
493
|
+
mt = t.sublookup(:named_m)
|
494
|
+
apt = t.sublookup(:ampm)
|
448
495
|
|
449
|
-
|
496
|
+
h = ht.strinp
|
497
|
+
m = mt ? mt.strinp : 0
|
498
|
+
#p [ 0, '-->', h, m ]
|
499
|
+
h = NHOURS[h]
|
500
|
+
m = NMINUTES[m] || m
|
501
|
+
#p [ 1, '-->', h, m ]
|
450
502
|
|
451
|
-
|
503
|
+
h += 12 if h < 13 && apt && apt.strinpd == 'pm'
|
452
504
|
|
453
|
-
|
454
|
-
when /^s/ then [ '*', '*', i ]
|
455
|
-
when /^m/ then [ '*', i ]
|
456
|
-
end
|
505
|
+
slot(:hm, h, m)
|
457
506
|
end
|
458
507
|
|
459
|
-
def
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
[ v, m ]
|
508
|
+
def rewrite_to_hour(t)
|
509
|
+
#Raabro.pp(t, colours: true)
|
510
|
+
ht0, ht1 = t.subgather(nil)
|
511
|
+
h0, h1 = rewrite(ht0), rewrite(ht1)
|
512
|
+
fail ArgumentError.new(
|
513
|
+
"cannot deal with #{ht0.strinp} to #{ht1.strinp}, minutes diverge"
|
514
|
+
) if h0.data1 != h1.data1
|
515
|
+
slot(:hm, "#{h0._data0}-#{h1._data0}", 0, strong: 0)
|
468
516
|
end
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
v += 12 if vs[1] == 'pm'
|
473
|
-
[ v, 0 ]
|
517
|
+
|
518
|
+
def rewrite_at(t)
|
519
|
+
_rewrite_subs(t)
|
474
520
|
end
|
475
|
-
|
476
|
-
|
477
|
-
|
521
|
+
|
522
|
+
def rewrite_every(t)
|
523
|
+
_rewrite_sub(t)
|
478
524
|
end
|
479
525
|
|
480
|
-
def
|
526
|
+
def rewrite_nat(t)
|
527
|
+
#Raabro.pp(t, colours: true)
|
528
|
+
Fugit::Nat::SlotGroup.new(_rewrite_subs(t).flatten)
|
529
|
+
end
|
530
|
+
end
|
481
531
|
|
482
|
-
|
532
|
+
class Slot
|
533
|
+
attr_reader :key
|
534
|
+
attr_accessor :_data0, :_data1
|
535
|
+
def initialize(key, d0, d1=nil, opts=nil)
|
536
|
+
d1, opts = d1.is_a?(Symbol) ? [ nil, d1 ] : [ d1, opts ]
|
537
|
+
@key, @_data0, @_data1 = key, d0, d1
|
538
|
+
@opts = (opts.is_a?(Symbol) ? { opts => true } : opts) || {}
|
539
|
+
end
|
540
|
+
def data0; @data0 ||= Array(@_data0); end
|
541
|
+
def data1; @data1 ||= Array(@_data1); end
|
542
|
+
def weak; @opts[:weak]; end
|
543
|
+
def strong; @opts[:strong]; end
|
544
|
+
def graded?; weak || strong; end
|
545
|
+
def append(slot)
|
546
|
+
@_data0, @_data1 = conflate(0, slot), conflate(1, slot)
|
547
|
+
@opts.clear
|
548
|
+
self
|
549
|
+
end
|
550
|
+
def inspect
|
551
|
+
a = [ @key, @_data0 ]
|
552
|
+
a << @_data1 if @_data1 != nil
|
553
|
+
a << @opts if @opts && @opts.keys.any?
|
554
|
+
"(slot #{a.collect(&:inspect).join(' ')})"
|
555
|
+
end
|
556
|
+
def a; [ data0, data1 ]; end
|
557
|
+
protected
|
558
|
+
def to_a(x)
|
559
|
+
return [] if x == '*'
|
560
|
+
Array(x)
|
561
|
+
end
|
562
|
+
def conflate(index, slot)
|
563
|
+
a, b = index == 0 ? [ @_data0, slot._data0 ] : [ @_data1, slot._data1 ]
|
564
|
+
return a if b == nil
|
565
|
+
return b if a == nil
|
566
|
+
if ra = (index == 0 && slot.strong == 1 && hour_range)
|
567
|
+
h0, h1 = ra[0], ra[1] - 1; return h0 == h1 ? h0 : "#{h0}-#{h1}"
|
568
|
+
elsif rb = (index == 0 && strong == 1 && slot.hour_range)
|
569
|
+
h0, h1 = rb[0], rb[1] - 1; return h0 == h1 ? h0 : "#{h0}-#{h1}"
|
570
|
+
end
|
571
|
+
return a if strong == index || strong == true
|
572
|
+
return b if slot.strong == index || slot.strong == true
|
573
|
+
return a if slot.weak == index || slot.weak == true
|
574
|
+
return b if weak == index || weak == true
|
575
|
+
return [ '*' ] if a == '*' && b == '*'
|
576
|
+
to_a(a).concat(to_a(b))
|
577
|
+
end
|
578
|
+
def hour_range
|
579
|
+
m = (key == :hm && @_data1 == 0 && @_data0.match(/\A(\d+)-(\d+)\z/))
|
580
|
+
m ? [ m[1].to_i, m[2].to_i ] : nil
|
483
581
|
end
|
582
|
+
end
|
484
583
|
|
485
|
-
|
584
|
+
class SlotGroup
|
486
585
|
|
487
|
-
def
|
586
|
+
def initialize(slots)
|
488
587
|
|
489
|
-
#
|
490
|
-
|
491
|
-
|
588
|
+
#puts "SlotGroup.new " + slots.inspect
|
589
|
+
@slots = {}
|
590
|
+
@hms = []
|
591
|
+
|
592
|
+
slots.each do |s|
|
593
|
+
if s.key == :hm
|
594
|
+
#ls = @hms.last; @hms.pop if ls && ls.key == :hm && ls.weak == true
|
595
|
+
@hms << s
|
596
|
+
elsif hs = @slots[s.key]
|
597
|
+
hs.append(s)
|
598
|
+
else
|
599
|
+
@slots[s.key] = s
|
600
|
+
end
|
601
|
+
end
|
602
|
+
|
603
|
+
if @slots[:monthday] || @slots[:weekday]
|
604
|
+
@hms << make_slot(:hm, 0, 0) if @hms.empty?
|
605
|
+
elsif @slots[:month]
|
606
|
+
@hms << make_slot(:hm, 0, 0) if @hms.empty?
|
607
|
+
@slots[:monthday] ||= make_slot(:monthday, 1)
|
608
|
+
end
|
492
609
|
end
|
493
610
|
|
494
|
-
|
611
|
+
def to_crons(opts)
|
495
612
|
|
496
|
-
|
497
|
-
def rewrite_dow_list(t); [ :dow_list, *_rewrite_children(t) ]; end
|
613
|
+
multi = opts.has_key?(:multi) ? opts[:multi] : false
|
498
614
|
|
499
|
-
|
615
|
+
hms = determine_hms
|
500
616
|
|
501
|
-
|
617
|
+
if multi == :fail && hms.count > 1
|
618
|
+
fail(ArgumentError.new(
|
619
|
+
"multiple crons in #{opts[:_s].inspect} - #{@slots.inspect}"))
|
620
|
+
elsif multi == true
|
621
|
+
hms.collect { |hm| parse_cron(hm) }
|
622
|
+
else
|
623
|
+
parse_cron(hms.first)
|
624
|
+
end
|
502
625
|
end
|
503
626
|
|
504
|
-
|
627
|
+
protected
|
505
628
|
|
506
|
-
|
629
|
+
def make_slot(key, data0, data1=nil)
|
507
630
|
|
508
|
-
|
631
|
+
Fugit::Nat::Slot.new(key, data0, data1)
|
509
632
|
end
|
510
633
|
|
511
|
-
|
512
|
-
alias rewrite_at _rewrite_multiple
|
634
|
+
def determine_hms
|
513
635
|
|
514
|
-
|
515
|
-
alias rewrite_every _rewrite_child
|
636
|
+
return [ [ [ '*' ], [ '*' ] ] ] if @hms.empty?
|
516
637
|
|
517
|
-
|
638
|
+
hms = @hms.dup
|
639
|
+
#
|
640
|
+
while ig = (hms.count > 1 && hms.index { |hm| hm.graded? }) do
|
641
|
+
sg = hms[ig]
|
642
|
+
so = hms.delete_at(ig == 0 ? 1 : ig - 1)
|
643
|
+
sg.append(so)
|
644
|
+
end
|
645
|
+
|
646
|
+
hms
|
647
|
+
.collect(&:a)
|
648
|
+
.inject({}) { |r, hm|
|
649
|
+
hm[1].each { |m| (r[m] ||= []).concat(hm[0]) }
|
650
|
+
r }
|
651
|
+
.inject({}) { |r, (m, hs)|
|
652
|
+
(r[hs.sort] ||= []) << m
|
653
|
+
r }
|
654
|
+
.to_a
|
655
|
+
end
|
656
|
+
|
657
|
+
def parse_cron(hm)
|
658
|
+
|
659
|
+
a = [
|
660
|
+
slot(:second, '0'),
|
661
|
+
hm[1],
|
662
|
+
hm[0],
|
663
|
+
slot(:monthday, '*'),
|
664
|
+
slot(:month, '*'),
|
665
|
+
slot(:weekday, '*') ]
|
666
|
+
tz = @slots[:tz]
|
667
|
+
a << tz.data0 if tz
|
668
|
+
a.shift if a.first == [ '0' ]
|
669
|
+
|
670
|
+
s = a
|
671
|
+
.collect { |e| e.uniq.sort.collect(&:to_s).join(',') }
|
672
|
+
.join(' ')
|
673
|
+
|
674
|
+
Fugit::Cron.parse(s)
|
675
|
+
end
|
518
676
|
|
519
|
-
|
520
|
-
|
677
|
+
def slot(key, default)
|
678
|
+
s = @slots[key]
|
679
|
+
s ? s.data0 : [ default ]
|
521
680
|
end
|
522
681
|
end
|
523
682
|
end
|