fugit 1.2.2 → 1.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c85cb2e1a6f9e87e56f09bd46ebc0f23cf41ebc9
4
- data.tar.gz: 2c7c9aeb2e20be568fd41d14bea36aa325077134
3
+ metadata.gz: 0e67fe1fbf7a5e0213b828cc78c59d6cf5b36a6e
4
+ data.tar.gz: 920ea06ef202094e9b1c9c719fb06c55e0926b51
5
5
  SHA512:
6
- metadata.gz: abdc614b1f22423e38cd28d89f3a75b7cb53f5c739f007645fc0957e09fde31b78ffa18ac80c85f3cd178ade3102b917edcc1ef540431be75edfb1db4f81ccb4
7
- data.tar.gz: df991f815915b44e0fb094a7dcda59f1b3e304c03d1b6ea708a8f6bfceedd1873ad481d1cde8f70db32465af2fe7e4cae34789a0e0344393361e98d79cdaa383
6
+ metadata.gz: d6f4c28d4e280b73a0b5a3c6bae84e328e06fa33cb573bc8fc1a3660e1c921dd666c793fd17a18f10bc4d630d5db2473c2d75659def60bb6fd7342c9e3dfbb51
7
+ data.tar.gz: 40ae39ba32717fcc2d0148d2e4a63a2cd179fa61ef5de13670057021b77a95c9bec1ae7f299a3117602e63a1b02c5b1de683748ef793fa74ec394a4a7861992c
@@ -2,6 +2,37 @@
2
2
  # CHANGELOG.md
3
3
 
4
4
 
5
+ ## fugit 1.3.3 released 2019-08-29
6
+
7
+ * Fix Cron#match?(t) with respect to the cron's timezone, gh-31
8
+
9
+
10
+ ## fugit 1.3.2 released 2019-08-14
11
+
12
+ * Allow for "* 0-24 * * *", gh-30
13
+
14
+
15
+ ## fugit 1.3.1 released 2019-07-27
16
+
17
+ * Fix nat parsing for 'every day at 18:00 and 18:15', gh-29
18
+ * and for 'every day at 18:00, 18:15, 20:00, and 20:15', gh-29
19
+ * Ensure multi: :fail doesn't force into multi, gh-28
20
+ * Fix nat parsing for 'every Fri-Sun at 18:00', gh-27
21
+
22
+
23
+ ## fugit 1.3.0 released 2019-07-21
24
+
25
+ * Introduce Fugit.parse_nat('every day at 18:00 and 19:15', multi: true)
26
+ * Rework AM/PM parsing
27
+
28
+
29
+ ## fugit 1.2.3 released 2019-07-16
30
+
31
+ * Allow for "from Monday to Friday at 19:22", gh-25
32
+ * Allow for "every Monday to Friday at 18:20", gh-25
33
+ * Allow for "every day at 18:00 and 20:00", gh-24
34
+
35
+
5
36
  ## fugit 1.2.2 released 2019-06-21
6
37
 
7
38
  * Fix Fugit.parse vs "every 15 minutes", gh-22
data/CREDITS.md CHANGED
@@ -1,6 +1,9 @@
1
1
 
2
2
  # fugit credits
3
3
 
4
+ * Milovan Zogovic https://github.com/assembler Cron#match? vs TZ, gh-31
5
+ * Jessica Stokes https://github.com/ticky 0-24 issue with cron, gh-30
6
+ * Shai Coleman https://github.com/shaicoleman parse_nat enhancements, gh-24, gh-25, and gh-28
4
7
  * Jan Stevens https://github.com/JanStevens Fugit.parse('every 15 minutes') gh-22
5
8
  * Fabio Pitino https://github.com/hspazio nil on February 30 gh-21
6
9
  * Cristian Oneț https://github.com/conet #previous_time vs 1/-1 endless loop gh-15
data/README.md CHANGED
@@ -54,6 +54,39 @@ Fugit.parse('2017-12-12 UTC').class # ==> ::EtOrbi::EoTime
54
54
  Fugit.parse('every day at noon').class # ==> ::Fugit::Cron
