fugit 1.3.7 → 1.4.2
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 +29 -0
- data/CREDITS.md +5 -1
- data/LICENSE.txt +1 -1
- data/README.md +20 -4
- data/fugit.gemspec +2 -2
- data/lib/fugit.rb +2 -1
- data/lib/fugit/at.rb +1 -0
- data/lib/fugit/cron.rb +34 -20
- data/lib/fugit/duration.rb +3 -3
- data/lib/fugit/misc.rb +1 -0
- data/lib/fugit/nat.rb +504 -349
- data/lib/fugit/parse.rb +1 -0
- metadata +11 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c0a2fdae36b04308242e58a5da6b2d5e65e749cb10b02d0d408988611067e46b
|
4
|
+
data.tar.gz: 19d7a91ec345a2b35c1b7568e3a6cfe4e70fc6f780e0332972507bbef6bda0e7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1f5eccb6a96c688390dfdf231ba30812b05bdb22010ff1820b08eae5348809dd5793900c45c073500b8e1358ab90b4e40ee6bfd8badf4838d8e5b13a1d0f1428
|
7
|
+
data.tar.gz: 69fdda6d16c19beebae198a0610d69ab4496f1873b2ee7b825737f635ffd8744d054436f0b51dd372caa45e2945d8051d6a191b89ecd27d3c4a8b72bca59cffa
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,35 @@
|
|
2
2
|
# CHANGELOG.md
|
3
3
|
|
4
4
|
|
5
|
+
## fugit 1.4.2 released 2021-01-12
|
6
|
+
|
7
|
+
* Fix Fugit::Cron.previous_time vs last day of month, gh-51
|
8
|
+
* Let Fugit::Cron.parse('') return nil, gh-49
|
9
|
+
|
10
|
+
|
11
|
+
## fugit 1.4.1 released 2020-11-25
|
12
|
+
|
13
|
+
* Suppress warning, gh-46, thanks @amatsuda
|
14
|
+
|
15
|
+
|
16
|
+
## fugit 1.4.0 released 2020-10-27
|
17
|
+
|
18
|
+
* Ensure cron accepts "25-L" for monthday, gh-45
|
19
|
+
* Allow for "every weekday 8am to 5pm", gh-44
|
20
|
+
* Allow "every day from the 25th to the last", gh-45
|
21
|
+
* Rework nat parser
|
22
|
+
|
23
|
+
|
24
|
+
## fugit 1.3.9 released 2020-09-17
|
25
|
+
|
26
|
+
* Prevent "New York skip", gh-43, thanks @honglooker
|
27
|
+
|
28
|
+
|
29
|
+
## fugit 1.3.8 released 2020-08-06
|
30
|
+
|
31
|
+
* Parse 'every day at 8:30' and ' at 8:30 pm', gh-42
|
32
|
+
|
33
|
+
|
5
34
|
## fugit 1.3.7 released 2020-08-05
|
6
35
|
|
7
36
|
* Parse 'every 12 hours at minute 50', gh-41
|
data/CREDITS.md
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
|
2
2
|
# fugit credits
|
3
3
|
|
4
|
-
*
|
4
|
+
* Solteszad https://github.com/solteszad gh-51, fix previous_time vs last day of month
|
5
|
+
* Niklas https://github.com/gr8bit gh-49, Fugit::Cron.parse('')
|
6
|
+
* Matsuda Akira https://github.com/amatsuda gh-46, warning suppression
|
7
|
+
* Honglooker https://github.com/honglooker gh-43, New York cron skip
|
8
|
+
* Jérôme Dalbert https://github.com/jeromedalbert gh-41, gh-42
|
5
9
|
* Danny Ben Shitrit https://github.com/DannyBen nat variants, gh-38
|
6
10
|
* Dominik Sander https://github.com/dsander #rough_frequency 0, gh-36
|
7
11
|
* Milovan Zogovic https://github.com/assembler Cron#match? vs TZ, gh-31
|
data/LICENSE.txt
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
|
2
|
-
Copyright (c) 2017-
|
2
|
+
Copyright (c) 2017-2021, John Mettraux, jmettraux+flor@gmail.com
|
3
3
|
|
4
4
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
5
|
of this software and associated documentation files (the "Software"), to deal
|
data/README.md
CHANGED
@@ -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)
|
@@ -111,12 +112,7 @@ module Fugit
|
|
111
112
|
end
|
112
113
|
|
113
114
|
def dec_month
|
114
|
-
|
115
|
-
#dec(@t.day * 24 * 3600 + @t.hour * 3600 + @t.min * 60 + @t.sec + 1)
|
116
|
-
#
|
117
|
-
# gh-18, so that '0 9 29 feb *' doesn't get skipped (over and over)
|
118
|
-
#
|
119
|
-
dec(@t.day * 24 * 3600 + 1)
|
115
|
+
dec((@t.day - 1) * 24 * 3600 + @t.hour * 3600 + @t.min * 60 + @t.sec + 1)
|
120
116
|
end
|
121
117
|
|
122
118
|
def dec_day
|
@@ -236,6 +232,8 @@ module Fugit
|
|
236
232
|
# the translation occurs in the timezone of
|
237
233
|
# this Fugit::Cron instance
|
238
234
|
|
235
|
+
zfrom = t.time.strftime('%z|%Z')
|
236
|
+
|
239
237
|
loop do
|
240
238
|
|
241
239
|
fail RuntimeError.new(
|
@@ -251,8 +249,14 @@ module Fugit
|
|
251
249
|
min_match?(t) || (t.inc_min; next)
|
252
250
|
sec_match?(t) || (t.inc_sec; next)
|
253
251
|
|
254
|
-
|
255
|
-
|
252
|
+
tt = t.time
|
253
|
+
st = tt.strftime('%F|%T')
|
254
|
+
zt = tt.strftime('%z|%Z')
|
255
|
+
#
|
256
|
+
if st == sfrom && zt != zfrom
|
257
|
+
from, sfrom, zfrom, ifrom = tt, st, zt, t.to_i
|
258
|
+
next
|
259
|
+
end
|
256
260
|
#
|
257
261
|
# when transitioning out of DST, this prevents #next_time from
|
258
262
|
# yielding the same literal time twice in a row, see gh-6
|
@@ -328,7 +332,7 @@ module Fugit
|
|
328
332
|
[ :seconds, 1, 60 ],
|
329
333
|
[ :minutes, 60, 60 ],
|
330
334
|
[ :hours, 3600, 24 ],
|
331
|
-
[ :days, 24 * 3600, 365 ] ]
|
335
|
+
[ :days, 24 * 3600, 365 ] ].freeze
|
332
336
|
|
333
337
|
def rough_frequency
|
334
338
|
|
@@ -487,7 +491,7 @@ module Fugit
|
|
487
491
|
|
488
492
|
sla = 1 if sla == nil
|
489
493
|
sta = min if sta == nil
|
490
|
-
edn = max if edn == nil
|
494
|
+
edn = max if edn == nil || edn < 0 && sta > 0
|
491
495
|
|
492
496
|
range(min, max, sta, edn, sla)
|
493
497
|
end
|
@@ -499,12 +503,10 @@ module Fugit
|
|
499
503
|
{ min: min, max: max, sta: sta, edn: edn, sla: sla }.inspect
|
500
504
|
) if (sta < 0 && edn > 0) || (edn < 0 && sta > 0)
|
501
505
|
|
502
|
-
#p({ min: min, max: max, sta: sta, edn: edn, sla: sla })
|
503
506
|
a = []
|
504
507
|
|
505
508
|
omin, omax = min, max
|
506
509
|
min, max = -max, -1 if sta < 0
|
507
|
-
#p({ min: min, max: max })
|
508
510
|
|
509
511
|
cur = sta
|
510
512
|
|
@@ -604,10 +606,18 @@ module Fugit
|
|
604
606
|
|
605
607
|
module Parser include Raabro
|
606
608
|
|
607
|
-
WEEKDAYS =
|
608
|
-
|
609
|
+
WEEKDAYS =
|
610
|
+
%w[ sunday monday tuesday wednesday thursday friday saturday ].freeze
|
611
|
+
|
612
|
+
WEEKDS =
|
613
|
+
WEEKDAYS.collect { |d| d[0, 3] }.freeze
|
614
|
+
DOW_REX =
|
615
|
+
/([0-7]|#{WEEKDS.join('|')})/i.freeze
|
609
616
|
|
610
|
-
MONTHS =
|
617
|
+
MONTHS =
|
618
|
+
%w[ - jan feb mar apr may jun jul aug sep oct nov dec ].freeze
|
619
|
+
MONTH_REX =
|
620
|
+
/(1[0-2]|0?[1-9]|#{MONTHS[1..-1].join('|')})/i.freeze
|
611
621
|
|
612
622
|
# piece parsers bottom to top
|
613
623
|
|
@@ -621,8 +631,8 @@ module Fugit
|
|
621
631
|
def mos(i); rex(:mos, i, /[0-5]?\d/); end # min or sec
|
622
632
|
def hou(i); rex(:hou, i, /(2[0-4]|[01]?[0-9])/); end
|
623
633
|
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,
|
634
|
+
def mon(i); rex(:mon, i, MONTH_REX); end
|
635
|
+
def dow(i); rex(:dow, i, DOW_REX); end
|
626
636
|
|
627
637
|
def dow_hash(i); rex(:hash, i, /#(-?[1-5]|last|l)/i); end
|
628
638
|
|
@@ -765,8 +775,12 @@ module Fugit
|
|
765
775
|
|
766
776
|
def rewrite_cron(t)
|
767
777
|
|
768
|
-
|
778
|
+
st = t
|
769
779
|
.sublookup(nil) # go to :ccron or :scron
|
780
|
+
|
781
|
+
return nil unless st
|
782
|
+
|
783
|
+
hcron = st
|
770
784
|
.subgather(nil) # list min, hou, mon, ...
|
771
785
|
.inject({}) { |h, tt|
|
772
786
|
h[tt.name] = tt.name == :tz ? rewrite_tz(tt) : rewrite_entry(tt)
|
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,504 +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
|
120
|
+
def _space(i); rex(nil, i, /[ \t]+/); end
|
121
|
+
def _sep(i); rex(nil, i, /([ \t]+|[ \t]*,[ \t]*)/); end
|
77
122
|
|
78
|
-
|
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]
|
123
|
+
def count(i); rex(:count, i, /\d+/); end
|
85
124
|
|
86
|
-
|
125
|
+
def omonthday(i)
|
126
|
+
rex(:omonthday, i, OMONTHDAY_REX)
|
127
|
+
end
|
128
|
+
def monthday(i)
|
129
|
+
rex(:monthday, i, MONTHDAY_REX)
|
130
|
+
end
|
131
|
+
def weekday(i)
|
132
|
+
rex(:weekday, i, WEEKDAY_REX)
|
87
133
|
end
|
88
134
|
|
89
|
-
def
|
90
|
-
|
91
|
-
def parse_interval_elt(e, opts, h)
|
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
|
92
137
|
|
93
|
-
|
138
|
+
def weekdays(i); jseq(:weekdays, i, :weekday, :_and_or_or_or_comma); end
|
94
139
|
|
95
|
-
|
96
|
-
when 's', 'sec', 'second', 'seconds'
|
97
|
-
h[:sec] = eone(e)
|
98
|
-
when 'm', 'min', 'mins', 'minute', 'minutes'
|
99
|
-
h[:hms] ||= [ [ '*', eone(e) ] ]
|
100
|
-
when 'h', 'hour', 'hours'
|
101
|
-
hms = h[:hms]
|
102
|
-
if hms && hms.size == 1 && hms.first.first == '*'
|
103
|
-
hms.first[0] = eone(e)
|
104
|
-
elsif ! hms
|
105
|
-
h[:hms] = [ [ eone(e), 0 ] ]
|
106
|
-
end
|
107
|
-
when 'd', 'day', 'days'
|
108
|
-
h[:dom] = "*/#{e1}" if e1 > 1
|
109
|
-
h[:hms] ||= [ [ 0, 0 ] ]
|
110
|
-
when 'w', 'week', 'weeks'
|
111
|
-
h[:_fail] = "cannot have crons for \"every #{e1} weeks\"" if e1 > 1
|
112
|
-
h[:hms] ||= [ [ 0, 0 ] ]
|
113
|
-
h[:dow] ||= 0
|
114
|
-
when 'M', 'month', 'months'
|
115
|
-
h[:_fail] = "cannot have crons for \"every #{e1} months\"" if e1 > 12
|
116
|
-
h[:hms] ||= [ [ 0, 0 ] ]
|
117
|
-
h[:dom] = 1
|
118
|
-
h[:mon] = eone(e)
|
119
|
-
when 'Y', 'y', 'year', 'years'
|
120
|
-
h[:_fail] = "cannot have crons for \"every #{e1} years\"" if e1 > 1
|
121
|
-
h[:hms] ||= [ [ 0, 0 ] ]
|
122
|
-
h[:dom] = 1
|
123
|
-
h[:mon] = 1
|
124
|
-
end
|
125
|
-
end
|
140
|
+
def on_the(i); seq(nil, i, :_the, :omonthdays); end
|
126
141
|
|
127
|
-
def
|
142
|
+
def _minute(i); rex(nil, i, /[ \t]*minute[ \t]+/i) end
|
128
143
|
|
129
|
-
|
130
|
-
|
144
|
+
def _dmin(i)
|
145
|
+
rex(:dmin, i, /[0-5]?[0-9]/)
|
131
146
|
end
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
h[:hms] ||= [ [ 0, 0 ] ]
|
136
|
-
h[:dow] = e[1] == e[2] ? e[1] : "#{e[1]}-#{e[2]}"
|
147
|
+
def and_dmin(i)
|
148
|
+
seq(nil, i, :_and_or_or_or_comma, :_minute, '?', :_dmin)
|
137
149
|
end
|
138
150
|
|
139
|
-
def
|
140
|
-
|
141
|
-
h[:dom] = e[1..-1].join(',')
|
151
|
+
def on_minutes(i)
|
152
|
+
seq(:on_minutes, i, :_minute, :_dmin, :and_dmin, '*')
|
142
153
|
end
|
143
154
|
|
144
|
-
def
|
145
|
-
|
146
|
-
(h[:hms] ||= []).concat(e[1])
|
147
|
-
|
148
|
-
l = h[:hms].last
|
149
|
-
h[:sec] = l.pop if l.size > 2
|
155
|
+
def on_thex(i);
|
156
|
+
rex(:on_thex, i, /[ \t]*the[ \t]+(hour|minute)[ \t]*/i);
|
150
157
|
end
|
151
158
|
|
152
|
-
def
|
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
|
153
162
|
|
154
|
-
|
155
|
-
|
156
|
-
h[:hms] = [ e1[1] ]
|
157
|
-
|
158
|
-
l = h[:hms].last
|
159
|
-
h[:sec] = l.pop if l.size > 2
|
163
|
+
def on_object(i)
|
164
|
+
alt(nil, i, :on_days, :on_weekdays, :on_minutes, :on_thes, :on_thex)
|
160
165
|
end
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
h[:tz] = e[1]
|
166
|
+
def on_objects(i)
|
167
|
+
jseq(nil, i, :on_object, :_and)
|
165
168
|
end
|
166
|
-
end
|
167
|
-
|
168
|
-
module Parser include Raabro
|
169
|
-
|
170
|
-
NUMS = %w[
|
171
|
-
zero one two three four five six seven eight nine ten eleven twelve ]
|
172
169
|
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
'
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
'twenty' => 20, 'twenty-five' => 25,
|
183
|
-
'thirty' => 30, 'thirty-five' => 35,
|
184
|
-
'fourty' => 40, 'fourty-five' => 45,
|
185
|
-
'fifty' => 50, 'fifty-five' => 55 }
|
186
|
-
|
187
|
-
oh = {
|
188
|
-
'1st' => 1, '2nd' => 2, '3rd' => 3, '21st' => 21, '22nd' => 22,
|
189
|
-
'23rd' => 23, '31st' => 31 }
|
190
|
-
%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 ]
|
191
|
-
.each { |i| oh["#{i}th"] = i.to_i }
|
192
|
-
%w[
|
193
|
-
first second third fourth fifth sixth seventh eighth ninth tenth
|
194
|
-
eleventh twelfth thirteenth fourteenth fifteenth sixteenth seventeenth
|
195
|
-
eighteenth nineteenth twentieth twenty-first twenty-second twenty-third
|
196
|
-
twenty-fourth twenty-fifth twenty-fifth twenty-sixth twenty-seventh
|
197
|
-
twenty-eighth twenty-ninth thirtieth thirty-first ]
|
198
|
-
.each_with_index { |e, i| oh[e] = i + 1 }
|
199
|
-
ORDINALS = oh
|
200
|
-
|
201
|
-
# piece parsers bottom to top
|
202
|
-
|
203
|
-
def _from(i); rex(nil, i, /\s*from\s+/i); end
|
204
|
-
def _every(i); rex(nil, i, /\s*(every)\s+/i); end
|
205
|
-
def _at(i); rex(nil, i, /\s*at\s+/i); end
|
206
|
-
def _in(i); rex(nil, i, /\s*(in|on)\s+/i); end
|
207
|
-
def _to(i); rex(nil, i, /\s*to\s+/i); end
|
208
|
-
def _dash(i); rex(nil, i, /-\s*/i); end
|
209
|
-
def _and(i); rex(nil, i, /\s*and\s+/i); end
|
210
|
-
def _on(i); rex(nil, i, /\s*on\s+/i); end
|
211
|
-
|
212
|
-
def _and_or_comma(i)
|
213
|
-
rex(nil, i, /\s*(,?\s*and\s|,?\s*or\s|,)\s*/i)
|
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)
|
214
179
|
end
|
215
|
-
|
216
|
-
|
180
|
+
|
181
|
+
def city_tz(i)
|
182
|
+
rex(nil, i, /[A-Z][a-zA-Z0-9+\-]+(\/[A-Z][a-zA-Z0-9+\-_]+){0,2}/)
|
217
183
|
end
|
218
|
-
def
|
219
|
-
rex(nil, i,
|
184
|
+
def named_tz(i)
|
185
|
+
rex(nil, i, /Z|UTC/)
|
220
186
|
end
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
def tz_name(i)
|
225
|
-
rex(nil, i,
|
226
|
-
/\s*[A-Z][a-zA-Z0-9+\-]+(\/[A-Z][a-zA-Z0-9+\-_]+){0,2}(\s+|$)/)
|
187
|
+
def delta_tz(i)
|
188
|
+
rex(nil, i, /[-+]([01][0-9]|2[0-4])(:?(00|15|30|45))?/)
|
227
189
|
end
|
228
|
-
def
|
229
|
-
|
230
|
-
/\s*[-+]([01][0-9]|2[0-4]):?(00|15|30|45)(\s+|$)/)
|
190
|
+
def tz(i)
|
191
|
+
alt(:tz, i, :city_tz, :named_tz, :delta_tz)
|
231
192
|
end
|
232
193
|
def tzone(i)
|
233
|
-
|
194
|
+
seq(nil, i, :_in_or_on, '?', :tz)
|
234
195
|
end
|
235
196
|
|
236
|
-
def
|
237
|
-
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)
|
238
201
|
end
|
239
202
|
|
240
|
-
def
|
241
|
-
rex(:
|
242
|
-
end
|
243
|
-
def named_digit(i)
|
244
|
-
seq(:named_digit, i, :dname, :integer)
|
203
|
+
def ampm(i)
|
204
|
+
rex(:ampm, i, /[ \t]*(am|pm)/i)
|
245
205
|
end
|
246
|
-
def
|
247
|
-
|
206
|
+
def dark(i)
|
207
|
+
rex(:dark, i, /[ \t]*dark/i)
|
248
208
|
end
|
249
209
|
|
250
|
-
def
|
251
|
-
|
210
|
+
def simple_h(i)
|
211
|
+
rex(:simple_h, i, /#{(0..24).to_a.reverse.join('|')}/)
|
252
212
|
end
|
253
|
-
|
254
|
-
|
255
|
-
rex(:nminute, i, /(#{NMINUTES.keys.join('|')})\s*/i)
|
213
|
+
def simple_hour(i)
|
214
|
+
seq(:simple_hour, i, :simple_h, :ampm, '?')
|
256
215
|
end
|
257
|
-
|
258
|
-
|
216
|
+
|
217
|
+
def named_m(i)
|
218
|
+
rex(:named_m, i, NAMED_M_REX)
|
259
219
|
end
|
260
|
-
def
|
261
|
-
seq(
|
220
|
+
def named_min(i)
|
221
|
+
seq(nil, i, :_space, :named_m)
|
262
222
|
end
|
263
223
|
|
224
|
+
def named_h(i)
|
225
|
+
rex(:named_h, i, NAMED_H_REX)
|
226
|
+
end
|
264
227
|
def named_hour(i)
|
265
|
-
|
228
|
+
seq(:named_hour, i, :named_h, :dark, '?', :named_min, '?', :ampm, '?')
|
266
229
|
end
|
267
230
|
|
268
|
-
def
|
269
|
-
rex(:shour, i, /(2[0-4]|[01]?[0-9])/)
|
270
|
-
end
|
271
|
-
def simple_hour(i)
|
272
|
-
seq(:simple_hour, i, :shour, :am_pm, '?')
|
273
|
-
end
|
231
|
+
def _point(i); rex(:point, i, POINT_REX); end
|
274
232
|
|
275
|
-
def
|
276
|
-
|
233
|
+
def counts(i)
|
234
|
+
jseq(nil, i, :count, :_and_or_or_or_comma)
|
277
235
|
end
|
278
236
|
|
237
|
+
def at_p(i)
|
238
|
+
seq(:at_p, i, :_point, :counts)
|
239
|
+
end
|
279
240
|
def at_point(i)
|
280
|
-
|
281
|
-
:digital_hour, :simple_hour, :named_hour, :numeral_hour,
|
282
|
-
:named_digits)
|
241
|
+
jseq(nil, i, :at_p, :_and_or_or)
|
283
242
|
end
|
284
243
|
|
285
|
-
|
286
|
-
|
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)
|
287
252
|
end
|
288
|
-
|
289
|
-
|
290
|
-
seq(nil, i, :_and_or_comma, :at_point)
|
253
|
+
def at_objects(i)
|
254
|
+
jseq(nil, i, :at_object, :_and_or_or_or_comma)
|
291
255
|
end
|
292
256
|
|
293
|
-
def
|
294
|
-
|
295
|
-
/(
|
296
|
-
y(ears?)?|months?|w(eeks?)?|d(ays?)?|
|
297
|
-
h(ours?)?|m(in(ute)?s?)?|s(ec(ond)?s?)?
|
298
|
-
)(\s+|$)/ix)
|
257
|
+
def at(i)
|
258
|
+
seq(:at, i, :_at, '?', :at_objects)
|
299
259
|
end
|
300
260
|
|
301
|
-
def
|
302
|
-
rex(:
|
303
|
-
/(year|month|week|day|hour|min(ute)?|sec(ond)?)(\s+|$)/i)
|
261
|
+
def interval(i)
|
262
|
+
rex(:interval, i, INTERVAL_REX)
|
304
263
|
end
|
305
|
-
|
306
|
-
|
264
|
+
|
265
|
+
# every day
|
266
|
+
# every 1 minute
|
267
|
+
def every_interval(i)
|
268
|
+
seq(:every_interval, i, :count, '?', :interval)
|
307
269
|
end
|
308
270
|
|
309
|
-
def
|
310
|
-
rex(:
|
271
|
+
def every_single_interval(i)
|
272
|
+
rex(:every_single_interval, i, /(1[ \t]+)?(week|year)/)
|
311
273
|
end
|
312
274
|
|
313
|
-
def
|
314
|
-
|
275
|
+
def to_weekday(i)
|
276
|
+
seq(:to_weekday, i, :weekday, :_to_or_dash, :weekday)
|
277
|
+
end
|
315
278
|
|
316
|
-
def
|
317
|
-
|
279
|
+
def weekday_range(i)
|
280
|
+
alt(nil, i, :to_weekday, :weekdays)
|
318
281
|
end
|
319
|
-
|
320
|
-
|
282
|
+
|
283
|
+
def to_omonthday(i)
|
284
|
+
seq(:to_omonthday, i,
|
285
|
+
:_the, '?', :omonthday, :_to, :_the, '?', :omonthday)
|
321
286
|
end
|
322
|
-
|
323
|
-
|
287
|
+
|
288
|
+
def to_hour(i)
|
289
|
+
seq(:to_hour, i, :at_object, :_to, :at_object)
|
324
290
|
end
|
325
291
|
|
326
|
-
def
|
327
|
-
|
292
|
+
def from_object(i)
|
293
|
+
alt(nil, i, :to_weekday, :to_omonthday, :to_hour)
|
328
294
|
end
|
329
|
-
def
|
330
|
-
|
295
|
+
def from_objects(i)
|
296
|
+
jseq(nil, i, :from_object, :_and_or_or)
|
331
297
|
end
|
332
|
-
def
|
333
|
-
|
298
|
+
def from(i)
|
299
|
+
seq(nil, i, :_from, '?', :from_objects)
|
334
300
|
end
|
335
301
|
|
336
|
-
|
337
|
-
|
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)
|
338
307
|
end
|
339
308
|
|
340
|
-
def
|
341
|
-
|
309
|
+
def otm(i)
|
310
|
+
rex(nil, i, /[ \t]+of the month/)
|
342
311
|
end
|
343
|
-
|
344
|
-
|
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)
|
345
320
|
end
|
346
|
-
|
347
|
-
|
321
|
+
|
322
|
+
def every_named(i)
|
323
|
+
rex(:every_named, i, /weekday/i)
|
348
324
|
end
|
349
325
|
|
350
|
-
def
|
351
|
-
|
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)
|
352
331
|
end
|
353
|
-
def
|
354
|
-
|
332
|
+
def every_objects(i)
|
333
|
+
jseq(nil, i, :every_object, :_and_or_or)
|
355
334
|
end
|
356
|
-
|
357
|
-
|
335
|
+
|
336
|
+
def every(i)
|
337
|
+
seq(:every, i, :_every, :every_objects)
|
358
338
|
end
|
359
339
|
|
360
|
-
def
|
361
|
-
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)
|
362
345
|
end
|
363
346
|
|
364
|
-
|
365
|
-
|
347
|
+
#
|
348
|
+
# rewrite parsed tree ###################################################
|
349
|
+
|
350
|
+
def slot(key, data0, data1=nil, opts=nil)
|
351
|
+
Slot.new(key, data0, data1, opts)
|
366
352
|
end
|
367
353
|
|
368
|
-
def
|
369
|
-
|
354
|
+
def _rewrite_subs(t, key=nil)
|
355
|
+
t.subgather(key).collect { |ct| rewrite(ct) }
|
370
356
|
end
|
371
|
-
def
|
372
|
-
|
357
|
+
def _rewrite_sub(t, key=nil)
|
358
|
+
st = t.sublookup(key)
|
359
|
+
st ? rewrite(st) : nil
|
373
360
|
end
|
374
361
|
|
375
|
-
def
|
376
|
-
|
362
|
+
def rewrite_dmin(t)
|
363
|
+
t.strinp
|
377
364
|
end
|
378
|
-
|
379
|
-
|
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)
|
380
371
|
end
|
381
|
-
|
382
|
-
|
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
|
383
380
|
end
|
384
|
-
|
385
|
-
|
381
|
+
|
382
|
+
def rewrite_on_thes(t)
|
383
|
+
_rewrite_subs(t, :omonthday)
|
386
384
|
end
|
387
|
-
def
|
388
|
-
|
385
|
+
def rewrite_on_days(t)
|
386
|
+
_rewrite_subs(t, :monthday)
|
389
387
|
end
|
390
388
|
|
391
|
-
def
|
392
|
-
|
393
|
-
end
|
394
|
-
def at_every(i)
|
395
|
-
seq(nil, i, :at, :every, :tz, '?')
|
389
|
+
def rewrite_on(t)
|
390
|
+
_rewrite_subs(t)
|
396
391
|
end
|
397
392
|
|
398
|
-
def
|
399
|
-
|
393
|
+
def rewrite_monthday(t)
|
394
|
+
slot(:monthday, t.string.to_i)
|
400
395
|
end
|
401
396
|
|
402
|
-
def
|
403
|
-
|
397
|
+
def rewrite_omonthday(t)
|
398
|
+
slot(:monthday, OMONTHDAYS[t.string.downcase])
|
404
399
|
end
|
405
|
-
|
406
|
-
|
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
|
407
412
|
end
|
408
|
-
|
409
|
-
|
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
|
410
420
|
end
|
411
421
|
|
412
|
-
def
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
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
|
417
440
|
end
|
418
441
|
|
419
|
-
|
442
|
+
def rewrite_every_named(t)
|
420
443
|
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
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
|
426
449
|
end
|
427
|
-
|
428
|
-
|
450
|
+
|
451
|
+
def rewrite_tz(t)
|
452
|
+
slot(:tz, t.string)
|
429
453
|
end
|
430
|
-
|
431
|
-
|
454
|
+
|
455
|
+
def rewrite_weekday(t)
|
456
|
+
Fugit::Cron::Parser::WEEKDS.index(t.string[0, 3].downcase)
|
432
457
|
end
|
433
458
|
|
434
|
-
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
|
435
464
|
|
436
|
-
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
|
437
470
|
|
438
|
-
|
471
|
+
def rewrite_to_omonthday(t)
|
472
|
+
md0, md1 = _rewrite_subs(t, :omonthday).collect(&:_data0)
|
473
|
+
slot(:monthday, "#{md0}-#{md1}")
|
439
474
|
end
|
440
475
|
|
441
|
-
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
|
442
482
|
|
443
|
-
|
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)
|
444
488
|
end
|
445
489
|
|
446
|
-
def
|
490
|
+
def rewrite_named_hour(t)
|
447
491
|
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
end
|
492
|
+
ht = t.sublookup(:named_h)
|
493
|
+
mt = t.sublookup(:named_m)
|
494
|
+
apt = t.sublookup(:ampm)
|
452
495
|
|
453
|
-
|
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 ]
|
454
502
|
|
455
|
-
|
503
|
+
h += 12 if h < 13 && apt && apt.strinpd == 'pm'
|
456
504
|
|
457
|
-
|
458
|
-
when /^s/ then [ '*', '*', i ]
|
459
|
-
when /^m/ then [ '*', i ]
|
460
|
-
end
|
505
|
+
slot(:hm, h, m)
|
461
506
|
end
|
462
507
|
|
463
|
-
def
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
[ 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)
|
472
516
|
end
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
v += 12 if vs[1] == 'pm'
|
477
|
-
[ v, 0 ]
|
517
|
+
|
518
|
+
def rewrite_at(t)
|
519
|
+
_rewrite_subs(t)
|
478
520
|
end
|
479
|
-
|
480
|
-
|
481
|
-
|
521
|
+
|
522
|
+
def rewrite_every(t)
|
523
|
+
_rewrite_sub(t)
|
482
524
|
end
|
483
525
|
|
484
|
-
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
|
485
531
|
|
486
|
-
|
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))
|
487
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
|
581
|
+
end
|
582
|
+
end
|
488
583
|
|
489
|
-
|
584
|
+
class SlotGroup
|
490
585
|
|
491
|
-
def
|
586
|
+
def initialize(slots)
|
492
587
|
|
493
|
-
#
|
494
|
-
|
495
|
-
|
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
|
496
609
|
end
|
497
610
|
|
498
|
-
|
611
|
+
def to_crons(opts)
|
499
612
|
|
500
|
-
|
501
|
-
def rewrite_dow_list(t); [ :dow_list, *_rewrite_children(t) ]; end
|
613
|
+
multi = opts.has_key?(:multi) ? opts[:multi] : false
|
502
614
|
|
503
|
-
|
615
|
+
hms = determine_hms
|
504
616
|
|
505
|
-
|
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
|
506
625
|
end
|
507
626
|
|
508
|
-
|
627
|
+
protected
|
509
628
|
|
510
|
-
|
629
|
+
def make_slot(key, data0, data1=nil)
|
511
630
|
|
512
|
-
|
631
|
+
Fugit::Nat::Slot.new(key, data0, data1)
|
513
632
|
end
|
514
633
|
|
515
|
-
|
516
|
-
alias rewrite_at _rewrite_multiple
|
634
|
+
def determine_hms
|
517
635
|
|
518
|
-
|
519
|
-
alias rewrite_every _rewrite_child
|
636
|
+
return [ [ [ '*' ], [ '*' ] ] ] if @hms.empty?
|
520
637
|
|
521
|
-
|
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
|
522
676
|
|
523
|
-
|
524
|
-
|
677
|
+
def slot(key, default)
|
678
|
+
s = @slots[key]
|
679
|
+
s ? s.data0 : [ default ]
|
525
680
|
end
|
526
681
|
end
|
527
682
|
end
|