fugit 1.3.4 → 1.3.9

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a10a8afb99cf16856e53766a86b1f0d13ddb0d2784c756cb525ceea8fbae7bd1
4
- data.tar.gz: dcd1d01a8f5f8fc819854f430e12fc6bf69850e64060cffbe93c595be78785a2
3
+ metadata.gz: ff6ed980e9137d5c51dda0b2ca0a66dffb35ae99352168a50e92fadbf272442a
4
+ data.tar.gz: '00977091b588564591b180debb728806bce562eaf2145b413e752c2eda90652e'
5
5
  SHA512:
6
- metadata.gz: 4b29f59bffe4f786b90c0f0a42c26c682e78b523183863f7cfc8c486ecca9056bb96f5eae92b2ee9446ece5c84a10aeddd9a52077eece5357af8985853da99ee
7
- data.tar.gz: a32ac9539496ff7f4bc1949f97eeefcf836a8f1607569080acafd2d338befd7cb97e10179ffee06d2da3ebead95830147f5f418a883a4bd2a77f549455761788
6
+ metadata.gz: 1c9290c85529fefb3ec1d8c664f4dabaa0e1bbe8d9430555a1af1c5c382e15afeb9f729fbc4b9eba00bf7db0a47625daf0f12a5328743b3f25af66728bd3d0b2
7
+ data.tar.gz: 67e65d620ebfb16c4c9d649e61ac743691c1bb77a2573ce750d8202bf5bb4f090a8d1feb86aab362b6916de9b028c3e7e00bdad8a708d3397736ede1c14f1b61
@@ -2,6 +2,33 @@
2
2
  # CHANGELOG.md
3
3
 
4
4
 
5
+ ## fugit 1.3.9 released 2020-09-17
6
+
7
+ * Prevent "New York skip", gh-43, thanks @honglooker
8
+
9
+
10
+ ## fugit 1.3.8 released 2020-08-06
11
+
12
+ * Parse 'every day at 8:30' and ' at 8:30 pm', gh-42
13
+
14
+
15
+ ## fugit 1.3.7 released 2020-08-05
16
+
17
+ * Parse 'every 12 hours at minute 50', gh-41
18
+
19
+
20
+ ## fugit 1.3.6 released 2020-06-01
21
+
22
+ * Introduce new nat syntaxed, gh-38
23
+ * Rework nat parser
24
+
25
+
26
+ ## fugit 1.3.5 released 2020-05-07
27
+
28
+ * Implement cron @noon, gh-37
29
+ * Normalize "every x", gh-37
30
+
31
+
5
32
  ## fugit 1.3.4 released 2020-04-06
6
33
 
7
34
  * Prevent #rough_frequency returning 0, gh-36
data/CREDITS.md CHANGED
@@ -1,10 +1,13 @@
1
1
 
2
2
  # fugit credits
3
3
 
4
+ * Honglooker https://github.com/honglooker gh-43, New York cron skip
5
+ * Jérôme Dalbert https://github.com/jeromedalbert gh-41, gh-42
6
+ * Danny Ben Shitrit https://github.com/DannyBen nat variants, gh-38
4
7
  * Dominik Sander https://github.com/dsander #rough_frequency 0, gh-36
5
8
  * Milovan Zogovic https://github.com/assembler Cron#match? vs TZ, gh-31
6
9
  * Jessica Stokes https://github.com/ticky 0-24 issue with cron, gh-30
7
- * Shai Coleman https://github.com/shaicoleman parse_nat enhancements, gh-24, gh-25, and gh-28
10
+ * Shai Coleman https://github.com/shaicoleman parse_nat enhancements, gh-24, gh-25, gh-28, and gh-37
8
11
  * Jan Stevens https://github.com/JanStevens Fugit.parse('every 15 minutes') gh-22
9
12
  * Fabio Pitino https://github.com/hspazio nil on February 30 gh-21
10
13
  * Cristian Oneț https://github.com/conet #previous_time vs 1/-1 endless loop gh-15
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.x.
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
@@ -232,6 +232,29 @@ p d.to_plain_s # => "2Y2M1D5h3600s"
232
232
  p Fugit::Duration.parse('1y2M1d4h').to_sec # => 36820800
233
233
  ```
234
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
+
235
258
  The `to_*_s` methods are also available as class methods:
236
259
  ```ruby
237
260
  p Fugit::Duration.to_plain_s('1y2M1d4h')
@@ -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.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'
@@ -1,7 +1,7 @@
1
1
 
2
2
  module Fugit
3
3
 
4
- VERSION = '1.3.4'
4
+ VERSION = '1.3.9'
5
5
  end