55
55
  ```
56
56
 
57
+ If fugit cannot extract a cron, duration or point in time out of the string, it will return nil.
58
+ ```ruby
59
+ Fugit.parse('nada')
60
+ # ==> nil
61
+ ```
62
+
63
+ ## `Fugit.do_parse(s)`
64
+
65
+ `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
+ ```ruby
67
+ Fugit.do_parse('nada')
68
+ # ==> /home/jmettraux/w/fugit/lib/fugit/parse.rb:32
69
+ # :in `do_parse': found no time information in "nada" (ArgumentError)
70
+ ```
71
+
72
+ ## parse_cron, parse_in, parse_at, parse_duration, and parse_nat
73
+
74
+ ```ruby
75
+ require 'fugit'
76
+
77
+ Fugit.parse_cron('0 0 1 jan *').class # ==> ::Fugit::Cron
78
+ Fugit.parse_duration('12y12M').class # ==> ::Fugit::Duration
79
+
80
+ Fugit.parse_at('2017-12-12').class # ==> ::EtOrbi::EoTime
81
+ Fugit.parse_at('2017-12-12 UTC').class # ==> ::EtOrbi::EoTime
82
+
83
+ Fugit.parse_nat('every day at noon').class # ==> ::Fugit::Cron
84
+ ```
85
+
86
+ ## do_parse_cron, do_parse_in, do_parse_at, do_parse_duration, and do_parse_nat
87
+
88
+ As `Fugit.parse(s)` returns nil when it doesn't grok its input, and `Fugit.do_parse(s)` fails when it doesn't grok, each of the `parse_` methods has its partner `do_parse_` method.
89
+
57
90
  ## `Fugit::Cron`
58
91
 
59
92
  A class `Fugit::Cron` to parse cron strings and then `#next_time` and `#previous_time` to compute the next or the previous occurrence respectively.
@@ -151,10 +184,12 @@ p d.to_long_s # => "2 years, 2 months, 1 day, and 5 hours"
151
184
  d += 3600
152
185
 
153
186
  p d.to_plain_s # => "2Y2M1D5h3600s"
187
+
188
+ p Fugit::Duration.parse('1y2M1d4h').to_sec # => 36820800
154
189
  ```
155
190
 
156
191
  The `to_*_s` methods are also available as class methods:
157
- ```
192
+ ```ruby
158
193
  p Fugit::Duration.to_plain_s('1y2M1d4h')
159
194
  # => "1Y2M1D4h"
160
195
  p Fugit::Duration.to_iso_s('1y2M1d4h')
@@ -199,6 +234,7 @@ Fugit::Nat.parse('every day at 5 pm') # ==> '0 17 * * *'
199
234
  Fugit::Nat.parse('every tuesday at 5 pm') # ==> '0 17 * * 2'
200
235
  Fugit::Nat.parse('every wed at 5 pm') # ==> '0 17 * * 3'
201
236
  Fugit::Nat.parse('every day at 16:30') # ==> '30 16 * * *'
237
+ Fugit::Nat.parse('every day at 16:00 and 18:00') # ==> '0 16,18 * * *'
202
238
  Fugit::Nat.parse('every day at noon') # ==> '0 12 * * *'
203
239
  Fugit::Nat.parse('every day at midnight') # ==> '0 0 * * *'
204
240
  Fugit::Nat.parse('every tuesday and monday at 5pm') # ==> '0 17 * * 1,2'
@@ -216,6 +252,21 @@ Directly with `Fugit.parse(s)` is OK too:
216
252
  Fugit.parse('every day at five') # ==> Fugit::Cron instance '0 5 * * *'
217
253
  ```
218
254
 
255
+ ### Ambiguous nats
256
+
257
+ Not all strings result in a clean, single, cron expression.
258
+
259
+ ```ruby
260
+ Fugit::Nat.parse('every day at 16:00 and 18:00', multi: true)
261
+ # ==> [ '0 16,18 * * *' ]
262
+ Fugit::Nat.parse('every day at 16:15 and 18:30')
263
+ # ==> [ '15 16 * * *' ]
264
+ Fugit::Nat.parse('every day at 16:15 and 18:30', multi: true)
265
+ # ==> [ '15 16 * * *', '30 18 * * *' ]
266
+ Fugit::Nat.parse('every day at 16:15 and 18:30', multi: :fail)
267
+ # ==> ArgumentError: multiple crons in "every day at 16:15 and 18:30" (15 16 * * * | 30 18 * * *)
268
+ ```
269
+
219
270
 
