fugit 1.3.2 → 1.3.7
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 +69 -2
- data/fugit.gemspec +1 -1
- data/lib/fugit.rb +1 -1
- data/lib/fugit/cron.rb +15 -2
- data/lib/fugit/nat.rb +410 -153
- 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: d3e29ff88eb9d7ce1e5214af33683f577967b86ded3ee9f90be8c628fa982e40
|
4
|
+
data.tar.gz: 7adb64aaee08b7c354a3e619eda0edb96696ad4fd1d19d39c06e9a9768d058b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: db82aa99da670c23ba90424e015afcc8e0b5fff419d62806439bd94f171b8e0df18bbf0d3531343e814d2f9a1709a577f91f0d12024be3654df0f73922a255d5
|
7
|
+
data.tar.gz: 5bd2ae16ad1e244a6a4f26f24ba8f5291c0e1ee3271b5ccbe86c1dcd3131a383731829c838826f8dbc94ecd6e14fa6d918576a3e2347882fb635e50c8c9c3ce4
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,33 @@
|
|
2
2
|
# CHANGELOG.md
|
3
3
|
|
4
4
|
|
5
|
+
## fugit 1.3.7 released 2020-08-05
|
6
|
+
|
7
|
+
* Parse 'every 12 hours at minute 50', gh-41
|
8
|
+
|
9
|
+
|
10
|
+
## fugit 1.3.6 released 2020-06-01
|
11
|
+
|
12
|
+
* Introduce new nat syntaxed, gh-38
|
13
|
+
* Rework nat parser
|
14
|
+
|
15
|
+
|
16
|
+
## fugit 1.3.5 released 2020-05-07
|
17
|
+
|
18
|
+
* Implement cron @noon, gh-37
|
19
|
+
* Normalize "every x", gh-37
|
20
|
+
|
21
|
+
|
22
|
+
## fugit 1.3.4 released 2020-04-06
|
23
|
+
|
24
|
+
* Prevent #rough_frequency returning 0, gh-36
|
25
|
+
|
26
|
+
|
27
|
+
## fugit 1.3.3 released 2019-08-29
|
28
|
+
|
29
|
+
* Fix Cron#match?(t) with respect to the cron's timezone, gh-31
|
30
|
+
|
31
|
+
|
5
32
|
## fugit 1.3.2 released 2019-08-14
|
6
33
|
|
7
34
|
* Allow for "* 0-24 * * *", gh-30
|
data/CREDITS.md
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
|
2
2
|
# fugit credits
|
3
3
|
|
4
|
+
* Jérôme Dalbert https://github.com/jeromedalbert every 12h at min 50, gh-41
|
5
|
+
* Danny Ben Shitrit https://github.com/DannyBen nat variants, gh-38
|
6
|
+
* Dominik Sander https://github.com/dsander #rough_frequency 0, gh-36
|
7
|
+
* Milovan Zogovic https://github.com/assembler Cron#match? vs TZ, gh-31
|
4
8
|
* Jessica Stokes https://github.com/ticky 0-24 issue with cron, gh-30
|
5
|
-
* Shai Coleman https://github.com/shaicoleman parse_nat enhancements, gh-24, gh-25, and gh-
|
9
|
+
* Shai Coleman https://github.com/shaicoleman parse_nat enhancements, gh-24, gh-25, gh-28, and gh-37
|
6
10
|
* Jan Stevens https://github.com/JanStevens Fugit.parse('every 15 minutes') gh-22
|
7
11
|
* Fabio Pitino https://github.com/hspazio nil on February 30 gh-21
|
8
12
|
* 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
@@ -9,7 +9,7 @@ Time tools for [flor](https://github.com/floraison/flor) and the floraison group
|
|
9
9
|
|
10
10
|
It uses [et-orbi](https://github.com/floraison/et-orbi) to represent time instances and [raabro](https://github.com/floraison/raabro) as a basis for its parsers.
|
11
11
|
|
12
|
-
Fugit is a core dependency of [rufus-scheduler](https://github.com/jmettraux/rufus-scheduler) 3.5.
|
12
|
+
Fugit is a core dependency of [rufus-scheduler](https://github.com/jmettraux/rufus-scheduler) >= 3.5.
|
13
13
|
|
14
14
|
|
15
15
|
## Related projects
|
@@ -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)`
|
@@ -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
|
@@ -188,6 +232,29 @@ p d.to_plain_s # => "2Y2M1D5h3600s"
|
|
188
232
|
p Fugit::Duration.parse('1y2M1d4h').to_sec # => 36820800
|
189
233
|
```
|
190
234
|
|
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"
|
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
|
+
|
191
258
|
The `to_*_s` methods are also available as class methods:
|
192
259
|
```ruby
|
193
260
|
p Fugit::Duration.to_plain_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
|
|
@@ -744,7 +757,7 @@ module Fugit
|
|
744
757
|
|
745
758
|
def rewrite_tz(t)
|
746
759
|
|
747
|
-
s = t.
|
760
|
+
s = t.strim
|
748
761
|
z = EtOrbi.get_tzone(s)
|
749
762
|
|
750
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,455 @@ 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
|
76
|
+
def assemble_cron(h)
|
90
77
|
|
91
|
-
|
92
|
-
|
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]
|
93
85
|
|
94
|
-
|
95
|
-
|
86
|
+
Fugit::Cron.parse(s.join(' '))
|
87
|
+
end
|
96
88
|
|
97
|
-
|
89
|
+
def eone(e); e1 = e[1]; e1 == 1 ? '*' : "*/#{e1}"; end
|
98
90
|
|
99
|
-
|
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]
|
91
|
+
def parse_interval_elt(e, opts, h)
|
105
92
|
|
106
|
-
|
93
|
+
e1 = e[1]
|
107
94
|
|
108
|
-
|
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
|
+
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
|
109
125
|
end
|
110
126
|
|
111
|
-
def
|
127
|
+
def parse_dow_list_elt(e, opts, h)
|
112
128
|
|
113
|
-
|
129
|
+
h[:hms] ||= [ [ 0, 0 ] ]
|
130
|
+
h[:dow] = e[1..-1].collect(&:to_s).sort.join(',')
|
114
131
|
end
|
115
132
|
|
116
|
-
def
|
133
|
+
def parse_dow_range_elt(e, opts, h)
|
117
134
|
|
118
|
-
h[:
|
119
|
-
h[:
|
120
|
-
h[:mon] = [ value == 1 ? '*' : "*/#{value}" ]
|
135
|
+
h[:hms] ||= [ [ 0, 0 ] ]
|
136
|
+
h[:dow] = e[1] == e[2] ? e[1] : "#{e[1]}-#{e[2]}"
|
121
137
|
end
|
122
138
|
|
123
|
-
def
|
139
|
+
def parse_day_of_month_elt(e, opts, h)
|
124
140
|
|
125
|
-
h[:
|
126
|
-
h[:dom] = [ value == 1 ? '*' : "*/#{value}" ]
|
141
|
+
h[:dom] = e[1..-1].join(',')
|
127
142
|
end
|
128
143
|
|
129
|
-
def
|
144
|
+
def parse_at_elt(e, opts, h)
|
130
145
|
|
131
|
-
h[:
|
146
|
+
(h[:hms] ||= []).concat(e[1])
|
147
|
+
|
148
|
+
l = h[:hms].last
|
149
|
+
h[:sec] = l.pop if l.size > 2
|
132
150
|
end
|
133
151
|
|
134
|
-
def
|
152
|
+
def parse_on_elt(e, opts, h)
|
153
|
+
|
154
|
+
e1 = e[1]
|
155
|
+
h[:dow] = e1[0]
|
156
|
+
h[:hms] = [ e1[1] ]
|
135
157
|
|
136
|
-
|
137
|
-
h[:
|
158
|
+
l = h[:hms].last
|
159
|
+
h[:sec] = l.pop if l.size > 2
|
138
160
|
end
|
139
161
|
|
140
|
-
def
|
162
|
+
def parse_tz_elt(e, opts, h)
|
141
163
|
|
142
|
-
h[:
|
143
|
-
h[:min] = [ '*' ]
|
144
|
-
h[:sec] = [ value == 1 ? '*' : "*/#{value}" ]
|
164
|
+
h[:tz] = e[1]
|
145
165
|
end
|
146
166
|
end
|
147
167
|
|
148
168
|
module Parser include Raabro
|
149
169
|
|
150
170
|
NUMS = %w[
|
151
|
-
zero
|
152
|
-
one two three four five six seven eight nine
|
153
|
-
ten eleven twelve ]
|
171
|
+
zero one two three four five six seven eight nine ten eleven twelve ]
|
154
172
|
|
155
173
|
WEEKDAYS =
|
156
174
|
Fugit::Cron::Parser::WEEKDS + Fugit::Cron::Parser::WEEKDAYS
|
157
175
|
|
158
|
-
NHOURS =
|
159
|
-
|
176
|
+
NHOURS = {
|
177
|
+
'noon' => [ 12, 0 ],
|
178
|
+
'midnight' => [ 0, 0 ], 'oh' => [ 0, 0 ] }
|
179
|
+
NMINUTES = {
|
180
|
+
"o'clock" => 0, 'five' => 5,
|
181
|
+
'ten' => 10, 'fifteen' => 15,
|
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
|
160
200
|
|
161
201
|
# piece parsers bottom to top
|
162
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)
|
214
|
+
end
|
215
|
+
def _at_comma(i)
|
216
|
+
rex(nil, i, /\s*(at\s|,|)\s*/i)
|
217
|
+
end
|
218
|
+
def _to_through(i)
|
219
|
+
rex(nil, i, /\s*(to|through)\s+/i)
|
220
|
+
end
|
221
|
+
|
222
|
+
def integer(i); rex(:int, i, /\d+\s*/); end
|
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+|$)/)
|
227
|
+
end
|
228
|
+
def tz_delta(i)
|
229
|
+
rex(nil, i,
|
230
|
+
/\s*[-+]([01][0-9]|2[0-4]):?(00|15|30|45)(\s+|$)/)
|
231
|
+
end
|
232
|
+
def tzone(i)
|
233
|
+
alt(:tzone, i, :tz_delta, :tz_name)
|
234
|
+
end
|
235
|
+
|
236
|
+
def and_named_digits(i)
|
237
|
+
rex(:xxx, i, 'TODO')
|
238
|
+
end
|
239
|
+
|
240
|
+
def dname(i)
|
241
|
+
rex(:dname, i, /(s(ec(onds?)?)?|m(in(utes?)?)?)\s+/i)
|
242
|
+
end
|
243
|
+
def named_digit(i)
|
244
|
+
seq(:named_digit, i, :dname, :integer)
|
245
|
+
end
|
246
|
+
def named_digits(i)
|
247
|
+
seq(nil, i, :named_digit, '+', :and_named_digits, '*')
|
248
|
+
end
|
249
|
+
|
163
250
|
def am_pm(i)
|
164
|
-
rex(:am_pm, i,
|
251
|
+
rex(:am_pm, i, /\s*(am|pm|dark)\s*/i)
|
252
|
+
end
|
253
|
+
|
254
|
+
def nminute(i)
|
255
|
+
rex(:nminute, i, /(#{NMINUTES.keys.join('|')})\s*/i)
|
256
|
+
end
|
257
|
+
def nhour(i)
|
258
|
+
rex(:nhour, i, /(#{NUMS.join('|')})\s*/i)
|
259
|
+
end
|
260
|
+
def numeral_hour(i)
|
261
|
+
seq(:numeral_hour, i, :nhour, :am_pm, '?', :nminute, '?')
|
262
|
+
end
|
263
|
+
|
264
|
+
def named_hour(i)
|
265
|
+
rex(:named_hour, i, /(#{NHOURS.keys.join('|')})/i)
|
266
|
+
end
|
267
|
+
|
268
|
+
def shour(i)
|
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, '?')
|
165
273
|
end
|
166
274
|
|
167
275
|
def digital_hour(i)
|
168
276
|
rex(:digital_hour, i, /(2[0-4]|[01][0-9]):?[0-5]\d/)
|
169
277
|
end
|
170
278
|
|
171
|
-
def
|
172
|
-
|
279
|
+
def at_point(i)
|
280
|
+
alt(nil, i,
|
281
|
+
:digital_hour, :simple_hour, :named_hour, :numeral_hour,
|
282
|
+
:named_digits)
|
173
283
|
end
|
174
|
-
|
175
|
-
|
284
|
+
|
285
|
+
def weekday(i)
|
286
|
+
rex(:weekday, i, /(#{WEEKDAYS.reverse.join('|')})\s*/i)
|
176
287
|
end
|
177
288
|
|
178
|
-
def
|
179
|
-
|
289
|
+
def and_at(i)
|
290
|
+
seq(nil, i, :_and_or_comma, :at_point)
|
180
291
|
end
|
181
|
-
|
182
|
-
|
292
|
+
|
293
|
+
def _intervals(i)
|
294
|
+
rex(:intervals, i,
|
295
|
+
/(
|
296
|
+
y(ears?)?|months?|w(eeks?)?|d(ays?)?|
|
297
|
+
h(ours?)?|m(in(ute)?s?)?|s(ec(ond)?s?)?
|
298
|
+
)(\s+|$)/ix)
|
183
299
|
end
|
184
300
|
|
185
|
-
def
|
186
|
-
rex(:
|
301
|
+
def sinterval(i)
|
302
|
+
rex(:sinterval, i,
|
303
|
+
/(year|month|week|day|hour|min(ute)?|sec(ond)?)(\s+|$)/i)
|
304
|
+
end
|
305
|
+
def ninterval(i)
|
306
|
+
seq(:ninterval, i, :integer, :_intervals)
|
187
307
|
end
|
188
308
|
|
189
|
-
def
|
190
|
-
|
191
|
-
|
309
|
+
def ordinal(i)
|
310
|
+
rex(:ordinal, i, /\s*(#{ORDINALS.keys.join('|')})\s*/)
|
311
|
+
end
|
192
312
|
|
193
|
-
def
|
313
|
+
def _mod(i); rex(nil, i, /\s*month\s+on\s+days?\s+/i); end
|
314
|
+
def _oftm(i); rex(nil, i, /\s*(day\s)?\s*of\s+the\s+month\s*/i); end
|
194
315
|
|
195
|
-
def
|
196
|
-
|
316
|
+
def dom(i)
|
317
|
+
rex(:int, i, /([12][0-9]|3[01]|[0-9])/)
|
318
|
+
end
|
319
|
+
def and_or_dom(i)
|
320
|
+
seq(nil, i, :_and_or_comma, :dom)
|
321
|
+
end
|
322
|
+
def dom_list(i)
|
323
|
+
seq(:dom_list, i, :dom, :and_or_dom, '*')
|
197
324
|
end
|
198
325
|
|
199
|
-
def
|
200
|
-
|
326
|
+
def dom_mod(i) # every month on day
|
327
|
+
seq(:dom, i, :_mod, :dom_list)
|
328
|
+
end
|
329
|
+
def dom_noftm(i) # every nth of month
|
330
|
+
seq(:dom, i, :ordinal, :_oftm)
|
201
331
|
end
|
202
|
-
def
|
203
|
-
|
332
|
+
def day_of_month(i)
|
333
|
+
alt(nil, i, :dom_noftm, :dom_mod)
|
204
334
|
end
|
205
|
-
def _tz(i); alt(:tz, i, :_tz_delta, :_tz_name); end
|
206
335
|
|
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)
|
336
|
+
def dow_class(i)
|
337
|
+
rex(:dow_class, i, /(weekday)(\s+|$)/i)
|
215
338
|
end
|
216
339
|
|
217
|
-
def
|
340
|
+
def dow(i)
|
341
|
+
seq(:dow, i, :weekday)
|
342
|
+
end
|
343
|
+
def and_or_dow(i)
|
344
|
+
seq(nil, i, :_and_or_comma, :dow)
|
345
|
+
end
|
346
|
+
def dow_list(i)
|
347
|
+
seq(:dow_list, i, :dow, :and_or_dow, '*')
|
348
|
+
end
|
218
349
|
|
219
|
-
def
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
350
|
+
def to_dow_range(i)
|
351
|
+
seq(:dow_range, i, :weekday, :_to_through, :weekday)
|
352
|
+
end
|
353
|
+
def dash_dow_range(i)
|
354
|
+
seq(:dow_range, i, :weekday, :_dash, :weekday)
|
355
|
+
end
|
356
|
+
def dow_range(i)
|
357
|
+
alt(nil, i, :to_dow_range, :dash_dow_range)
|
358
|
+
end
|
359
|
+
|
360
|
+
def day_of_week(i)
|
361
|
+
alt(nil, i, :dow_range, :dow_list, :dow_class)
|
362
|
+
end
|
363
|
+
|
364
|
+
def interval(i)
|
365
|
+
alt(nil, i, :sinterval, :ninterval)
|
366
|
+
end
|
367
|
+
|
368
|
+
def every_object(i)
|
369
|
+
alt(nil, i, :day_of_month, :interval, :day_of_week)
|
370
|
+
end
|
371
|
+
def from_object(i)
|
372
|
+
alt(nil, i, :interval, :to_dow_range)
|
373
|
+
end
|
374
|
+
|
375
|
+
def tz(i)
|
376
|
+
seq(nil, i, :_in, '?', :tzone)
|
377
|
+
end
|
378
|
+
def on(i)
|
379
|
+
seq(:on, i, :_on, :weekday, :at_point, :and_at, '*')
|
380
|
+
end
|
381
|
+
def at(i)
|
382
|
+
seq(:at, i, :_at_comma, :at_point, :and_at, '*')
|
383
|
+
end
|
384
|
+
def from(i)
|
385
|
+
seq(:from, i, :_from, :from_object)
|
386
|
+
end
|
387
|
+
def every(i)
|
388
|
+
seq(:every, i, :_every, :every_object)
|
389
|
+
end
|
390
|
+
|
391
|
+
def at_from(i)
|
392
|
+
seq(nil, i, :at, :from, :tz, '?')
|
393
|
+
end
|
394
|
+
def at_every(i)
|
395
|
+
seq(nil, i, :at, :every, :tz, '?')
|
396
|
+
end
|
397
|
+
|
398
|
+
def from_at(i)
|
399
|
+
seq(nil, i, :from, :at, '?', :tz, '?')
|
227
400
|
end
|
228
401
|
|
229
|
-
def
|
402
|
+
def every_(i)
|
403
|
+
seq(nil, i, :every, :tz, '?')
|
404
|
+
end
|
405
|
+
def every_on(i)
|
406
|
+
seq(nil, i, :every, :on, :tz, '?')
|
407
|
+
end
|
408
|
+
def every_at(i)
|
409
|
+
seq(nil, i, :every, :at, :tz, '?')
|
410
|
+
end
|
230
411
|
|
231
|
-
def
|
232
|
-
|
412
|
+
def nat(i)
|
413
|
+
alt(:nat, i,
|
414
|
+
:every_at, :every_on, :every_,
|
415
|
+
:from_at,
|
416
|
+
:at_every, :at_from)
|
417
|
+
end
|
233
418
|
|
234
419
|
# rewrite parsed tree
|
235
420
|
|
236
|
-
def
|
421
|
+
#def _rewrite_single(t)
|
422
|
+
# [ t.name, rewrite(t.sublookup(nil)) ]
|
423
|
+
#end
|
424
|
+
def _rewrite_children(t)
|
425
|
+
t.subgather(nil).collect { |tt| rewrite(tt) }
|
426
|
+
end
|
427
|
+
def _rewrite_multiple(t)
|
428
|
+
[ t.name, _rewrite_children(t) ]
|
429
|
+
end
|
430
|
+
def _rewrite_child(t)
|
431
|
+
rewrite(t.sublookup(nil))
|
432
|
+
end
|
433
|
+
|
434
|
+
def rewrite_int(t); t.string.to_i; end
|
435
|
+
|
436
|
+
def rewrite_tzone(t)
|
437
|
+
|
438
|
+
[ :tz, t.strim ]
|
439
|
+
end
|
440
|
+
|
441
|
+
def rewrite_sinterval(t)
|
442
|
+
|
443
|
+
[ :interval, 1, t.strim ]
|
444
|
+
end
|
445
|
+
|
446
|
+
def rewrite_ninterval(t)
|
447
|
+
|
448
|
+
[ :interval,
|
449
|
+
t.sublookup(:int).string.to_i,
|
450
|
+
t.sublookup(:intervals).strim ]
|
451
|
+
end
|
452
|
+
|
453
|
+
def rewrite_named_digit(t)
|
454
|
+
|
455
|
+
i = t.sublookup(:int).string.to_i
|
456
|
+
|
457
|
+
case n = t.sublookup(:dname).strim
|
458
|
+
when /^s/ then [ '*', '*', i ]
|
459
|
+
when /^m/ then [ '*', i ]
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
def rewrite_named_hour(t)
|
464
|
+
NHOURS[t.strim.downcase]
|
465
|
+
end
|
466
|
+
def rewrite_numeral_hour(t)
|
467
|
+
vs = t.subgather(nil).collect { |st| st.strim.downcase }
|
468
|
+
v = NUMS.index(vs[0])
|
469
|
+
v += 12 if vs[1] == 'pm'
|
470
|
+
m = NMINUTES[vs[2]] || 0
|
471
|
+
[ v, m ]
|
472
|
+
end
|
473
|
+
def rewrite_simple_hour(t)
|
474
|
+
vs = t.subgather(nil).collect { |st| st.strim.downcase }
|
475
|
+
v = vs[0].to_i
|
476
|
+
v += 12 if vs[1] == 'pm'
|
477
|
+
[ v, 0 ]
|
478
|
+
end
|
479
|
+
def rewrite_digital_hour(t)
|
480
|
+
m = t.string.match(/(\d\d?):?(\d\d)/)
|
481
|
+
[ m[1].to_i, m[2].to_i ]
|
482
|
+
end
|
483
|
+
|
484
|
+
def rewrite_weekday(t)
|
485
|
+
|
486
|
+
WEEKDAYS.index(t.strim.downcase[0, 3])
|
487
|
+
end
|
488
|
+
|
489
|
+
def rewrite_ordinal(t); ORDINALS[t.strim]; end
|
490
|
+
|
491
|
+
def rewrite_dom(t)
|
237
492
|
|
238
493
|
#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
|
-
|
494
|
+
[ :day_of_month,
|
495
|
+
*_rewrite_children(t).flatten.select { |e| e.is_a?(Integer) } ]
|
496
|
+
end
|
497
|
+
|
498
|
+
alias rewrite_dow _rewrite_child
|
499
|
+
|
500
|
+
def rewrite_dom_list(t); [ :dom_list, *_rewrite_children(t) ]; end
|
501
|
+
def rewrite_dow_list(t); [ :dow_list, *_rewrite_children(t) ]; end
|
502
|
+
|
503
|
+
def rewrite_dow_class(t)
|
504
|
+
|
505
|
+
[ :dow_range, 1, 5 ] # only "weekday" for now
|
506
|
+
end
|
507
|
+
|
508
|
+
def rewrite_dow_range(t)
|
509
|
+
|
510
|
+
tts = t.subgather(nil)
|
511
|
+
|
512
|
+
[ :dow_range, rewrite(tts[0]), rewrite(tts[1]) ]
|
513
|
+
end
|
514
|
+
|
515
|
+
alias rewrite_on _rewrite_multiple
|
516
|
+
alias rewrite_at _rewrite_multiple
|
517
|
+
|
518
|
+
alias rewrite_from _rewrite_child
|
519
|
+
alias rewrite_every _rewrite_child
|
520
|
+
|
521
|
+
def rewrite_nat(t)
|
522
|
+
|
523
|
+
t.subgather(nil).collect { |tt| rewrite(tt) }
|
524
|
+
#.tap { |x| pp x }
|
268
525
|
end
|
269
526
|
end
|
270
527
|
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.7
|
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-08-05 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.1.2
|
122
121
|
signing_key:
|
123
122
|
specification_version: 4
|
124
123
|
summary: time tools for flor
|