6
6
 
7
7
  require 'time'
@@ -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 ]
@@ -235,6 +236,8 @@ module Fugit
235
236
  # the translation occurs in the timezone of
236
237
  # this Fugit::Cron instance
237
238
 
239
+ zfrom = t.time.strftime('%z|%Z')
240
+
238
241
  loop do
239
242
 
240
243
  fail RuntimeError.new(
@@ -250,8 +253,14 @@ module Fugit
250
253
  min_match?(t) || (t.inc_min; next)
251
254
  sec_match?(t) || (t.inc_sec; next)
252
255
 
253
- st = t.time.strftime('%F|%T')
254
- (from, sfrom, ifrom = t.time, st, t.to_i; next) if st == sfrom
256
+ tt = t.time
257
+ st = tt.strftime('%F|%T')
258
+ zt = tt.strftime('%z|%Z')
259
+ #
260
+ if st == sfrom && zt != zfrom
261
+ from, sfrom, zfrom, ifrom = tt, st, zt, t.to_i
262
+ next
263
+ end
255
264
  #
256
265
  # when transitioning out of DST, this prevents #next_time from
257
266
  # yielding the same literal time twice in a row, see gh-6
@@ -756,7 +765,7 @@ module Fugit
756
765
 
757
766
  def rewrite_tz(t)
758
767
 
759
- s = t.string.strip
768
+ s = t.strim
760
769
  z = EtOrbi.get_tzone(s)
761
770
 
762
771
  [ s, z ]
@@ -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
- a = Parser.parse(s)
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
- dhs, aa = a
41
- .partition { |e| e[0] == :digital_hour }
42
- ms = dhs
43
- .inject({}) { |h, dh| (h[dh[1][0]] ||= []) << dh[1][1]; h }
44
- .values
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
- #if ms.size <= 1 || hs.size <= 1
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,461 @@ module Fugit
64
73
  end
65
74
  end
66
75
 
67
- def parse_cron(a, opts)
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
- h[:min] ||= [ 0 ]
92
- h[:min].uniq!
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
- h[:hou].uniq!;
95
- h[:hou].sort!
86
+ Fugit::Cron.parse(s.join(' '))
87
+ end
96
88
 
97
- h[:dow].sort! if h[:dow]
89
+ def eone(e); e1 = e[1]; e1 == 1 ? '*' : "*/#{e1}"; end
98
90
 
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]
91
+ def parse_interval_elt(e, opts, h)
105
92
 
106
- s = a.join(' ')
93
+ e1 = e[1]
107
94
 
108
- Fugit::Cron.parse(s)
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 process_duration(h, interval, value)
127
+ def parse_dow_list_elt(e, opts, h)
112
128
 
113
- send("process_duration_#{interval}", h, value)
129
+ h[:hms] ||= [ [ 0, 0 ] ]
130
+ h[:dow] = e[1..-1].collect(&:to_s).sort.join(',')
114
131
  end
115
132
 
116
- def process_duration_mon(h, value)
133
+ def parse_dow_range_elt(e, opts, h)
117
134
 
118
- h[:hou] = [ 0 ]
119
- h[:dom] = [ 1 ]
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 process_duration_day(h, value)
139
+ def parse_day_of_month_elt(e, opts, h)
124
140
 
125
- h[:hou] = [ 0 ]
126
- h[:dom] = [ value == 1 ? '*' : "*/#{value}" ]
141
+ h[:dom] = e[1..-1].join(',')
127
142
  end
128
143
 
129
- def process_duration_hou(h, value)
144
+ def parse_at_elt(e, opts, h)
130
145
 
131
- h[:hou] = [ value == 1 ? '*' : "*/#{value}" ]
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 process_duration_min(h, value)
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
- h[:hou] = [ '*' ]
137
- h[:min] = [ value == 1 ? '*' : "*/#{value}" ]
158
+ l = h[:hms].last
159
+ h[:sec] = l.pop if l.size > 2
138
160
  end
139
161
 
140
- def process_duration_sec(h, value)
162
+ def parse_tz_elt(e, opts, h)
141
163
 
142
- h[:hou] = [ '*' ]
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
- { 'noon' => [ 12, 0 ], 'midnight' => [ 0, 0 ] }
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, / *(am|pm)/i)
251
+ rex(:am_pm, i, /\s*(am|pm|dark)\s*/i)
165
252
  end
166
253
 
167
- def digital_hour(i)
168
- rex(:digital_hour, i, /(2[0-4]|[01][0-9]):?[0-5]\d/)
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)
169
266
  end