220
271
  ## LICENSE
221
272
 
@@ -1,7 +1,7 @@
1
1
 
2
2
  module Fugit
3
3
 
4
- VERSION = '1.2.2'
4
+ VERSION = '1.3.3'
5
5
  end
6
6
 
7
7
  require 'time'
@@ -197,7 +197,7 @@ module Fugit
197
197
 
198
198
  def match?(t)
199
199
 
200
- t = Fugit.do_parse_at(t)
200
+ t = Fugit.do_parse_at(t).translate(@timezone)
201
201
 
202
202
  month_match?(t) && day_match?(t) &&
203
203
  hour_match?(t) && min_match?(t) && sec_match?(t)
@@ -475,9 +475,50 @@ module Fugit
475
475
  sla = 1 if sla == nil
476
476
  sta = min if sta == nil
477
477
  edn = max if edn == nil
478
- sta, edn = edn, sta if sta > edn
479
478
 
480
- (sta..edn).step(sla).to_a
479
+ range(min, max, sta, edn, sla)
480
+ end
481
+
482
+ def range(min, max, sta, edn, sla)
483
+
484
+ fail ArgumentError.new(
485
+ 'both start and end must be negative in ' +
486
+ { min: min, max: max, sta: sta, edn: edn, sla: sla }.inspect
487
+ ) if (sta < 0 && edn > 0) || (edn < 0 && sta > 0)
488
+
489
+ #p({ min: min, max: max, sta: sta, edn: edn, sla: sla })
490
+ a = []
491
+
492
+ omin, omax = min, max
493
+ min, max = -max, -1 if sta < 0
494
+ #p({ min: min, max: max })
495
+
496
+ cur = sta
497
+
498
+ loop do
499
+
500
+ a << cur
501
+ break if cur == edn
502
+
503
+ cur += 1
504
+ if cur > max
505
+ cur = min
506
+ edn = edn - max - 1 if edn > max
507
+ end
508
+
509
+ fail RuntimeError.new(
510
+ "too many loops for " +
511
+ { min: omin, max: omax, sta: sta, edn: edn, sla: sla }.inspect +
512
+ " #range, breaking, " +
513
+ "please fill an issue at https://git.io/fjJC9"
514
+ ) if a.length > 2 * omax
515
+ # there is a #uniq afterwards, hence the 2* for 0-24 and friends
516
+ end
517
+
518
+ a.each_with_index
519
+ .select { |e, i| i % sla == 0 }
520
+ .collect(&:first)
521
+ .uniq
481
522
  end
482
523
 
483
524
  def compact(key)
@@ -529,7 +570,8 @@ module Fugit
529
570
  ((a || 0)..(z || (a ? a : 6))).step(sl < 1 ? 1 : sl)
530
571
  .each { |i| @weekdays << [ i ] }
531
572
  elsif z
532
- (a..z).each { |i| @weekdays << [ i ] }
573
+ z = z + 7 if a > z
574
+ (a..z).each { |i| @weekdays << [ (i > 6) ? i - 7 : i ] }
533
575
  elsif a
534
576
  @weekdays << [ a ]
535
577
  #else
@@ -538,6 +580,7 @@ module Fugit
538
580
 
539
581
  @weekdays.each { |wd| wd[0] = 0 if wd[0] == 7 } # turn sun7 into sun0
540
582
  @weekdays.uniq!
583
+ @weekdays.sort!
541
584
  @weekdays = nil if @weekdays.empty?
542
585
  end
543
586
 
@@ -685,7 +728,9 @@ module Fugit
685
728
 
686
729
  a = at ? rewrite_bound(k, at) : nil
687
730
  z = zt ? rewrite_bound(k, zt) : nil
688
- a, z = z, a if a && z && a > z
731
+
732
+ #a, z = z, a if a && z && a > z
733
+ # handled downstream since gh-27
689
734
 
690
735
  [ a, z, sl, ha, mo ]
691
736
  end
@@ -8,7 +8,7 @@ module Fugit
8
8
 
9
9
  class << self
10
10
 
11
- def parse(s)
11
+ def parse(s, opts={})
12
12
 
