fugit 1.3.1 → 1.3.6
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 +5 -5
- data/CHANGELOG.md +27 -0
- data/CREDITS.md +5 -1
- data/LICENSE.txt +1 -1
- data/Makefile +1 -1
- data/README.md +72 -3
- data/fugit.gemspec +1 -1
- data/lib/fugit.rb +1 -1
- data/lib/fugit/cron.rb +25 -7
- data/lib/fugit/nat.rb +409 -156
- metadata +5 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 90b61604e15e2cb24d4fb124306affea15a644c2591fc3a49649981616351d66
|
4
|
+
data.tar.gz: 235eec423691346077fd9cfdaed895e28843ecf6681b5aa00a83cfcf9ca711a3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1fd5abf6871b1cb60220bf7d6089be6bff48dfae80a05db6d031def126275940d50cbc5dfcaad493f2b6d327b66162c2a44329966459bfb3d9d5b912e3b7e68c
|
7
|
+
data.tar.gz: '08a846fcce60834f8f0c289e14317249123f1f4028c478953ed04f759e633080c4d843f55c56fb973a104e1b44bbbbc92eecc978a954d7b0b317d7b23b82611b'
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,33 @@
|
|
2
2
|
# CHANGELOG.md
|
3
3
|
|
4
4
|
|
5
|
+
## fugit 1.3.6 released 2020-06-01
|
6
|
+
|
7
|
+
* Introduce new nat syntaxed, gh-38
|
8
|
+
* Rework nat parser
|
9
|
+
|
10
|
+
|
11
|
+
## fugit 1.3.5 released 2020-05-07
|
12
|
+
|
13
|
+
* Implement cron @noon, gh-37
|
14
|
+
* Normalize "every x", gh-37
|
15
|
+
|
16
|
+
|
17
|
+
## fugit 1.3.4 released 2020-04-06
|
18
|
+
|
19
|
+
* Prevent #rough_frequency returning 0, gh-36
|
20
|
+
|
21
|
+
|
22
|
+
## fugit 1.3.3 released 2019-08-29
|
23
|
+
|
24
|
+
* Fix Cron#match?(t) with respect to the cron's timezone, gh-31
|
25
|
+
|
26
|
+
|
27
|
+
## fugit 1.3.2 released 2019-08-14
|
28
|
+
|
29
|
+
* Allow for "* 0-24 * * *", gh-30
|
30
|
+
|
31
|
+
|
5
32
|
## fugit 1.3.1 released 2019-07-27
|
6
33
|
|
7
34
|
* Fix nat parsing for 'every day at 18:00 and 18:15', gh-29
|
data/CREDITS.md
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
|
2
2
|
# fugit credits
|
3
3
|
|
4
|
-
*
|
4
|
+
* Danny Ben Shitrit https://github.com/DannyBen nat variants, gh-38
|
5
|
+
* Dominik Sander https://github.com/dsander #rough_frequency 0, gh-36
|
6
|
+
* Milovan Zogovic https://github.com/assembler Cron#match? vs TZ, gh-31
|
7
|
+
* Jessica Stokes https://github.com/ticky 0-24 issue with cron, gh-30
|
8
|
+
* Shai Coleman https://github.com/shaicoleman parse_nat enhancements, gh-24, gh-25, gh-28, and gh-37
|
5
9
|
* Jan Stevens https://github.com/JanStevens Fugit.parse('every 15 minutes') gh-22
|
6
10
|
* Fabio Pitino https://github.com/hspazio nil on February 30 gh-21
|
7
11
|
* Cristian Oneț https://github.com/conet #previous_time vs 1/-1 endless loop gh-15
|
data/LICENSE.txt
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
|
2
|
-
Copyright (c) 2017-
|
2
|
+
Copyright (c) 2017-2020, 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/Makefile
CHANGED
data/README.md
CHANGED
@@ -36,6 +36,7 @@ The intersection of those two projects is where fugit is born:
|
|
36
36
|
* [rufus-scheduler](https://github.com/jmettraux/rufus-scheduler) -
|
37
37
|
* [flor](https://github.com/floraison/flor) - used in the [cron](https://github.com/floraison/flor/blob/master/doc/procedures/cron.md) procedure
|
38
38
|
* [que-scheduler](https://github.com/hlascelles/que-scheduler) - a reliable job scheduler for [que](https://github.com/chanks/que)
|
39
|
+
* [serial_scheduler](https://github.com/grosser/serial_scheduler) - ruby task scheduler without threading
|
39
40
|
* ...
|
40
41
|
|
41
42
|
## `Fugit.parse(s)`
|
@@ -63,7 +64,7 @@ Fugit.parse('nada')
|
|
63
64
|
## `Fugit.do_parse(s)`
|
64
65
|
|
65
66
|
`Fugit.do_parse(s)` is equivalent to `Fugit.parse(s)`, but instead of returning nil, it raises an error if the given string contains no time information.
|
66
|
-
```
|
67
|
+
```ruby
|
67
68
|
Fugit.do_parse('nada')
|
68
69
|
# ==> /home/jmettraux/w/fugit/lib/fugit/parse.rb:32
|
69
70
|
# :in `do_parse': found no time information in "nada" (ArgumentError)
|
@@ -137,6 +138,49 @@ Example of cron strings understood by fugit:
|
|
137
138
|
# and more...
|
138
139
|
```
|
139
140
|
|
141
|
+
### the first Monday of the month
|
142
|
+
|
143
|
+
Fugit tries to follow the `man 5 crontab` documentation.
|
144
|
+
|
145
|
+
There is a surprising thing about this canon, all the columns are joined by ANDs, except for monthday and weekday which are joined together by OR if they are both set (they are not `*`).
|
146
|
+
|
147
|
+
Many people (me included) [are suprised](https://superuser.com/questions/428807/run-a-cron-job-on-the-first-monday-of-every-month) when they try to specify "at 05:00 on the first Monday of the month" as `0 5 1-7 * 1` or `0 5 1-7 * mon` and the results are off.
|
148
|
+
|
149
|
+
The man page says:
|
150
|
+
|
151
|
+
> Note: The day of a command's execution can be specified by
|
152
|
+
> two fields -- day of month, and day of week. If both fields
|
153
|
+
> are restricted (ie, are not *), the command will be run when
|
154
|
+
> either field matches the current time.
|
155
|
+
> For example, ``30 4 1,15 * 5'' would cause a command to be run
|
156
|
+
> at 4:30 am on the 1st and 15th of each month, plus every Friday.
|
157
|
+
|
158
|
+
Fugit follows this specification.
|
159
|
+
|
160
|
+
There is a solution though, please read on.
|
161
|
+
|
162
|
+
### the hash extension
|
163
|
+
|
164
|
+
Fugit understands `0 5 * * 1#1` or `0 5 * * mon#1` as "each first Monday of the month, at 05:00".
|
165
|
+
|
166
|
+
```ruby
|
167
|
+
'0 5 * * 1#1' #
|
168
|
+
'0 5 * * mon#1' # the first Monday of the month at 05:00
|
169
|
+
|
170
|
+
'0 6 * * 5#4,5#5' #
|
171
|
+
'0 6 * * fri#4,fri#5' # the 4th and 5th Fridays of the month at 06:00
|
172
|
+
|
173
|
+
'0 7 * * 5#-1' #
|
174
|
+
'0 7 * * fri#-1' # the last Friday of the month at 07:00
|
175
|
+
|
176
|
+
'0 7 * * 5#L' #
|
177
|
+
'0 7 * * fri#L' #
|
178
|
+
'0 7 * * 5#last' #
|
179
|
+
'0 7 * * fri#last' # the last Friday of the month at 07:00
|
180
|
+
|
181
|
+
'0 23 * * mon#2,tue' # the 2nd Monday of the month and every Tuesday, at 23:00
|
182
|
+
```
|
183
|
+
|
140
184
|
### the modulo extension
|
141
185
|
|
142
186
|
Fugit, since 1.1.10, also understands cron strings like "`9 0 * * sun%2`" which can be read as "every other Sunday at 9am".
|
@@ -145,7 +189,7 @@ For odd Sundays, one can write `9 0 * * sun%2+1`.
|
|
145
189
|
|
146
190
|
It can be combined, as in `9 0 * * sun%2,tue%3+2`
|
147
191
|
|
148
|
-
But what does it
|
192
|
+
But what does it reference to? It starts at 1 on 2019-01-01.
|
149
193
|
|
150
194
|
```ruby
|
151
195
|
require 'et-orbi' # >= 1.1.8
|
@@ -184,10 +228,35 @@ p d.to_long_s # => "2 years, 2 months, 1 day, and 5 hours"
|
|
184
228
|
d += 3600
|
185
229
|
|
186
230
|
p d.to_plain_s # => "2Y2M1D5h3600s"
|
231
|
+
|
232
|
+
p Fugit::Duration.parse('1y2M1d4h').to_sec # => 36820800
|
187
233
|
```
|
188
234
|
|
189
|
-
|
235
|
+
There is a `#deflate` method
|
236
|
+
|
237
|
+
```ruby
|
238
|
+
Fugit::Duration.parse(1000).to_plain_s # => "1000s"
|
239
|
+
Fugit::Duration.parse(3600).to_plain_s # => "3600s"
|
240
|
+
Fugit::Duration.parse(1000).deflate.to_plain_s # => "16m40s"
|
241
|
+
Fugit::Duration.parse(3600).deflate.to_plain_s # => "1h"
|
242
|
+
|
243
|
+
# or event shorter
|
244
|
+
Fugit.parse(1000).deflate.to_plain_s # => "16m40s"
|
245
|
+
Fugit.parse(3600).deflate.to_plain_s # => "1h"
|
190
246
|
```
|
247
|
+
|
248
|
+
There is also an `#inflate` method
|
249
|
+
|
250
|
+
```ruby
|
251
|
+
Fugit::Duration.parse('1h30m12').inflate.to_plain_s # => "5412s"
|
252
|
+
Fugit.parse('1h30m12').inflate.to_plain_s # => "5412s"
|
253
|
+
|
254
|
+
Fugit.parse('1h30m12').to_sec # => 5412
|
255
|
+
Fugit.parse('1h30m12').to_sec.to_s + 's' # => "5412s"
|
256
|
+
```
|
257
|
+
|
258
|
+
The `to_*_s` methods are also available as class methods:
|
259
|
+
```ruby
|
191
260
|
p Fugit::Duration.to_plain_s('1y2M1d4h')
|
192
261
|
# => "1Y2M1D4h"
|
193
262
|
p Fugit::Duration.to_iso_s('1y2M1d4h')
|
data/fugit.gemspec
CHANGED
@@ -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.3'
|
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/cron.rb
CHANGED
@@ -11,6 +11,7 @@ module Fugit
|
|
11
11
|
'@weekly' => '0 0 * * 0',
|
12
12
|
'@daily' => '0 0 * * *',
|
13
13
|
'@midnight' => '0 0 * * *',
|
14
|
+
'@noon' => '0 12 * * *',
|
14
15
|
'@hourly' => '0 * * * *' }
|
15
16
|
MAXDAYS = [
|
16
17
|
nil, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ]
|
@@ -188,6 +189,17 @@ module Fugit
|
|
188
189
|
|
189
190
|
return weekday_match?(nt) || monthday_match?(nt) \
|
190
191
|
if @weekdays && @monthdays
|
192
|
+
#
|
193
|
+
# From `man 5 crontab`
|
194
|
+
#
|
195
|
+
# Note: The day of a command's execution can be specified
|
196
|
+
# by two fields -- day of month, and day of week.
|
197
|
+
# If both fields are restricted (ie, are not *), the command will be
|
198
|
+
# run when either field matches the current time.
|
199
|
+
# For example, ``30 4 1,15 * 5'' would cause a command to be run
|
200
|
+
# at 4:30 am on the 1st and 15th of each month, plus every Friday.
|
201
|
+
#
|
202
|
+
# as seen in gh-5 and gh-35
|
191
203
|
|
192
204
|
return false unless weekday_match?(nt)
|
193
205
|
return false unless monthday_match?(nt)
|
@@ -197,7 +209,7 @@ module Fugit
|
|
197
209
|
|
198
210
|
def match?(t)
|
199
211
|
|
200
|
-
t = Fugit.do_parse_at(t)
|
212
|
+
t = Fugit.do_parse_at(t).translate(@timezone)
|
201
213
|
|
202
214
|
month_match?(t) && day_match?(t) &&
|
203
215
|
hour_match?(t) && min_match?(t) && sec_match?(t)
|
@@ -336,6 +348,7 @@ module Fugit
|
|
336
348
|
return (a + [ a.first + v1 ])
|
337
349
|
.each_cons(2)
|
338
350
|
.collect { |a0, a1| a1 - a0 }
|
351
|
+
.select { |d| d > 0 } # weed out zero deltas
|
339
352
|
.min * v0
|
340
353
|
end
|
341
354
|
|
@@ -497,23 +510,28 @@ module Fugit
|
|
497
510
|
|
498
511
|
loop do
|
499
512
|
|
500
|
-
#p({ cur: cur })
|
501
513
|
a << cur
|
502
514
|
break if cur == edn
|
503
515
|
|
504
516
|
cur += 1
|
505
|
-
|
506
|
-
|
517
|
+
if cur > max
|
518
|
+
cur = min
|
519
|
+
edn = edn - max - 1 if edn > max
|
520
|
+
end
|
507
521
|
|
508
522
|
fail RuntimeError.new(
|
509
523
|
"too many loops for " +
|
510
524
|
{ min: omin, max: omax, sta: sta, edn: edn, sla: sla }.inspect +
|
511
525
|
" #range, breaking, " +
|
512
526
|
"please fill an issue at https://git.io/fjJC9"
|
513
|
-
) if a.length > omax
|
527
|
+
) if a.length > 2 * omax
|
528
|
+
# there is a #uniq afterwards, hence the 2* for 0-24 and friends
|
514
529
|
end
|
515
530
|
|
516
|
-
a.each_with_index
|
531
|
+
a.each_with_index
|
532
|
+
.select { |e, i| i % sla == 0 }
|
533
|
+
.collect(&:first)
|
534
|
+
.uniq
|
517
535
|
end
|
518
536
|
|
519
537
|
def compact(key)
|
@@ -739,7 +757,7 @@ module Fugit
|
|
739
757
|
|
740
758
|
def rewrite_tz(t)
|
741
759
|
|
742
|
-
s = t.
|
760
|
+
s = t.strim
|
743
761
|
z = EtOrbi.get_tzone(s)
|
744
762
|
|
745
763
|
[ s, z ]
|
data/lib/fugit/nat.rb
CHANGED
@@ -15,16 +15,8 @@ module Fugit
|
|
15
15
|
return nil unless s.is_a?(String)
|
16
16
|
|
17
17
|
#p s; Raabro.pp(Parser.parse(s, debug: 3), colours: true)
|
18
|
-
|
19
|
-
|
20
|
-
return nil unless a
|
21
|
-
|
22
|
-
return parse_crons(s, a, opts) \
|
23
|
-
if a.include?([ :flag, 'every' ])
|
24
|
-
return parse_crons(s, a, opts) \
|
25
|
-
if a.include?([ :flag, 'from' ]) && a.find { |e| e[0] == :day_range }
|
26
|
-
|
27
|
-
nil
|
18
|
+
#(p s; Raabro.pp(Parser.parse(s, debug: 1), colours: true)) rescue nil
|
19
|
+
parse_crons(s, Parser.parse(s), opts)
|
28
20
|
end
|
29
21
|
|
30
22
|
def do_parse(s, opts={})
|
@@ -37,20 +29,37 @@ module Fugit
|
|
37
29
|
|
38
30
|
def parse_crons(s, a, opts)
|
39
31
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
.
|
32
|
+
#p a
|
33
|
+
return nil unless a
|
34
|
+
|
35
|
+
h = a
|
36
|
+
.reverse
|
37
|
+
.inject({}) { |r, e| send("parse_#{e[0]}_elt", e, opts, r); r }
|
38
|
+
#
|
39
|
+
# the reverse ensure that in "every day at five", the
|
40
|
+
# "at five" is placed before the "every day" so that
|
41
|
+
# parse_x_elt calls have the right sequence
|
42
|
+
#p h
|
43
|
+
|
44
|
+
if f = h[:_fail]
|
45
|
+
#fail ArgumentError.new(f)
|
46
|
+
return nil
|
47
|
+
end
|
48
|
+
|
49
|
+
hms = h[:hms]
|
50
|
+
|
51
|
+
hours = (hms || [])
|
45
52
|
.uniq
|
53
|
+
.inject({}) { |r, hm| (r[hm[1]] ||= []) << hm[0]; r }
|
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
|
46
60
|
|
47
|
-
crons =
|
48
|
-
|
49
|
-
if ms.size <= 1
|
50
|
-
[ parse_cron(a, opts) ]
|
51
|
-
else
|
52
|
-
dhs.collect { |dh| parse_cron([ dh ] + aa, opts) }
|
53
|
-
end
|
61
|
+
crons = hours
|
62
|
+
.collect { |hm| assemble_cron(h.merge(hms: hm)) }
|
54
63
|
|
55
64
|
fail ArgumentError.new(
|
56
65
|
"multiple crons in #{s.inspect} " +
|
@@ -64,207 +73,451 @@ module Fugit
|
|
64
73
|
end
|
65
74
|
end
|
66
75
|
|
67
|
-
def
|
68
|
-
|
69
|
-
h = { min: nil, hou: [], dom: nil, mon: nil, dow: nil }
|
70
|
-
hkeys = h.keys
|
71
|
-
|
72
|
-
a.each do |key, val|
|
73
|
-
if key == :biz_day
|
74
|
-
(h[:dow] ||= []) << '1-5'
|
75
|
-
elsif key == :simple_hour || key == :numeral_hour
|
76
|
-
h[:hou] << val
|
77
|
-
elsif key == :digital_hour
|
78
|
-
(h[:hou] ||= []) << val[0].to_i
|
79
|
-
(h[:min] ||= []) << val[1].to_i
|
80
|
-
elsif key == :name_day
|
81
|
-
(h[:dow] ||= []) << val
|
82
|
-
elsif key == :day_range
|
83
|
-
(h[:dow] ||= []) << val.collect { |v| v.to_s[0, 3] }.join('-')
|
84
|
-
elsif key == :tz
|
85
|
-
h[:tz] = val
|
86
|
-
elsif key == :duration
|
87
|
-
process_duration(h, *val[0].to_h.first)
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
h[:min] ||= [ 0 ]
|
92
|
-
h[:min].uniq!
|
76
|
+
def assemble_cron(h)
|
93
77
|
|
94
|
-
|
95
|
-
|
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]
|
96
85
|
|
97
|
-
|
98
|
-
|
99
|
-
a = hkeys
|
100
|
-
.collect { |k|
|
101
|
-
v = h[k]
|
102
|
-
(v && v.any?) ? v.collect(&:to_s).join(',') : '*' }
|
103
|
-
a.insert(0, h[:sec]) if h[:sec]
|
104
|
-
a << h[:tz].first if h[:tz]
|
105
|
-
|
106
|
-
s = a.join(' ')
|
86
|
+
Fugit::Cron.parse(s.join(' '))
|
87
|
+
end
|
107
88
|
|
108
|
-
|
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
|
109
121
|
end
|
110
122
|
|
111
|
-
def
|
123
|
+
def parse_dow_list_elt(e, opts, h)
|
112
124
|
|
113
|
-
|
125
|
+
h[:hms] ||= [ [ 0, 0 ] ]
|
126
|
+
h[:dow] = e[1..-1].collect(&:to_s).sort.join(',')
|
114
127
|
end
|
115
128
|
|
116
|
-
def
|
129
|
+
def parse_dow_range_elt(e, opts, h)
|
117
130
|
|
118
|
-
h[:
|
119
|
-
h[:
|
120
|
-
h[:mon] = [ value == 1 ? '*' : "*/#{value}" ]
|
131
|
+
h[:hms] ||= [ [ 0, 0 ] ]
|
132
|
+
h[:dow] = e[1] == e[2] ? e[1] : "#{e[1]}-#{e[2]}"
|
121
133
|
end
|
122
134
|
|
123
|
-
def
|
135
|
+
def parse_day_of_month_elt(e, opts, h)
|
124
136
|
|
125
|
-
h[:
|
126
|
-
h[:dom] = [ value == 1 ? '*' : "*/#{value}" ]
|
137
|
+
h[:dom] = e[1..-1].join(',')
|
127
138
|
end
|
128
139
|
|
129
|
-
def
|
140
|
+
def parse_at_elt(e, opts, h)
|
130
141
|
|
131
|
-
h[:
|
142
|
+
(h[:hms] ||= []).concat(e[1])
|
143
|
+
|
144
|
+
l = h[:hms].last
|
145
|
+
h[:sec] = l.pop if l.size > 2
|
132
146
|
end
|
133
147
|
|
134
|
-
def
|
148
|
+
def parse_on_elt(e, opts, h)
|
149
|
+
|
150
|
+
e1 = e[1]
|
151
|
+
h[:dow] = e1[0]
|
152
|
+
h[:hms] = [ e1[1] ]
|
135
153
|
|
136
|
-
|
137
|
-
h[:
|
154
|
+
l = h[:hms].last
|
155
|
+
h[:sec] = l.pop if l.size > 2
|
138
156
|
end
|
139
157
|
|
140
|
-
def
|
158
|
+
def parse_tz_elt(e, opts, h)
|
141
159
|
|
142
|
-
h[:
|
143
|
-
h[:min] = [ '*' ]
|
144
|
-
h[:sec] = [ value == 1 ? '*' : "*/#{value}" ]
|
160
|
+
h[:tz] = e[1]
|
145
161
|
end
|
146
162
|
end
|
147
163
|
|
148
164
|
module Parser include Raabro
|
149
165
|
|
150
166
|
NUMS = %w[
|
151
|
-
zero
|
152
|
-
one two three four five six seven eight nine
|
153
|
-
ten eleven twelve ]
|
167
|
+
zero one two three four five six seven eight nine ten eleven twelve ]
|
154
168
|
|
155
169
|
WEEKDAYS =
|
156
170
|
Fugit::Cron::Parser::WEEKDS + Fugit::Cron::Parser::WEEKDAYS
|
157
171
|
|
158
|
-
NHOURS =
|
159
|
-
|
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
|
160
196
|
|
161
197
|
# piece parsers bottom to top
|
162
198
|
|
199
|
+
def _from(i); rex(nil, i, /\s*from\s+/i); end
|
200
|
+
def _every(i); rex(nil, i, /\s*(every)\s+/i); end
|
201
|
+
def _at(i); rex(nil, i, /\s*at\s+/i); end
|
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
|
207
|
+
|
208
|
+
def _and_or_comma(i)
|
209
|
+
rex(nil, i, /\s*(,?\s*and\s|,?\s*or\s|,)\s*/i)
|
210
|
+
end
|
211
|
+
def _at_comma(i)
|
212
|
+
rex(nil, i, /\s*(at\s|,|)\s*/i)
|
213
|
+
end
|
214
|
+
def _to_through(i)
|
215
|
+
rex(nil, i, /\s*(to|through)\s+/i)
|
216
|
+
end
|
217
|
+
|
218
|
+
def integer(i); rex(:int, i, /\d+\s*/); end
|
219
|
+
|
220
|
+
def tz_name(i)
|
221
|
+
rex(nil, i,
|
222
|
+
/\s*[A-Z][a-zA-Z0-9+\-]+(\/[A-Z][a-zA-Z0-9+\-_]+){0,2}(\s+|$)/)
|
223
|
+
end
|
224
|
+
def tz_delta(i)
|
225
|
+
rex(nil, i,
|
226
|
+
/\s*[-+]([01][0-9]|2[0-4]):?(00|15|30|45)(\s+|$)/)
|
227
|
+
end
|
228
|
+
def tzone(i)
|
229
|
+
alt(:tzone, i, :tz_delta, :tz_name)
|
230
|
+
end
|
231
|
+
|
232
|
+
def and_named_digits(i)
|
233
|
+
rex(:xxx, i, 'TODO')
|
234
|
+
end
|
235
|
+
|
236
|
+
def dname(i)
|
237
|
+
rex(:dname, i, /(s(ec(onds?)?)?|m(in(utes?)?)?)\s+/i)
|
238
|
+
end
|
239
|
+
def named_digit(i)
|
240
|
+
seq(:named_digit, i, :dname, :integer)
|
241
|
+
end
|
242
|
+
def named_digits(i)
|
243
|
+
seq(nil, i, :named_digit, '+', :and_named_digits, '*')
|
244
|
+
end
|
245
|
+
|
163
246
|
def am_pm(i)
|
164
|
-
rex(:am_pm, i,
|
247
|
+
rex(:am_pm, i, /\s*(am|pm|dark)\s*/i)
|
248
|
+
end
|
249
|
+
|
250
|
+
def nminute(i)
|
251
|
+
rex(:nminute, i, /(#{NMINUTES.keys.join('|')})\s*/i)
|
252
|
+
end
|
253
|
+
def nhour(i)
|
254
|
+
rex(:nhour, i, /(#{NUMS.join('|')})\s*/i)
|
255
|
+
end
|
256
|
+
def numeral_hour(i)
|
257
|
+
seq(:numeral_hour, i, :nhour, :am_pm, '?', :nminute, '?')
|
258
|
+
end
|
259
|
+
|
260
|
+
def named_hour(i)
|
261
|
+
rex(:named_hour, i, /(#{NHOURS.keys.join('|')})/i)
|
262
|
+
end
|
263
|
+
|
264
|
+
def shour(i)
|
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, '?')
|
165
269
|
end
|
166
270
|
|
167
271
|
def digital_hour(i)
|
168
272
|
rex(:digital_hour, i, /(2[0-4]|[01][0-9]):?[0-5]\d/)
|
169
273
|
end
|
170
274
|
|
171
|
-
def
|
172
|
-
|
275
|
+
def at_point(i)
|
276
|
+
alt(nil, i,
|
277
|
+
:digital_hour, :simple_hour, :named_hour, :numeral_hour,
|
278
|
+
:named_digits)
|
173
279
|
end
|
174
|
-
|
175
|
-
|
280
|
+
|
281
|
+
def weekday(i)
|
282
|
+
rex(:weekday, i, /(#{WEEKDAYS.reverse.join('|')})\s*/i)
|
176
283
|
end
|
177
284
|
|
178
|
-
def
|
179
|
-
|
285
|
+
def and_at(i)
|
286
|
+
seq(nil, i, :_and_or_comma, :at_point)
|
180
287
|
end
|
181
|
-
|
182
|
-
|
288
|
+
|
289
|
+
def _intervals(i)
|
290
|
+
rex(:intervals, i,
|
291
|
+
/(
|
292
|
+
y(ears?)?|months?|w(eeks?)?|d(ays?)?|
|
293
|
+
h(ours?)?|m(in(ute)?s?)?|s(ec(ond)?s?)?
|
294
|
+
)(\s+|$)/ix)
|
183
295
|
end
|
184
296
|
|
185
|
-
def
|
186
|
-
rex(:
|
297
|
+
def sinterval(i)
|
298
|
+
rex(:sinterval, i,
|
299
|
+
/(year|month|week|day|hour|min(ute)?|sec(ond)?)(\s+|$)/i)
|
300
|
+
end
|
301
|
+
def ninterval(i)
|
302
|
+
seq(:ninterval, i, :integer, :_intervals)
|
187
303
|
end
|
188
304
|
|
189
|
-
def
|
190
|
-
|
191
|
-
|
305
|
+
def ordinal(i)
|
306
|
+
rex(:ordinal, i, /\s*(#{ORDINALS.keys.join('|')})\s*/)
|
307
|
+
end
|
192
308
|
|
193
|
-
def
|
309
|
+
def _mod(i); rex(nil, i, /\s*month\s+on\s+days?\s+/i); end
|
310
|
+
def _oftm(i); rex(nil, i, /\s*(day\s)?\s*of\s+the\s+month\s*/i); end
|
194
311
|
|
195
|
-
def
|
196
|
-
|
312
|
+
def dom(i)
|
313
|
+
rex(:int, i, /([12][0-9]|3[01]|[0-9])/)
|
314
|
+
end
|
315
|
+
def and_or_dom(i)
|
316
|
+
seq(nil, i, :_and_or_comma, :dom)
|
317
|
+
end
|
318
|
+
def dom_list(i)
|
319
|
+
seq(:dom_list, i, :dom, :and_or_dom, '*')
|
197
320
|
end
|
198
321
|
|
199
|
-
def
|
200
|
-
|
322
|
+
def dom_mod(i) # every month on day
|
323
|
+
seq(:dom, i, :_mod, :dom_list)
|
324
|
+
end
|
325
|
+
def dom_noftm(i) # every nth of month
|
326
|
+
seq(:dom, i, :ordinal, :_oftm)
|
201
327
|
end
|
202
|
-
def
|
203
|
-
|
328
|
+
def day_of_month(i)
|
329
|
+
alt(nil, i, :dom_noftm, :dom_mod)
|
204
330
|
end
|
205
|
-
def _tz(i); alt(:tz, i, :_tz_delta, :_tz_name); end
|
206
331
|
|
207
|
-
def
|
208
|
-
rex(
|
209
|
-
:duration, i,
|
210
|
-
/
|
211
|
-
\d+
|
212
|
-
\s?
|
213
|
-
(mon(ths?)?|d(ays?)?|h(ours?)?|m(in(ute)?s?)?|s(ec(ond)?s?)?)
|
214
|
-
/ix)
|
332
|
+
def dow_class(i)
|
333
|
+
rex(:dow_class, i, /(weekday)(\s+|$)/i)
|
215
334
|
end
|
216
335
|
|
217
|
-
def
|
336
|
+
def dow(i)
|
337
|
+
seq(:dow, i, :weekday)
|
338
|
+
end
|
339
|
+
def and_or_dow(i)
|
340
|
+
seq(nil, i, :_and_or_comma, :dow)
|
341
|
+
end
|
342
|
+
def dow_list(i)
|
343
|
+
seq(:dow_list, i, :dow, :and_or_dow, '*')
|
344
|
+
end
|
218
345
|
|
219
|
-
def
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
346
|
+
def to_dow_range(i)
|
347
|
+
seq(:dow_range, i, :weekday, :_to_through, :weekday)
|
348
|
+
end
|
349
|
+
def dash_dow_range(i)
|
350
|
+
seq(:dow_range, i, :weekday, :_dash, :weekday)
|
351
|
+
end
|
352
|
+
def dow_range(i)
|
353
|
+
alt(nil, i, :to_dow_range, :dash_dow_range)
|
354
|
+
end
|
355
|
+
|
356
|
+
def day_of_week(i)
|
357
|
+
alt(nil, i, :dow_range, :dow_list, :dow_class)
|
358
|
+
end
|
359
|
+
|
360
|
+
def interval(i)
|
361
|
+
alt(nil, i, :sinterval, :ninterval)
|
362
|
+
end
|
363
|
+
|
364
|
+
def every_object(i)
|
365
|
+
alt(nil, i, :day_of_month, :interval, :day_of_week)
|
366
|
+
end
|
367
|
+
def from_object(i)
|
368
|
+
alt(nil, i, :interval, :to_dow_range)
|
369
|
+
end
|
370
|
+
|
371
|
+
def tz(i)
|
372
|
+
seq(nil, i, :_in, '?', :tzone)
|
373
|
+
end
|
374
|
+
def on(i)
|
375
|
+
seq(:on, i, :_on, :weekday, :at_point, :and_at, '*')
|
376
|
+
end
|
377
|
+
def at(i)
|
378
|
+
seq(:at, i, :_at_comma, :at_point, :and_at, '*')
|
379
|
+
end
|
380
|
+
def from(i)
|
381
|
+
seq(:from, i, :_from, :from_object)
|
382
|
+
end
|
383
|
+
def every(i)
|
384
|
+
seq(:every, i, :_every, :every_object)
|
385
|
+
end
|
386
|
+
|
387
|
+
def at_from(i)
|
388
|
+
seq(nil, i, :at, :from, :tz, '?')
|
389
|
+
end
|
390
|
+
def at_every(i)
|
391
|
+
seq(nil, i, :at, :every, :tz, '?')
|
392
|
+
end
|
393
|
+
|
394
|
+
def from_at(i)
|
395
|
+
seq(nil, i, :from, :at, '?', :tz, '?')
|
227
396
|
end
|
228
397
|
|
229
|
-
def
|
398
|
+
def every_(i)
|
399
|
+
seq(nil, i, :every, :tz, '?')
|
400
|
+
end
|
401
|
+
def every_on(i)
|
402
|
+
seq(nil, i, :every, :on, :tz, '?')
|
403
|
+
end
|
404
|
+
def every_at(i)
|
405
|
+
seq(nil, i, :every, :at, :tz, '?')
|
406
|
+
end
|
230
407
|
|
231
|
-
def
|
232
|
-
|
408
|
+
def nat(i)
|
409
|
+
alt(:nat, i,
|
410
|
+
:every_at, :every_on, :every_,
|
411
|
+
:from_at,
|
412
|
+
:at_every, :at_from)
|
413
|
+
end
|
233
414
|
|
234
415
|
# rewrite parsed tree
|
235
416
|
|
236
|
-
def
|
417
|
+
#def _rewrite_single(t)
|
418
|
+
# [ t.name, rewrite(t.sublookup(nil)) ]
|
419
|
+
#end
|
420
|
+
def _rewrite_children(t)
|
421
|
+
t.subgather(nil).collect { |tt| rewrite(tt) }
|
422
|
+
end
|
423
|
+
def _rewrite_multiple(t)
|
424
|
+
[ t.name, _rewrite_children(t) ]
|
425
|
+
end
|
426
|
+
def _rewrite_child(t)
|
427
|
+
rewrite(t.sublookup(nil))
|
428
|
+
end
|
429
|
+
|
430
|
+
def rewrite_int(t); t.string.to_i; end
|
431
|
+
|
432
|
+
def rewrite_tzone(t)
|
433
|
+
|
434
|
+
[ :tz, t.strim ]
|
435
|
+
end
|
436
|
+
|
437
|
+
def rewrite_sinterval(t)
|
438
|
+
|
439
|
+
[ :interval, 1, t.strim ]
|
440
|
+
end
|
441
|
+
|
442
|
+
def rewrite_ninterval(t)
|
443
|
+
|
444
|
+
[ :interval,
|
445
|
+
t.sublookup(:int).string.to_i,
|
446
|
+
t.sublookup(:intervals).strim ]
|
447
|
+
end
|
448
|
+
|
449
|
+
def rewrite_named_digit(t)
|
450
|
+
|
451
|
+
i = t.sublookup(:int).string.to_i
|
452
|
+
|
453
|
+
case n = t.sublookup(:dname).strim
|
454
|
+
when /^s/ then [ '*', '*', i ]
|
455
|
+
when /^m/ then [ '*', i ]
|
456
|
+
end
|
457
|
+
end
|
458
|
+
|
459
|
+
def rewrite_named_hour(t)
|
460
|
+
NHOURS[t.strim.downcase]
|
461
|
+
end
|
462
|
+
def rewrite_numeral_hour(t)
|
463
|
+
vs = t.subgather(nil).collect { |st| st.strim.downcase }
|
464
|
+
v = NUMS.index(vs[0])
|
465
|
+
v += 12 if vs[1] == 'pm'
|
466
|
+
m = NMINUTES[vs[2]] || 0
|
467
|
+
[ v, m ]
|
468
|
+
end
|
469
|
+
def rewrite_simple_hour(t)
|
470
|
+
vs = t.subgather(nil).collect { |st| st.strim.downcase }
|
471
|
+
v = vs[0].to_i
|
472
|
+
v += 12 if vs[1] == 'pm'
|
473
|
+
[ v, 0 ]
|
474
|
+
end
|
475
|
+
def rewrite_digital_hour(t)
|
476
|
+
m = t.string.match(/(\d\d?):?(\d\d)/)
|
477
|
+
[ m[1].to_i, m[2].to_i ]
|
478
|
+
end
|
479
|
+
|
480
|
+
def rewrite_weekday(t)
|
481
|
+
|
482
|
+
WEEKDAYS.index(t.strim.downcase[0, 3])
|
483
|
+
end
|
484
|
+
|
485
|
+
def rewrite_ordinal(t); ORDINALS[t.strim]; end
|
486
|
+
|
487
|
+
def rewrite_dom(t)
|
237
488
|
|
238
489
|
#Raabro.pp(t, colours: true)
|
239
|
-
|
240
|
-
.
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
490
|
+
[ :day_of_month,
|
491
|
+
*_rewrite_children(t).flatten.select { |e| e.is_a?(Integer) } ]
|
492
|
+
end
|
493
|
+
|
494
|
+
alias rewrite_dow _rewrite_child
|
495
|
+
|
496
|
+
def rewrite_dom_list(t); [ :dom_list, *_rewrite_children(t) ]; end
|
497
|
+
def rewrite_dow_list(t); [ :dow_list, *_rewrite_children(t) ]; end
|
498
|
+
|
499
|
+
def rewrite_dow_class(t)
|
500
|
+
|
501
|
+
[ :dow_range, 1, 5 ] # only "weekday" for now
|
502
|
+
end
|
503
|
+
|
504
|
+
def rewrite_dow_range(t)
|
505
|
+
|
506
|
+
tts = t.subgather(nil)
|
507
|
+
|
508
|
+
[ :dow_range, rewrite(tts[0]), rewrite(tts[1]) ]
|
509
|
+
end
|
510
|
+
|
511
|
+
alias rewrite_on _rewrite_multiple
|
512
|
+
alias rewrite_at _rewrite_multiple
|
513
|
+
|
514
|
+
alias rewrite_from _rewrite_child
|
515
|
+
alias rewrite_every _rewrite_child
|
516
|
+
|
517
|
+
def rewrite_nat(t)
|
518
|
+
|
519
|
+
t.subgather(nil).collect { |tt| rewrite(tt) }
|
520
|
+
#.tap { |x| pp x }
|
268
521
|
end
|
269
522
|
end
|
270
523
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fugit
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.3.
|
4
|
+
version: 1.3.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Mettraux
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-06-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: raabro
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '1.
|
19
|
+
version: '1.3'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '1.
|
26
|
+
version: '1.3'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: et-orbi
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -117,8 +117,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
117
117
|
- !ruby/object:Gem::Version
|
118
118
|
version: '0'
|
119
119
|
requirements: []
|
120
|
-
|
121
|
-
rubygems_version: 2.5.2.3
|
120
|
+
rubygems_version: 3.0.3
|
122
121
|
signing_key:
|
123
122
|
specification_version: 4
|
124
123
|
summary: time tools for flor
|