170
267
 
171
- def _simple_hour(i)
172
- rex(:sh, i, /(2[0-4]|[01]?[0-9])/)
268
+ def shour(i)
269
+ rex(:shour, i, /(2[0-4]|[01]?[0-9])/)
173
270
  end
174
271
  def simple_hour(i)
175
- seq(:simple_hour, i, :_simple_hour, :am_pm, '?')
272
+ seq(:simple_hour, i, :shour, :am_pm, '?')
176
273
  end
177
274
 
178
- def _numeral_hour(i)
179
- rex(:nh, i, /(#{NUMS.join('|')})/i)
275
+ def dig_hour_b(i); rex(nil, i, /(2[0-4]|[01][0-9]|[0-9]):[0-5]\d/); end
276
+ def dig_hour_a(i); rex(nil, i, /(2[0-4]|[01][0-9])[0-5]\d/); end
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, '?')
180
281
  end
181
- def numeral_hour(i)
182
- seq(:numeral_hour, i, :_numeral_hour, :am_pm, '?')
282
+
283
+ def at_point(i)
284
+ alt(nil, i,
285
+ :digital_hour, :simple_hour, :named_hour, :numeral_hour,
286
+ :named_digits)
183
287
  end
184
288
 
185
- def name_hour(i)
186
- rex(:name_hour, i, /(#{NHOURS.keys.join('|')})/i)
289
+ def weekday(i)
290
+ rex(:weekday, i, /(#{WEEKDAYS.reverse.join('|')})\s*/i)
187
291
  end
188
292
 
189
- def plain_day(i); rex(:plain_day, i, /day/i); end
190
- def biz_day(i); rex(:biz_day, i, /(biz|business|week) *day/i); end
191
- def name_day(i); rex(:name_day, i, /#{WEEKDAYS.reverse.join('|')}/i); end
293
+ def and_at(i)
294
+ seq(nil, i, :_and_or_comma, :at_point)
295
+ end
192
296
 
193
- def range_sep(i); rex(nil, i, / *- *| +(to|through) +/); end
297
+ def _intervals(i)
298
+ rex(:intervals, i,
299
+ /(
300
+ y(ears?)?|months?|w(eeks?)?|d(ays?)?|
301
+ h(ours?)?|m(in(ute)?s?)?|s(ec(ond)?s?)?
302
+ )(\s+|$)/ix)
303
+ end
194
304
 
195
- def day_range(i)
196
- seq(:day_range, i, :name_day, :range_sep, :name_day)
305
+ def sinterval(i)
306
+ rex(:sinterval, i,
307
+ /(year|month|week|day|hour|min(ute)?|sec(ond)?)(\s+|$)/i)
308
+ end
309
+ def ninterval(i)
310
+ seq(:ninterval, i, :integer, :_intervals)
197
311
  end
198
312
 
199
- def _tz_name(i)
200
- rex(nil, i, /[A-Z][a-zA-Z0-9+\-]+(\/[A-Z][a-zA-Z0-9+\-_]+){0,2}/)
313
+ def ordinal(i)
314
+ rex(:ordinal, i, /\s*(#{ORDINALS.keys.join('|')})\s*/)
201
315
  end
202
- def _tz_delta(i)
203
- rex(nil, i, /[-+]([01][0-9]|2[0-4]):?(00|15|30|45)/)
316
+
317
+ def _mod(i); rex(nil, i, /\s*month\s+on\s+days?\s+/i); end
318
+ def _oftm(i); rex(nil, i, /\s*(day\s)?\s*of\s+the\s+month\s*/i); end
319
+
320
+ def dom(i)
321
+ rex(:int, i, /([12][0-9]|3[01]|[0-9])/)
322
+ end
323
+ def and_or_dom(i)
324
+ seq(nil, i, :_and_or_comma, :dom)
325
+ end
326
+ def dom_list(i)
327
+ seq(:dom_list, i, :dom, :and_or_dom, '*')
204
328
  end
205
- def _tz(i); alt(:tz, i, :_tz_delta, :_tz_name); end
206
329
 
207
- def duration(i)
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)
330
+ def dom_mod(i) # every month on day
331
+ seq(:dom, i, :_mod, :dom_list)
332
+ end
333
+ def dom_noftm(i) # every nth of month
334
+ seq(:dom, i, :ordinal, :_oftm)
335
+ end
336
+ def day_of_month(i)
337
+ alt(nil, i, :dom_noftm, :dom_mod)
215
338
  end
216
339
 
217
- def flag(i); rex(:flag, i, /(every|from|at|after|on|in)/i); end
340
+ def dow_class(i)
341
+ rex(:dow_class, i, /(weekday)(\s+|$)/i)
342
+ end
218
343
 
219
- def datum(i)
220
- alt(nil, i,
221
- :day_range,
222
- :plain_day, :biz_day, :name_day,
223
- :_tz,
224
- :flag,
225
- :duration,
226
- :name_hour, :numeral_hour, :digital_hour, :simple_hour)
344
+ def dow(i)
345
+ seq(:dow, i, :weekday)
346
+ end
347
+ def and_or_dow(i)
348
+ seq(nil, i, :_and_or_comma, :dow)
349
+ end
350
+ def dow_list(i)
351
+ seq(:dow_list, i, :dow, :and_or_dow, '*')
352
+ end
353
+
354
+ def to_dow_range(i)
355
+ seq(:dow_range, i, :weekday, :_to_through, :weekday)
356
+ end
357
+ def dash_dow_range(i)
358
+ seq(:dow_range, i, :weekday, :_dash, :weekday)
359
+ end
360
+ def dow_range(i)
361
+ alt(nil, i, :to_dow_range, :dash_dow_range)
362
+ end
363
+
364
+ def day_of_week(i)
365
+ alt(nil, i, :dow_range, :dow_list, :dow_class)
366
+ end
367
+
368
+ def interval(i)
369
+ alt(nil, i, :sinterval, :ninterval)
370
+ end
371
+
372
+ def every_object(i)
373
+ alt(nil, i, :day_of_month, :interval, :day_of_week)
374
+ end
375
+ def from_object(i)
376
+ alt(nil, i, :interval, :to_dow_range)
377
+ end
378
+
379
+ def tz(i)
380
+ seq(nil, i, :_in, '?', :tzone)
381
+ end
382
+ def on(i)
383
+ seq(:on, i, :_on, :weekday, :at_point, :and_at, '*')
384
+ end
385
+ def at(i)
386
+ seq(:at, i, :_at_comma, :at_point, :and_at, '*')
387
+ end
388
+ def from(i)
389
+ seq(:from, i, :_from, :from_object)
390
+ end
391
+ def every(i)
392
+ seq(:every, i, :_every, :every_object)
393
+ end
394
+
395
+ def at_from(i)
396
+ seq(nil, i, :at, :from, :tz, '?')
397
+ end
398
+ def at_every(i)
399
+ seq(nil, i, :at, :every, :tz, '?')
400
+ end
401
+
402
+ def from_at(i)
403
+ seq(nil, i, :from, :at, '?', :tz, '?')
227
404
  end
228
405
 
229
- def sugar(i); rex(nil, i, /(and|or|[, \t]+)/i); end
406
+ def every_(i)
407
+ seq(nil, i, :every, :tz, '?')
408
+ end
409
+ def every_on(i)
410
+ seq(nil, i, :every, :on, :tz, '?')
411
+ end
412
+ def every_at(i)
413
+ seq(nil, i, :every, :at, :tz, '?')
414
+ end
230
415
 
231
- def elt(i); alt(nil, i, :sugar, :datum); end
232
- def nat(i); rep(:nat, i, :elt, 1); end
416
+ def nat(i)
417
+ alt(:nat, i,
418
+ :every_at, :every_on, :every_,
419
+ :from_at,
420
+ :at_every, :at_from)
421
+ end
233
422
 
234
423
  # rewrite parsed tree
235
424
 
236
- def rewrite_nat(t)
425
+ #def _rewrite_single(t)
426
+ # [ t.name, rewrite(t.sublookup(nil)) ]
427
+ #end
428
+ def _rewrite_children(t)
429
+ t.subgather(nil).collect { |tt| rewrite(tt) }
430
+ end
431
+ def _rewrite_multiple(t)
432
+ [ t.name, _rewrite_children(t) ]
433
+ end
434
+ def _rewrite_child(t)
435
+ rewrite(t.sublookup(nil))
436
+ end
437
+
438
+ def rewrite_int(t); t.string.to_i; end
439
+
440
+ def rewrite_tzone(t)
441
+
442
+ [ :tz, t.strim ]
443
+ end
444
+
445
+ def rewrite_sinterval(t)
446
+
447
+ [ :interval, 1, t.strim ]
448
+ end
449
+
450
+ def rewrite_ninterval(t)
451
+
452
+ [ :interval,
453
+ t.sublookup(:int).string.to_i,
454
+ t.sublookup(:intervals).strim ]
455
+ end
456
+
457
+ def rewrite_named_digit(t)
458
+
459
+ i = t.sublookup(:int).string.to_i
460
+
461
+ case n = t.sublookup(:dname).strim
462
+ when /^s/ then [ '*', '*', i ]
463
+ when /^m/ then [ '*', i ]
464
+ end
465
+ end
466
+
467
+ def rewrite_named_hour(t)
468
+ NHOURS[t.strim.downcase]
469
+ end
470
+ def rewrite_numeral_hour(t)
471
+ vs = t.subgather(nil).collect { |st| st.strim.downcase }
472
+ v = NUMS.index(vs[0])
473
+ v += 12 if vs[1] == 'pm'
474
+ m = NMINUTES[vs[2]] || 0
475
+ [ v, m ]
476
+ end
477
+ def rewrite_simple_hour(t)
478
+ vs = t.subgather(nil).collect { |st| st.strim.downcase }
479
+ v = vs[0].to_i
480
+ v += 12 if vs[1] == 'pm'
481
+ [ v, 0 ]
482
+ end
483
+ def rewrite_digital_hour(t)
484
+ m = t.string.match(/(\d\d?):?(\d\d)(\s+pm)?/i)
485
+ hou = m[1].to_i; hou += 12 if m[3] && hou < 12
486
+ min = m[2].to_i
487
+ [ hou, min ]
488
+ end
489
+
490
+ def rewrite_weekday(t)
491
+
492
+ WEEKDAYS.index(t.strim.downcase[0, 3])
493
+ end
494
+
495
+ def rewrite_ordinal(t); ORDINALS[t.strim]; end
496
+
497
+ def rewrite_dom(t)
237
498
 
238
499
  #Raabro.pp(t, colours: true)
239
- t
240
- .subgather(nil)
241
- .collect { |tt|
242
-
243
- k = tt.name
244
- v = tt.string.downcase
245
-
246
- case k
247
- when :tz
248
- [ k, [ tt.string.strip, EtOrbi.get_tzone(tt.string.strip) ] ]
249
- when :duration
250
- [ k, [ Fugit::Duration.parse(tt.string.strip) ] ]
251
- when :digital_hour
252
- v = v.gsub(/:/, '')
253
- [ k, [ v[0, 2], v[2, 2] ] ]
254
- when :name_hour
255
- [ :digital_hour, NHOURS[v] ]
256
- when :name_day
257
- [ k, WEEKDAYS.index(v[0, 3]) ]
258
- when :day_range
259
- [ k, tt.subgather(nil).collect { |st| st.string.downcase } ]
260
- when :numeral_hour, :simple_hour
261
- vs = tt.subgather(nil).collect { |ttt| ttt.string.downcase.strip }
262
- v = k == :simple_hour ? vs[0].to_i : NUMS.index(vs[0])
263
- v += 12 if vs[1] == 'pm'
264
- [ k, v ]
265
- else
266
- [ k, v ]
267
- end }
500
+ [ :day_of_month,
501
+ *_rewrite_children(t).flatten.select { |e| e.is_a?(Integer) } ]
502
+ end
503
+
504
+ alias rewrite_dow _rewrite_child
505
+
506
+ def rewrite_dom_list(t); [ :dom_list, *_rewrite_children(t) ]; end
507
+ def rewrite_dow_list(t); [ :dow_list, *_rewrite_children(t) ]; end
508
+
509
+ def rewrite_dow_class(t)
510
+
511
+ [ :dow_range, 1, 5 ] # only "weekday" for now
512
+ end
513
+
514
+ def rewrite_dow_range(t)
515
+
516
+ tts = t.subgather(nil)
517
+
518
+ [ :dow_range, rewrite(tts[0]), rewrite(tts[1]) ]
519
+ end
520
+
521
+ alias rewrite_on _rewrite_multiple
522
+ alias rewrite_at _rewrite_multiple
523
+
524
+ alias rewrite_from _rewrite_child
525
+ alias rewrite_every _rewrite_child
526
+
527
+ def rewrite_nat(t)
528
+
529
+ t.subgather(nil).collect { |tt| rewrite(tt) }
530
+ #.tap { |x| pp x }
268
531
  end
269
532
  end
270
533
  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
4
+ version: 1.3.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Mettraux
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-05 00:00:00.000000000 Z
11
+ date: 2020-09-16 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.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.1'
26
+ version: '1.3'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: et-orbi
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -117,7 +117,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
117
117
  - !ruby/object:Gem::Version
118
118
  version: '0'
119
119
  requirements: []
120
- rubygems_version: 3.0.3
120
+ rubygems_version: 3.1.2
121
121
  signing_key:
122
122
  specification_version: 4
123
123
  summary: time tools for flor