13
13
  return s if s.is_a?(Fugit::Cron) || s.is_a?(Fugit::Duration)
14
14
 
@@ -17,20 +17,54 @@ module Fugit
17
17
  #p s; Raabro.pp(Parser.parse(s, debug: 3), colours: true)
18
18
  a = Parser.parse(s)
19
19
 
20
- if a && a.include?([ :flag, 'every' ])
21
- parse_cron(a)
22
- else
23
- nil
24
- end
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
25
28
  end
26
29
 
27
- def do_parse(s)
30
+ def do_parse(s, opts={})
28
31
 
29
- parse(s) ||
32
+ parse(s, opts) ||
30
33
  fail(ArgumentError.new("could not parse a nat #{s.inspect}"))
31
34
  end
32
35
 
33
- def parse_cron(a)
36
+ protected
37
+
38
+ def parse_crons(s, a, opts)
39
+
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
45
+ .uniq
46
+
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
54
+
55
+ fail ArgumentError.new(
56
+ "multiple crons in #{s.inspect} " +
57
+ "(#{crons.collect(&:original).join(' | ')})"
58
+ ) if opts[:multi] == :fail && crons.size > 1
59
+
60
+ if opts[:multi] == true || (opts[:multi] && opts[:multi] != :fail)
61
+ crons
62
+ else
63
+ crons.first
64
+ end
65
+ end
66
+
67
+ def parse_cron(a, opts)
34
68
 
35
69
  h = { min: nil, hou: [], dom: nil, mon: nil, dow: nil }
36
70
  hkeys = h.keys
@@ -41,20 +75,25 @@ module Fugit
41
75
  elsif key == :simple_hour || key == :numeral_hour
42
76
  h[:hou] << val
43
77
  elsif key == :digital_hour
44
- h[:hou] = [ val[0] ]
45
- h[:min] = [ val[1] ]
78
+ (h[:hou] ||= []) << val[0].to_i
79
+ (h[:min] ||= []) << val[1].to_i
46
80
  elsif key == :name_day
47
81
  (h[:dow] ||= []) << val
48
- elsif key == :flag && val == 'pm' && h[:hou]
49
- h[:hou][-1] = h[:hou][-1] + 12
82
+ elsif key == :day_range
83
+ (h[:dow] ||= []) << val.collect { |v| v.to_s[0, 3] }.join('-')
50
84
  elsif key == :tz
51
85
  h[:tz] = val
52
86
  elsif key == :duration
53
87
  process_duration(h, *val[0].to_h.first)
54
88
  end
55
89
  end
90
+
56
91
  h[:min] ||= [ 0 ]
57
- h[:hou].sort! if h[:hou]
92
+ h[:min].uniq!
93
+
94
+ h[:hou].uniq!;
95
+ h[:hou].sort!
96
+
58
97
  h[:dow].sort! if h[:dow]
59
98
 
60
99
  a = hkeys
@@ -63,6 +102,7 @@ module Fugit
63
102
  (v && v.any?) ? v.collect(&:to_s).join(',') : '*' }
64
103
  a.insert(0, h[:sec]) if h[:sec]
65
104
  a << h[:tz].first if h[:tz]
105
+
66
106
  s = a.join(' ')
67
107
 
68
108
  Fugit::Cron.parse(s)
@@ -120,15 +160,28 @@ module Fugit
120
160
 
121
161
  # piece parsers bottom to top
122
162
 
163
+ def am_pm(i)
164
+ rex(:am_pm, i, / *(am|pm)/i)
165
+ end
166
+
123
167
  def digital_hour(i)
124
168
  rex(:digital_hour, i, /(2[0-4]|[01][0-9]):?[0-5]\d/)
125
169
  end
170
+
171
+ def _simple_hour(i)
172
+ rex(:sh, i, /(2[0-4]|[01]?[0-9])/)
173
+ end
126
174
  def simple_hour(i)
