fugit 1.3.8 → 1.4.3
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 -0
- data/LICENSE.txt +1 -1
- data/README.md +25 -5
- data/fugit.gemspec +2 -2
- data/lib/fugit.rb +2 -1
- data/lib/fugit/at.rb +1 -0
- data/lib/fugit/cron.rb +37 -25
- data/lib/fugit/duration.rb +7 -7
- data/lib/fugit/misc.rb +4 -0
- data/lib/fugit/nat.rb +504 -355
- 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: 30b1280ebc1d629dbbdcba1b02718a535b0fdec70c9d690f3d5208181ffe4577
|
4
|
+
data.tar.gz: 918af2ece3f4c94eb1145b60da397a43ee574023831bae4b7c0b95e6a97e389b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 37b8ad132efdd2b9be99fb9fd1ab6f505024fd309865cf007d9ade6df20207a4165e49679a4113614010f7436ddbf28e2fc6ecbdbbedc713bb3e4def3f2b03ca
|
7
|
+
data.tar.gz: 4fc6e3a8f080b698151cc6dadfb1d2869cb736a4e6c60ce7fe6c26a598f83b693d32f37edd1f42cd20dd0efdb47dd04cde097fca07d0bf5fe6eb9412b72ef0cf
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,35 @@
|
|
2
2
|
# CHANGELOG.md
|
3
3
|
|
4
4
|
|
5
|
+
## fugit 1.4.3 released 2021-03-23
|
6
|
+
|
7
|
+
* Fix entering DST issue, gh-53
|
8
|
+
|
9
|
+
|
10
|
+
## fugit 1.4.2 released 2021-01-12
|
11
|
+
|
12
|
+
* Fix Fugit::Cron.previous_time vs last day of month, gh-51
|
13
|
+
* Let Fugit::Cron.parse('') return nil, gh-49
|
14
|
+
|
15
|
+
|
16
|
+
## fugit 1.4.1 released 2020-11-25
|
17
|
+
|
18
|
+
* Suppress warning, gh-46, thanks @amatsuda
|
19
|
+
|
20
|
+
|
21
|
+
## fugit 1.4.0 released 2020-10-27
|
22
|
+
|
23
|
+
* Ensure cron accepts "25-L" for monthday, gh-45
|
24
|
+
* Allow for "every weekday 8am to 5pm", gh-44
|
25
|
+
* Allow "every day from the 25th to the last", gh-45
|
26
|
+
* Rework nat parser
|
27
|
+
|
28
|
+
|
29
|
+
## fugit 1.3.9 released 2020-09-17
|
30
|
+
|
31
|
+
* Prevent "New York skip", gh-43, thanks @honglooker
|
32
|
+
|
33
|
+
|
5
34
|
## fugit 1.3.8 released 2020-08-06
|
6
35
|
|
7
36
|
* Parse 'every day at 8:30' and ' at 8:30 pm', gh-42
|
data/CREDITS.md
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
|
2
2
|
# fugit credits
|
3
3
|
|
4
|
+
* Andy Pfister https://github.com/andyundso gh-53, entering DST
|
5
|
+
* Solteszad https://github.com/solteszad gh-51, fix previous_time vs last day of month
|
6
|
+
* Niklas https://github.com/gr8bit gh-49, Fugit::Cron.parse('')
|
7
|
+
* Matsuda Akira https://github.com/amatsuda gh-46, warning suppression
|
8
|
+
* Honglooker https://github.com/honglooker gh-43, New York cron skip
|
4
9
|
* Jérôme Dalbert https://github.com/jeromedalbert gh-41, gh-42
|
5
10
|
* Danny Ben Shitrit https://github.com/DannyBen nat variants, gh-38
|
6
11
|
* Dominik Sander https://github.com/dsander #rough_frequency 0, gh-36
|
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
@@ -1,7 +1,7 @@
|
|
1
1
|
|
2
2
|
# fugit
|
3
3
|
|
4
|
-
[![
|
4
|
+
[![tests](https://github.com/floraison/fugit/workflows/test/badge.svg)](https://github.com/floraison/fugit/actions)
|
5
5
|
[![Gem Version](https://badge.fury.io/rb/fugit.svg)](http://badge.fury.io/rb/fugit)
|
6
6
|
[![Join the chat at https://gitter.im/floraison/fugit](https://badges.gitter.im/floraison/fugit.svg)](https://gitter.im/floraison/fugit?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
7
7
|
|
@@ -321,19 +321,39 @@ 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
|
+
|
353
|
+
### Nat Midnight
|
354
|
+
|
355
|
+
`"Every day at midnight"` is supported, but `"Every monday at midnight"` will be interpreted (as of Fugit <= 1.4.x) as `"Every monday at 00:00"`. Sorry about that.
|
356
|
+
|
337
357
|
|
338
358
|
## LICENSE
|
339
359
|
|
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)
|
@@ -78,10 +79,7 @@ module Fugit
|
|
78
79
|
%w[ year month day wday hour min sec wday_in_month rweek rday ]
|
79
80
|
.collect(&:to_sym).each { |k| define_method(k) { @t.send(k) } }
|
80
81
|
|
81
|
-
def inc(i)
|
82
|
-
@t = @t + i
|
83
|
-
self
|
84
|
-
end
|
82
|
+
def inc(i); @t = @t + i; self; end
|
85
83
|
def dec(i); inc(-i); end
|
86
84
|
|
87
85
|
def inc_month
|
@@ -94,6 +92,7 @@ module Fugit
|
|
94
92
|
|
95
93
|
def inc_day
|
96
94
|
inc((24 - @t.hour) * 3600 - @t.min * 60 - @t.sec)
|
95
|
+
inc( - @t.hour * 3600) if @t.hour != 0 # compensate for entering DST
|
97
96
|
end
|
98
97
|
def inc_hour
|
99
98
|
inc((60 - @t.min) * 60 - @t.sec)
|
@@ -111,12 +110,7 @@ module Fugit
|
|
111
110
|
end
|
112
111
|
|
113
112
|
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)
|
113
|
+
dec((@t.day - 1) * DAY_S + @t.hour * 3600 + @t.min * 60 + @t.sec + 1)
|
120
114
|
end
|
121
115
|
|
122
116
|
def dec_day
|
@@ -236,6 +230,8 @@ module Fugit
|
|
236
230
|
# the translation occurs in the timezone of
|
237
231
|
# this Fugit::Cron instance
|
238
232
|
|
233
|
+
zfrom = t.time.strftime('%z|%Z')
|
234
|
+
|
239
235
|
loop do
|
240
236
|
|
241
237
|
fail RuntimeError.new(
|
@@ -251,8 +247,14 @@ module Fugit
|
|
251
247
|
min_match?(t) || (t.inc_min; next)
|
252
248
|
sec_match?(t) || (t.inc_sec; next)
|
253
249
|
|
254
|
-
|
255
|
-
|
250
|
+
tt = t.time
|
251
|
+
st = tt.strftime('%F|%T')
|
252
|
+
zt = tt.strftime('%z|%Z')
|
253
|
+
#
|
254
|
+
if st == sfrom && zt != zfrom
|
255
|
+
from, sfrom, zfrom, ifrom = tt, st, zt, t.to_i
|
256
|
+
next
|
257
|
+
end
|
256
258
|
#
|
257
259
|
# when transitioning out of DST, this prevents #next_time from
|
258
260
|
# yielding the same literal time twice in a row, see gh-6
|
@@ -328,7 +330,7 @@ module Fugit
|
|
328
330
|
[ :seconds, 1, 60 ],
|
329
331
|
[ :minutes, 60, 60 ],
|
330
332
|
[ :hours, 3600, 24 ],
|
331
|
-
[ :days,
|
333
|
+
[ :days, DAY_S, 365 ] ].freeze
|
332
334
|
|
333
335
|
def rough_frequency
|
334
336
|
|
@@ -370,7 +372,7 @@ module Fugit
|
|
370
372
|
|
371
373
|
@delta_min = deltas.min; @delta_max = deltas.max
|
372
374
|
@occurrences = deltas.size
|
373
|
-
@span_years = span /
|
375
|
+
@span_years = span / YEAR_S
|
374
376
|
@yearly_occurrences = @occurrences.to_f / @span_years
|
375
377
|
end
|
376
378
|
|
@@ -487,7 +489,7 @@ module Fugit
|
|
487
489
|
|
488
490
|
sla = 1 if sla == nil
|
489
491
|
sta = min if sta == nil
|
490
|
-
edn = max if edn == nil
|
492
|
+
edn = max if edn == nil || edn < 0 && sta > 0
|
491
493
|
|
492
494
|
range(min, max, sta, edn, sla)
|
493
495
|
end
|
@@ -499,12 +501,10 @@ module Fugit
|
|
499
501
|
{ min: min, max: max, sta: sta, edn: edn, sla: sla }.inspect
|
500
502
|
) if (sta < 0 && edn > 0) || (edn < 0 && sta > 0)
|
501
503
|
|
502
|
-
#p({ min: min, max: max, sta: sta, edn: edn, sla: sla })
|
503
504
|
a = []
|
504
505
|
|
505
506
|
omin, omax = min, max
|
506
507
|
min, max = -max, -1 if sta < 0
|
507
|
-
#p({ min: min, max: max })
|
508
508
|
|
509
509
|
cur = sta
|
510
510
|
|
@@ -604,10 +604,18 @@ module Fugit
|
|
604
604
|
|
605
605
|
module Parser include Raabro
|
606
606
|
|
607
|
-
WEEKDAYS =
|
608
|
-
|
607
|
+
WEEKDAYS =
|
608
|
+
%w[ sunday monday tuesday wednesday thursday friday saturday ].freeze
|
609
|
+
|
610
|
+
WEEKDS =
|
611
|
+
WEEKDAYS.collect { |d| d[0, 3] }.freeze
|
612
|
+
DOW_REX =
|
613
|
+
/([0-7]|#{WEEKDS.join('|')})/i.freeze
|
609
614
|
|
610
|
-
MONTHS =
|
615
|
+
MONTHS =
|
616
|
+
%w[ - jan feb mar apr may jun jul aug sep oct nov dec ].freeze
|
617
|
+
MONTH_REX =
|
618
|
+
/(1[0-2]|0?[1-9]|#{MONTHS[1..-1].join('|')})/i.freeze
|
611
619
|
|
612
620
|
# piece parsers bottom to top
|
613
621
|
|
@@ -621,8 +629,8 @@ module Fugit
|
|
621
629
|
def mos(i); rex(:mos, i, /[0-5]?\d/); end # min or sec
|
622
630
|
def hou(i); rex(:hou, i, /(2[0-4]|[01]?[0-9])/); end
|
623
631
|
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,
|
632
|
+
def mon(i); rex(:mon, i, MONTH_REX); end
|
633
|
+
def dow(i); rex(:dow, i, DOW_REX); end
|
626
634
|
|
627
635
|
def dow_hash(i); rex(:hash, i, /#(-?[1-5]|last|l)/i); end
|
628
636
|
|
@@ -765,8 +773,12 @@ module Fugit
|
|
765
773
|
|
766
774
|
def rewrite_cron(t)
|
767
775
|
|
768
|
-
|
776
|
+
st = t
|
769
777
|
.sublookup(nil) # go to :ccron or :scron
|
778
|
+
|
779
|
+
return nil unless st
|
780
|
+
|
781
|
+
hcron = st
|
770
782
|
.subgather(nil) # list min, hou, mon, ...
|
771
783
|
.inject({}) { |h, tt|
|
772
784
|
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
|
|
@@ -60,16 +61,15 @@ module Fugit
|
|
60
61
|
end
|
61
62
|
|
62
63
|
KEYS = {
|
63
|
-
yea: { a: 'Y', r: 'y', i: 'Y', s:
|
64
|
-
mon: { a: 'M', r: 'M', i: 'M', s: 30 *
|
65
|
-
wee: { a: 'W', r: 'w', i: 'W', s: 7 *
|
66
|
-
day: { a: 'D', r: 'd', i: 'D', s:
|
64
|
+
yea: { a: 'Y', r: 'y', i: 'Y', s: YEAR_S, x: 0, l: 'year' },
|
65
|
+
mon: { a: 'M', r: 'M', i: 'M', s: 30 * DAY_S, x: 1, l: 'month' },
|
66
|
+
wee: { a: 'W', r: 'w', i: 'W', s: 7 * DAY_S, I: true, l: 'week' },
|
67
|
+
day: { a: 'D', r: 'd', i: 'D', s: DAY_S, 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,510 +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
|
153
|
-
|
154
|
-
|
155
|
-
h[:dow] = e1[0]
|
156
|
-
h[:hms] = [ e1[1] ]
|
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
|
157
162
|
|
158
|
-
|
159
|
-
|
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
|
-
|
173
|
-
WEEKDAYS =
|
174
|
-
Fugit::Cron::Parser::WEEKDS + Fugit::Cron::Parser::WEEKDAYS
|
175
169
|
|
176
|
-
|
177
|
-
'
|
178
|
-
'
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
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(:
|
203
|
+
def ampm(i)
|
204
|
+
rex(:ampm, i, /[ \t]*(am|pm)/i)
|
242
205
|
end
|
243
|
-
def
|
244
|
-
|
245
|
-
end
|
246
|
-
def named_digits(i)
|
247
|
-
seq(nil, i, :named_digit, '+', :and_named_digits, '*')
|
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
|
-
|
277
|
-
def dig_hour(i); alt(nil, i, :dig_hour_a, :dig_hour_b); end
|
278
|
-
#
|
279
|
-
def digital_hour(i)
|
280
|
-
seq(:digital_hour, i, :dig_hour, :am_pm, '?')
|
233
|
+
def counts(i)
|
234
|
+
jseq(nil, i, :count, :_and_or_or_or_comma)
|
281
235
|
end
|
282
236
|
|
237
|
+
def at_p(i)
|
238
|
+
seq(:at_p, i, :_point, :counts)
|
239
|
+
end
|
283
240
|
def at_point(i)
|
284
|
-
|
285
|
-
:digital_hour, :simple_hour, :named_hour, :numeral_hour,
|
286
|
-
:named_digits)
|
241
|
+
jseq(nil, i, :at_p, :_and_or_or)
|
287
242
|
end
|
288
243
|
|
289
|
-
|
290
|
-
|
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)
|
291
252
|
end
|
292
|
-
|
293
|
-
|
294
|
-
seq(nil, i, :_and_or_comma, :at_point)
|
253
|
+
def at_objects(i)
|
254
|
+
jseq(nil, i, :at_object, :_and_or_or_or_comma)
|
295
255
|
end
|
296
256
|
|
297
|
-
def
|
298
|
-
|
299
|
-
/(
|
300
|
-
y(ears?)?|months?|w(eeks?)?|d(ays?)?|
|
301
|
-
h(ours?)?|m(in(ute)?s?)?|s(ec(ond)?s?)?
|
302
|
-
)(\s+|$)/ix)
|
257
|
+
def at(i)
|
258
|
+
seq(:at, i, :_at, '?', :at_objects)
|
303
259
|
end
|
304
260
|
|
305
|
-
def
|
306
|
-
rex(:
|
307
|
-
/(year|month|week|day|hour|min(ute)?|sec(ond)?)(\s+|$)/i)
|
261
|
+
def interval(i)
|
262
|
+
rex(:interval, i, INTERVAL_REX)
|
308
263
|
end
|
309
|
-
|
310
|
-
|
264
|
+
|
265
|
+
# every day
|
266
|
+
# every 1 minute
|
267
|
+
def every_interval(i)
|
268
|
+
seq(:every_interval, i, :count, '?', :interval)
|
311
269
|
end
|
312
270
|
|
313
|
-
def
|
314
|
-
rex(:
|
271
|
+
def every_single_interval(i)
|
272
|
+
rex(:every_single_interval, i, /(1[ \t]+)?(week|year)/)
|
315
273
|
end
|
316
274
|
|
317
|
-
def
|
318
|
-
|
275
|
+
def to_weekday(i)
|
276
|
+
seq(:to_weekday, i, :weekday, :_to_or_dash, :weekday)
|
277
|
+
end
|
319
278
|
|
320
|
-
def
|
321
|
-
|
279
|
+
def weekday_range(i)
|
280
|
+
alt(nil, i, :to_weekday, :weekdays)
|
322
281
|
end
|
323
|
-
|
324
|
-
|
282
|
+
|
283
|
+
def to_omonthday(i)
|
284
|
+
seq(:to_omonthday, i,
|
285
|
+
:_the, '?', :omonthday, :_to, :_the, '?', :omonthday)
|
325
286
|
end
|
326
|
-
|
327
|
-
|
287
|
+
|
288
|
+
def to_hour(i)
|
289
|
+
seq(:to_hour, i, :at_object, :_to, :at_object)
|
328
290
|
end
|
329
291
|
|
330
|
-
def
|
331
|
-
|
292
|
+
def from_object(i)
|
293
|
+
alt(nil, i, :to_weekday, :to_omonthday, :to_hour)
|
332
294
|
end
|
333
|
-
def
|
334
|
-
|
295
|
+
def from_objects(i)
|
296
|
+
jseq(nil, i, :from_object, :_and_or_or)
|
335
297
|
end
|
336
|
-
def
|
337
|
-
|
298
|
+
def from(i)
|
299
|
+
seq(nil, i, :_from, '?', :from_objects)
|
338
300
|
end
|
339
301
|
|
340
|
-
|
341
|
-
|
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)
|
342
307
|
end
|
343
308
|
|
344
|
-
def
|
345
|
-
|
309
|
+
def otm(i)
|
310
|
+
rex(nil, i, /[ \t]+of the month/)
|
346
311
|
end
|
347
|
-
|
348
|
-
|
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)
|
349
320
|
end
|
350
|
-
|
351
|
-
|
321
|
+
|
322
|
+
def every_named(i)
|
323
|
+
rex(:every_named, i, /weekday/i)
|
352
324
|
end
|
353
325
|
|
354
|
-
def
|
355
|
-
|
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)
|
356
331
|
end
|
357
|
-
def
|
358
|
-
|
332
|
+
def every_objects(i)
|
333
|
+
jseq(nil, i, :every_object, :_and_or_or)
|
359
334
|
end
|
360
|
-
|
361
|
-
|
335
|
+
|
336
|
+
def every(i)
|
337
|
+
seq(:every, i, :_every, :every_objects)
|
362
338
|
end
|
363
339
|
|
364
|
-
def
|
365
|
-
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)
|
366
345
|
end
|
367
346
|
|
368
|
-
|
369
|
-
|
347
|
+
#
|
348
|
+
# rewrite parsed tree ###################################################
|
349
|
+
|
350
|
+
def slot(key, data0, data1=nil, opts=nil)
|
351
|
+
Slot.new(key, data0, data1, opts)
|
370
352
|
end
|
371
353
|
|
372
|
-
def
|
373
|
-
|
354
|
+
def _rewrite_subs(t, key=nil)
|
355
|
+
t.subgather(key).collect { |ct| rewrite(ct) }
|
374
356
|
end
|
375
|
-
def
|
376
|
-
|
357
|
+
def _rewrite_sub(t, key=nil)
|
358
|
+
st = t.sublookup(key)
|
359
|
+
st ? rewrite(st) : nil
|
377
360
|
end
|
378
361
|
|
379
|
-
def
|
380
|
-
|
362
|
+
def rewrite_dmin(t)
|
363
|
+
t.strinp
|
381
364
|
end
|
382
|
-
|
383
|
-
|
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)
|
384
371
|
end
|
385
|
-
|
386
|
-
|
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
|
387
380
|
end
|
388
|
-
|
389
|
-
|
381
|
+
|
382
|
+
def rewrite_on_thes(t)
|
383
|
+
_rewrite_subs(t, :omonthday)
|
390
384
|
end
|
391
|
-
def
|
392
|
-
|
385
|
+
def rewrite_on_days(t)
|
386
|
+
_rewrite_subs(t, :monthday)
|
393
387
|
end
|
394
388
|
|
395
|
-
def
|
396
|
-
|
397
|
-
end
|
398
|
-
def at_every(i)
|
399
|
-
seq(nil, i, :at, :every, :tz, '?')
|
389
|
+
def rewrite_on(t)
|
390
|
+
_rewrite_subs(t)
|
400
391
|
end
|
401
392
|
|
402
|
-
def
|
403
|
-
|
393
|
+
def rewrite_monthday(t)
|
394
|
+
slot(:monthday, t.string.to_i)
|
404
395
|
end
|
405
396
|
|
406
|
-
def
|
407
|
-
|
397
|
+
def rewrite_omonthday(t)
|
398
|
+
slot(:monthday, OMONTHDAYS[t.string.downcase])
|
408
399
|
end
|
409
|
-
|
410
|
-
|
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
|
411
412
|
end
|
412
|
-
|
413
|
-
|
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
|
414
420
|
end
|
415
421
|
|
416
|
-
def
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
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
|
421
440
|
end
|
422
441
|
|
423
|
-
|
442
|
+
def rewrite_every_named(t)
|
424
443
|
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
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
|
430
449
|
end
|
431
|
-
|
432
|
-
|
450
|
+
|
451
|
+
def rewrite_tz(t)
|
452
|
+
slot(:tz, t.string)
|
433
453
|
end
|
434
|
-
|
435
|
-
|
454
|
+
|
455
|
+
def rewrite_weekday(t)
|
456
|
+
Fugit::Cron::Parser::WEEKDS.index(t.string[0, 3].downcase)
|
436
457
|
end
|
437
458
|
|
438
|
-
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
|
439
464
|
|
440
|
-
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
|
441
470
|
|
442
|
-
|
471
|
+
def rewrite_to_omonthday(t)
|
472
|
+
md0, md1 = _rewrite_subs(t, :omonthday).collect(&:_data0)
|
473
|
+
slot(:monthday, "#{md0}-#{md1}")
|
443
474
|
end
|
444
475
|
|
445
|
-
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
|
446
482
|
|
447
|
-
|
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)
|
448
488
|
end
|
449
489
|
|
450
|
-
def
|
490
|
+
def rewrite_named_hour(t)
|
451
491
|
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
end
|
492
|
+
ht = t.sublookup(:named_h)
|
493
|
+
mt = t.sublookup(:named_m)
|
494
|
+
apt = t.sublookup(:ampm)
|
456
495
|
|
457
|
-
|
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 ]
|
458
502
|
|
459
|
-
|
503
|
+
h += 12 if h < 13 && apt && apt.strinpd == 'pm'
|
460
504
|
|
461
|
-
|
462
|
-
when /^s/ then [ '*', '*', i ]
|
463
|
-
when /^m/ then [ '*', i ]
|
464
|
-
end
|
505
|
+
slot(:hm, h, m)
|
465
506
|
end
|
466
507
|
|
467
|
-
def
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
[ 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)
|
476
516
|
end
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
v += 12 if vs[1] == 'pm'
|
481
|
-
[ v, 0 ]
|
517
|
+
|
518
|
+
def rewrite_at(t)
|
519
|
+
_rewrite_subs(t)
|
482
520
|
end
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
min = m[2].to_i
|
487
|
-
[ hou, min ]
|
521
|
+
|
522
|
+
def rewrite_every(t)
|
523
|
+
_rewrite_sub(t)
|
488
524
|
end
|
489
525
|
|
490
|
-
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
|
491
531
|
|
492
|
-
|
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
|
493
581
|
end
|
582
|
+
end
|
494
583
|
|
495
|
-
|
584
|
+
class SlotGroup
|
496
585
|
|
497
|
-
def
|
586
|
+
def initialize(slots)
|
498
587
|
|
499
|
-
#
|
500
|
-
|
501
|
-
|
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
|
502
609
|
end
|
503
610
|
|
504
|
-
|
611
|
+
def to_crons(opts)
|
612
|
+
|
613
|
+
multi = opts.has_key?(:multi) ? opts[:multi] : false
|
614
|
+
|
615
|
+
hms = determine_hms
|
616
|
+
|
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
|
625
|
+
end
|
505
626
|
|
506
|
-
|
507
|
-
def rewrite_dow_list(t); [ :dow_list, *_rewrite_children(t) ]; end
|
627
|
+
protected
|
508
628
|
|
509
|
-
def
|
629
|
+
def make_slot(key, data0, data1=nil)
|
510
630
|
|
511
|
-
|
631
|
+
Fugit::Nat::Slot.new(key, data0, data1)
|
512
632
|
end
|
513
633
|
|
514
|
-
def
|
634
|
+
def determine_hms
|
635
|
+
|
636
|
+
return [ [ [ '*' ], [ '*' ] ] ] if @hms.empty?
|
515
637
|
|
516
|
-
|
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
|
517
645
|
|
518
|
-
|
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
|
519
655
|
end
|
520
656
|
|
521
|
-
|
522
|
-
alias rewrite_at _rewrite_multiple
|
657
|
+
def parse_cron(hm)
|
523
658
|
|
524
|
-
|
525
|
-
|
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' ]
|
526
669
|
|
527
|
-
|
670
|
+
s = a
|
671
|
+
.collect { |e| e.uniq.sort.collect(&:to_s).join(',') }
|
672
|
+
.join(' ')
|
673
|
+
|
674
|
+
Fugit::Cron.parse(s)
|
675
|
+
end
|
528
676
|
|
529
|
-
|
530
|
-
|
677
|
+
def slot(key, default)
|
678
|
+
s = @slots[key]
|
679
|
+
s ? s.data0 : [ default ]
|
531
680
|
end
|
532
681
|
end
|
533
682
|
end
|