127
- rex(:simple_hour, i, /(2[0-4]|[01]?[0-9])/)
175
+ seq(:simple_hour, i, :_simple_hour, :am_pm, '?')
176
+ end
177
+
178
+ def _numeral_hour(i)
179
+ rex(:nh, i, /(#{NUMS.join('|')})/i)
128
180
  end
129
181
  def numeral_hour(i)
130
- rex(:numeral_hour, i, /(#{NUMS.join('|')})/i)
182
+ seq(:numeral_hour, i, :_numeral_hour, :am_pm, '?')
131
183
  end
184
+
132
185
  def name_hour(i)
133
186
  rex(:name_hour, i, /(#{NHOURS.keys.join('|')})/i)
134
187
  end
@@ -137,6 +190,12 @@ module Fugit
137
190
  def biz_day(i); rex(:biz_day, i, /(biz|business|week) *day/i); end
138
191
  def name_day(i); rex(:name_day, i, /#{WEEKDAYS.reverse.join('|')}/i); end
139
192
 
193
+ def range_sep(i); rex(nil, i, / *- *| +(to|through) +/); end
194
+
195
+ def day_range(i)
196
+ seq(:day_range, i, :name_day, :range_sep, :name_day)
197
+ end
198
+
140
199
  def _tz_name(i)
141
200
  rex(nil, i, /[A-Z][a-zA-Z0-9+\-]+(\/[A-Z][a-zA-Z0-9+\-_]+){0,2}/)
142
201
  end
@@ -155,10 +214,11 @@ module Fugit
155
214
  /ix)
156
215
  end
157
216
 
158
- def flag(i); rex(:flag, i, /(every|at|after|am|pm|on|in)/i); end
217
+ def flag(i); rex(:flag, i, /(every|from|at|after|on|in)/i); end
159
218
 
160
219
  def datum(i)
161
220
  alt(nil, i,
221
+ :day_range,
162
222
  :plain_day, :biz_day, :name_day,
163
223
  :_tz,
164
224
  :flag,
@@ -188,10 +248,6 @@ module Fugit
188
248
  [ k, [ tt.string.strip, EtOrbi.get_tzone(tt.string.strip) ] ]
189
249
  when :duration
190
250
  [ k, [ Fugit::Duration.parse(tt.string.strip) ] ]
191
- when :numeral_hour
192
- [ k, NUMS.index(v) ]
193
- when :simple_hour
194
- [ k, v.to_i ]
195
251
  when :digital_hour
196
252
  v = v.gsub(/:/, '')
197
253
  [ k, [ v[0, 2], v[2, 2] ] ]
@@ -199,6 +255,13 @@ module Fugit
199
255
  [ :digital_hour, NHOURS[v] ]
200
256
  when :name_day
201
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 ]
202
265
  else
203
266
  [ k, v ]
204
267
  end }
@@ -5,13 +5,13 @@ module Fugit
5
5
 
6
6
  def parse_cron(s); ::Fugit::Cron.parse(s); end
7
7
  def parse_duration(s); ::Fugit::Duration.parse(s); end
8
- def parse_nat(s); ::Fugit::Nat.parse(s); end
8
+ def parse_nat(s, opts={}); ::Fugit::Nat.parse(s, opts); end
9
9
  def parse_at(s); ::Fugit::At.parse(s); end
10
10
  def parse_in(s); parse_duration(s); end
11
11
 
12
12
  def do_parse_cron(s); ::Fugit::Cron.do_parse(s); end
13
13
  def do_parse_duration(s); ::Fugit::Duration.do_parse(s); end
14
- def do_parse_nat(s); ::Fugit::Nat.do_parse(s); end
14
+ def do_parse_nat(s, opts={}); ::Fugit::Nat.do_parse(s, opts); end
15
15
  def do_parse_at(s); ::Fugit::At.do_parse(s); end
16
16
  def do_parse_in(s); do_parse_duration(s); end
17
17
 
@@ -21,7 +21,7 @@ module Fugit
21
21
 
22
22
  (opts[:cron] != false && parse_cron(s)) ||
23
23
  (opts[:duration] != false && parse_duration(s)) ||
24
- (opts[:nat] != false && parse_nat(s)) ||
24
+ (opts[:nat] != false && parse_nat(s, opts)) ||
25
25
  (opts[:at] != false && parse_at(s)) ||
26
26
  nil
27
27
  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.2.2
4
+ version: 1.3.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Mettraux
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-06-21 00:00:00.000000000 Z
11
+ date: 2019-08-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: raabro