fugit 1.11.2 → 1.12.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c0951fa769cae432ad6e27c721a2abd80464c0d664a9254ba034de7dfcaaef23
4
- data.tar.gz: 27bf22062b2e54764ae30cf68d39e8f514a5e5fdb43034a33b738ae3be9326b8
3
+ metadata.gz: 15a3ea665c020e35ce51dcbc82be9a74e6f4abcaa2441316b514c630ffdbc094
4
+ data.tar.gz: f99246722de255325b57ea46f7b3f24bbfc11d65f1eb98fde9a2a43a3c249df0
5
5
  SHA512:
6
- metadata.gz: 5ceb6ff8033f80397465ddbb2b0d8ba17739208c7d0ecf3ce594fde86912c7d6ef8cccbff4b1f4e094bfa4fee6eeedbf9a3e136ba7732894e810c63a584442b8
7
- data.tar.gz: 24ad8e57d86132aac3231e56f162abc5454d608bab8fa61d90c22ea9af334b30a3b32d072db72fa425cce244f38da18dfbb814a2b0e23bd588b5d86006ff5f4e
6
+ metadata.gz: cc55e7332f5f111ddf27d145fed7fbfeaeed30f492ba343d1f4067e776c96874b42bad9488b9683203040cc50333f74bab56fa6e61744ef92fb059f2a2c9f076
7
+ data.tar.gz: 5ee65240dd04fddb7777bee327f8fee0cfef5260ed55db9ec71ece64c5e5e6b53283aa7d26ba52facbca3882aee0920ff0dd3715fea5ad9153caca82ea712450
data/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
  # CHANGELOG.md
3
3
 
4
4
 
5
+ ## fugit 1.12.1 released 2025-10-14
6
+
7
+ * Fix Fugit::Cron#to_cron_s to include & if @day_and
8
+
9
+
10
+ ## fugit 1.12.0 released 2025-09-30
11
+
12
+ * Upgrade et-orbi to ~> 1.4.0 for EtOrbi.rweek_ref=, gh-114
13
+ this changes the rweek reference point to 2018-12-31 (Monday)
14
+ * Fix `12 0 * * wed%4+1,wed%4` issue where 1 wed was ignored, gh-114
15
+
16
+
5
17
  ## fugit 1.11.2 released 2025-08-22
6
18
 
7
19
  * Fix "every day at midnight America/Los_Angeles", gh-113, Mark R. James
data/CREDITS.md CHANGED
@@ -1,6 +1,8 @@
1
1
 
2
2
  # fugit credits
3
3
 
4
+ * Eric Claerhout https://github.com/swebra gh-114 rweek readme clarity
5
+ * Hugh Kelsey https://github.com/hughkelsey gh-114 rweek/rday / wed%4+1,wed%4
4
6
  * Mark R. James, https://github.com/mrj, AM vs America/Los_Angeles, gh-113
5
7
  * Tejas Bubane, https://github.com/tejasbubane, r3.4 in test matrix, gh-109
6
8
  * Luis Castillo, https://github.com/lekastillo, nice_hash gh-108
data/README.md CHANGED
@@ -296,43 +296,188 @@ The hash extension can only be used in the day-of-week field.
296
296
 
297
297
  ### the modulo extension
298
298
 
299
- Fugit, since 1.1.10, also understands cron strings like "`9 0 * * sun%2`" which can be read as "every other Sunday at 9am".
299
+ Since 1.1.10, fugit also understands cron strings like `9 0 * * sun%2` which can be read as "every other Sunday at 9am" or `12 0 * * mon%4` for "every fourth monday at noon".
300
300
 
301
301
  The modulo extension can only be used in the day-of-week field.
302
302
 
303
303
  For odd Sundays, one can write `9 0 * * sun%2+1`.
304
304
 
305
- It can be combined, as in `9 0 * * sun%2,tue%3+2`
305
+ It can be combined, as in `9 0 * * sun%2,tue%3+2`, which will match every other Sunday and 1 in 3 Tuesdays (with an offset of 2).
306
306
 
307
- But what does it reference to? It starts at 1 on 2019-01-01 (in the EtOrbi instance timezone, not the UTC "timezone").
307
+ What does `sun%2` actually mean?
308
+ ```ruby
309
+ t.wday == 0 && t.rweek % 2 == 0
310
+ ```
308
311
 
312
+ What does `tue%3+2` mean?
309
313
  ```ruby
310
- require 'et-orbi' # >= 1.1.8
314
+ t.wday == 2 && t.rweek % 3 == 2
315
+ ```
311
316
 
312
- # the reference
313
- p EtOrbi.parse('2019-01-01').wday # => 2
314
- p EtOrbi.parse('2019-01-01').rweek # => 1
315
- p EtOrbi.parse('2019-01-01').rweek % 2 # => 1
317
+ #### et-orbi < 1.4.0 : reference set on Tuesday 2019-01-01
316
318
 
317
- # today (as of this coding...)
318
- p EtOrbi.parse('2019-04-11').wday # => 4
319
- p EtOrbi.parse('2019-04-11').rweek # => 15
320
- p EtOrbi.parse('2019-04-11').rweek % 2 # => 1
319
+ The original implementation of `#rweek` (and `#rday`) found in [et-orbi](https://github.com/floraison/et-orbi) was initially pointing to "Tuesday 2019-01-01" and it was set as rday 1 and rweek 1.
321
320
 
322
- c = Fugit.parse('* * * * tue%2')
323
- c.match?('2019-01-01') # => false, since rweek % 2 == 1
324
- c.match?('2019-01-08') # => true, since rweek % 2 == 0
321
+ Consider this iteration through the days around 2019-01-01.
322
+ ```ruby
323
+ require 'fugit'
325
324
 
326
- c = Fugit.parse('* * * * tue%2+1')
327
- c.match?('2019-01-01') # => true, since (rweek + 1) % 2 == 0
328
- c.match?('2019-01-08') # => false, since (rweek + 1) % 2 == 1
325
+ t = EtOrbi.parse('2018-12-28 12:00')
329
326
 
330
- # ...
327
+ 15.times do |i|
328
+
329
+ puts " * %14s / rday: %5d / rweek: %5d" % [
330
+ t.strftime('%F %a'), t.rday, t.rweek ]
331
+
332
+ w = t.rweek
333
+ t = t.add(24 * 3600)
334
+ puts if t.rweek != w
335
+
336
+ if i == 7
337
+ puts "\n (...)\n\n"
338
+ t = EtOrbi.parse('2025-10-04 12:00')
339
+ end
340
+ end
331
341
  ```
332
342
 
333
- `sun%2` matches if Sunday and `current_date.rweek % 2 == 0`
334
- `tue%3+2` matches if Tuesday and `current_date.rweek + 2 % 3 == 0`
335
- `tue%x+y` matches if Tuesday and `current_date.rweek + y % x == 0`
343
+ For et-orbi 1.2.11, it yields:
344
+ ```
345
+ * 2018-12-28 Fri / rday: -3 / rweek: 0
346
+ * 2018-12-29 Sat / rday: -2 / rweek: 0
347
+ * 2018-12-30 Sun / rday: -1 / rweek: 0
348
+ * 2018-12-31 Mon / rday: 0 / rweek: 0
349
+
350
+ * 2019-01-01 Tue / rday: 1 / rweek: 1
351
+ * 2019-01-02 Wed / rday: 2 / rweek: 1
352
+ * 2019-01-03 Thu / rday: 3 / rweek: 1
353
+ * 2019-01-04 Fri / rday: 4 / rweek: 1
354
+ * 2019-01-05 Sat / rday: 5 / rweek: 1
355
+
356
+ (...)
357
+
358
+ * 2025-10-04 Sat / rday: 2469 / rweek: 353
359
+ * 2025-10-05 Sun / rday: 2470 / rweek: 353
360
+ * 2025-10-06 Mon / rday: 2471 / rweek: 353
361
+
362
+ * 2025-10-07 Tue / rday: 2472 / rweek: 354
363
+ * 2025-10-08 Wed / rday: 2473 / rweek: 354
364
+ * 2025-10-09 Thu / rday: 2474 / rweek: 354
365
+ ```
366
+
367
+ This [was problematic](https://github.com/floraison/fugit/issues/114), since the week started on, well, Tuesday.
368
+
369
+
370
+ #### et-orbi >= 1.4.0 : reference set on Monday 2018-12-31
371
+
372
+ Since 1.4.0, et-orbi starts by default on Monday (2018-12-31), as rday 0 with rweek 0.
373
+
374
+ Thus, the above code yields:
375
+ ```
376
+ * 2018-12-28 Fri / rday: -3 / rweek: -1
377
+ * 2018-12-29 Sat / rday: -2 / rweek: -1
378
+ * 2018-12-30 Sun / rday: -1 / rweek: -1
379
+
380
+ * 2018-12-31 Mon / rday: 0 / rweek: 0
381
+ * 2019-01-01 Tue / rday: 1 / rweek: 0
382
+ * 2019-01-02 Wed / rday: 2 / rweek: 0
383
+ * 2019-01-03 Thu / rday: 3 / rweek: 0
384
+ * 2019-01-04 Fri / rday: 4 / rweek: 0
385
+ * 2019-01-05 Sat / rday: 5 / rweek: 0
386
+
387
+ (...)
388
+
389
+ * 2025-10-04 Sat / rday: 2469 / rweek: 352
390
+ * 2025-10-05 Sun / rday: 2470 / rweek: 352
391
+
392
+ * 2025-10-06 Mon / rday: 2471 / rweek: 353
393
+ * 2025-10-07 Tue / rday: 2472 / rweek: 353
394
+ * 2025-10-08 Wed / rday: 2473 / rweek: 353
395
+ * 2025-10-09 Thu / rday: 2474 / rweek: 353
396
+ ```
397
+
398
+
399
+ #### modulo and et-orbi >= 1.4.0 sanity check
400
+
401
+ Given the cron `"0 12 * * mon%2,wed%3+1"`, here is a piece of code that considers a range of 44 days and tells in its last column if YES or no if each of the days matches the cron.
402
+
403
+ ```ruby
404
+ require 'fugit'
405
+
406
+ c = Fugit.parse_cron('0 12 * * mon%2,wed%3+1')
407
+
408
+ t = EtOrbi.parse('2025-09-20 12:00')
409
+
410
+ 44.times do
411
+
412
+ wd = t.strftime('%a')
413
+ wd = %w[ Mon Wed ].include?(wd) ? '*' + wd.upcase : ' ' + wd.downcase
414
+
415
+ puts "%14s | rweek: %3d | %%2: %d == 0 | %%3: %d == 1 | ? %3s" % [
416
+ t.strftime('%F') + ' ' + wd,
417
+ t.rweek,
418
+ t.rweek % 2, t.rweek % 3,
419
+ c.match?(t)
420
+ ].map { |e| e == true ? 'YES' : e == false ? 'no' : e }
421
+
422
+ w = t.rweek
423
+ t = t.add(24 * 3600)
424
+ puts if t.rweek != w
425
+ end
426
+ ```
427
+
428
+ Here's the output:
429
+ ```
430
+ 2025-09-20 sat | rweek: 350 | %2: 0 == 0 | %3: 2 == 1 | ? no
431
+ 2025-09-21 sun | rweek: 350 | %2: 0 == 0 | %3: 2 == 1 | ? no
432
+
433
+ 2025-09-22 *MON | rweek: 351 | %2: 1 == 0 | %3: 0 == 1 | ? no
434
+ 2025-09-23 tue | rweek: 351 | %2: 1 == 0 | %3: 0 == 1 | ? no
435
+ 2025-09-24 *WED | rweek: 351 | %2: 1 == 0 | %3: 0 == 1 | ? no
436
+ 2025-09-25 thu | rweek: 351 | %2: 1 == 0 | %3: 0 == 1 | ? no
437
+ 2025-09-26 fri | rweek: 351 | %2: 1 == 0 | %3: 0 == 1 | ? no
438
+ 2025-09-27 sat | rweek: 351 | %2: 1 == 0 | %3: 0 == 1 | ? no
439
+ 2025-09-28 sun | rweek: 351 | %2: 1 == 0 | %3: 0 == 1 | ? no
440
+
441
+ 2025-09-29 *MON | rweek: 352 | %2: 0 == 0 | %3: 1 == 1 | ? YES
442
+ 2025-09-30 tue | rweek: 352 | %2: 0 == 0 | %3: 1 == 1 | ? no
443
+ 2025-10-01 *WED | rweek: 352 | %2: 0 == 0 | %3: 1 == 1 | ? YES
444
+ 2025-10-02 thu | rweek: 352 | %2: 0 == 0 | %3: 1 == 1 | ? no
445
+ 2025-10-03 fri | rweek: 352 | %2: 0 == 0 | %3: 1 == 1 | ? no
446
+ 2025-10-04 sat | rweek: 352 | %2: 0 == 0 | %3: 1 == 1 | ? no
447
+ 2025-10-05 sun | rweek: 352 | %2: 0 == 0 | %3: 1 == 1 | ? no
448
+
449
+ 2025-10-06 *MON | rweek: 353 | %2: 1 == 0 | %3: 2 == 1 | ? no
450
+ 2025-10-07 tue | rweek: 353 | %2: 1 == 0 | %3: 2 == 1 | ? no
451
+ 2025-10-08 *WED | rweek: 353 | %2: 1 == 0 | %3: 2 == 1 | ? no
452
+ 2025-10-09 thu | rweek: 353 | %2: 1 == 0 | %3: 2 == 1 | ? no
453
+ 2025-10-10 fri | rweek: 353 | %2: 1 == 0 | %3: 2 == 1 | ? no
454
+ 2025-10-11 sat | rweek: 353 | %2: 1 == 0 | %3: 2 == 1 | ? no
455
+ 2025-10-12 sun | rweek: 353 | %2: 1 == 0 | %3: 2 == 1 | ? no
456
+
457
+ 2025-10-13 *MON | rweek: 354 | %2: 0 == 0 | %3: 0 == 1 | ? YES
458
+ 2025-10-14 tue | rweek: 354 | %2: 0 == 0 | %3: 0 == 1 | ? no
459
+ 2025-10-15 *WED | rweek: 354 | %2: 0 == 0 | %3: 0 == 1 | ? no
460
+ 2025-10-16 thu | rweek: 354 | %2: 0 == 0 | %3: 0 == 1 | ? no
461
+ 2025-10-17 fri | rweek: 354 | %2: 0 == 0 | %3: 0 == 1 | ? no
462
+ 2025-10-18 sat | rweek: 354 | %2: 0 == 0 | %3: 0 == 1 | ? no
463
+ 2025-10-19 sun | rweek: 354 | %2: 0 == 0 | %3: 0 == 1 | ? no
464
+
465
+ 2025-10-20 *MON | rweek: 355 | %2: 1 == 0 | %3: 1 == 1 | ? no
466
+ 2025-10-21 tue | rweek: 355 | %2: 1 == 0 | %3: 1 == 1 | ? no
467
+ 2025-10-22 *WED | rweek: 355 | %2: 1 == 0 | %3: 1 == 1 | ? YES
468
+ 2025-10-23 thu | rweek: 355 | %2: 1 == 0 | %3: 1 == 1 | ? no
469
+ 2025-10-24 fri | rweek: 355 | %2: 1 == 0 | %3: 1 == 1 | ? no
470
+ 2025-10-25 sat | rweek: 355 | %2: 1 == 0 | %3: 1 == 1 | ? no
471
+ 2025-10-26 sun | rweek: 355 | %2: 1 == 0 | %3: 1 == 1 | ? no
472
+
473
+ 2025-10-27 *MON | rweek: 356 | %2: 0 == 0 | %3: 2 == 1 | ? YES
474
+ 2025-10-28 tue | rweek: 356 | %2: 0 == 0 | %3: 2 == 1 | ? no
475
+ 2025-10-29 *WED | rweek: 356 | %2: 0 == 0 | %3: 2 == 1 | ? no
476
+ 2025-10-30 thu | rweek: 356 | %2: 0 == 0 | %3: 2 == 1 | ? no
477
+ 2025-10-31 fri | rweek: 356 | %2: 0 == 0 | %3: 2 == 1 | ? no
478
+ 2025-11-01 sat | rweek: 356 | %2: 0 == 0 | %3: 2 == 1 | ? no
479
+ 2025-11-02 sun | rweek: 356 | %2: 0 == 0 | %3: 2 == 1 | ? no
480
+ ```
336
481
 
337
482
 
338
483
  ### the second extension
data/fugit.gemspec CHANGED
@@ -41,7 +41,7 @@ Time tools for flor and the floraison project. Cron parsing and occurrence compu
41
41
  # this dependency appears in 'et-orbi'
42
42
 
43
43
  s.add_runtime_dependency 'raabro', '~> 1.4'
44
- s.add_runtime_dependency 'et-orbi', '~> 1', '>= 1.2.11'
44
+ s.add_runtime_dependency 'et-orbi', '~> 1.4'
45
45
 
46
46
  s.add_development_dependency 'rspec', '~> 3.8'
47
47
  s.add_development_dependency 'chronic', '~> 0.10'
data/lib/fugit/cron.rb CHANGED
@@ -175,34 +175,30 @@ module Fugit
175
175
 
176
176
  def weekday_hash_match?(nt, hsh)
177
177
 
178
+ return false unless hsh.is_a?(Integer)
179
+
178
180
  phsh, nhsh = nt.wday_in_month
181
+ #
182
+ # positive wday, from the beginning of the month
183
+ # negative wday, from the end of the month, -1 == last
179
184
 
180
- if hsh > 0
181
- hsh == phsh # positive wday, from the beginning of the month
182
- else
183
- hsh == nhsh # negative wday, from the end of the month, -1 == last
184
- end
185
+ (hsh == phsh) || (hsh == nhsh)
185
186
  end
186
187
 
187
188
  def weekday_modulo_match?(nt, mod)
188
189
 
189
- (nt.rweek % mod[0]) == (mod[1] % mod[0])
190
+ mod.is_a?(Array) &&
191
+ ((nt.rweek % mod[0]) == (mod[1] % mod[0]))
190
192
  end
191
193
 
192
194
  def weekday_match?(nt)
193
195
 
194
- return true if @weekdays.nil?
195
-
196
- wd, hom = @weekdays.find { |d, _| d == nt.wday }
197
-
198
- return false unless wd
199
- return true if hom.nil?
200
-
201
- if hom.is_a?(Array)
202
- weekday_modulo_match?(nt, hom)
203
- else
204
- weekday_hash_match?(nt, hom)
205
- end
196
+ @weekdays.nil? ||
197
+ @weekdays.find { |wd, hom|
198
+ (nt.wday == wd) &&
199
+ (hom.nil? ||
200
+ weekday_modulo_match?(nt, hom) ||
201
+ weekday_hash_match?(nt, hom)) }
206
202
  end
207
203
 
208
204
  def monthday_match?(nt)
@@ -735,7 +731,8 @@ module Fugit
735
731
  else
736
732
  a.collect(&:to_s).join('#')
737
733
  end }
738
- .join(',')
734
+ .join(',') +
735
+ (@day_and ? '&' : '')
739
736
  end
740
737
 
741
738
  module Parser include Raabro
data/lib/fugit/nat.rb CHANGED
@@ -719,6 +719,8 @@ module Fugit
719
719
  end
720
720
  end
721
721
 
722
+ SLASH_REGEX = /^(\d+|\*)\/(\d+)$/.freeze
723
+
722
724
  # Return nil if the cron is "not strict"
723
725
  #
724
726
  # For example, "0 0/17 * * *" (gh-86) is a perfectly valid
@@ -729,16 +731,18 @@ module Fugit
729
731
  #
730
732
  def restrict(a, cron)
731
733
 
732
- if m = ((a[1] && a[1][0]) || '').match(/^(\d+|\*)\/(\d+)$/)
733
- #p m
734
- sla = m[1].to_i
735
- return nil unless [ 1, 2, 3, 4, 5, 6, 8, 12 ].include?(sla)
734
+ if m = ((a[0] && a[0] != [ 0 ] && a[0][0]) || '').match(SLASH_REGEX)
735
+ return nil unless (1..60).include?(m[1].to_i)
736
+ end
737
+ if m = ((a[1] && a[1][0]) || '').match(SLASH_REGEX)
738
+ return nil unless [ 1, 2, 3, 4, 5, 6, 8, 12 ].include?(m[1].to_i)
736
739
  end
737
740
 
738
741
  cron
739
742
  end
740
743
 
741
744
  def slot(key, default)
745
+
742
746
  s = @slots[key]
743
747
  s ? s.data0 : [ default ]
744
748
  end
data/lib/fugit/parse.rb CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Fugit
4
4
 
5
+ DO_PARSE_ORDER = %i[ cron duration nat at ].freeze
6
+
5
7
  class << self
6
8
 
7
9
  def parse_cron(s); ::Fugit::Cron.parse(s); end
@@ -34,7 +36,7 @@ module Fugit
34
36
  result = nil
35
37
  errors = []
36
38
 
37
- %i[ cron duration nat at ]
39
+ DO_PARSE_ORDER
38
40
  .each { |k|
39
41
  begin
40
42
  result ||= (opts[k] != false && self.send("do_parse_#{k}", s))
data/lib/fugit.rb CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  module Fugit
5
5
 
6
- VERSION = '1.11.2'
6
+ VERSION = '1.12.1'
7
7
  end
8
8
 
9
9
  require 'time'
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.11.2
4
+ version: 1.12.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Mettraux
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-08-22 00:00:00.000000000 Z
11
+ date: 2025-10-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: raabro
@@ -30,20 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '1'
34
- - - ">="
35
- - !ruby/object:Gem::Version
36
- version: 1.2.11
33
+ version: '1.4'
37
34
  type: :runtime
38
35
  prerelease: false
39
36
  version_requirements: !ruby/object:Gem::Requirement
40
37
  requirements:
41
38
  - - "~>"
42
39
  - !ruby/object:Gem::Version
43
- version: '1'
44
- - - ">="
45
- - !ruby/object:Gem::Version
46
- version: 1.2.11
40
+ version: '1.4'
47
41
  - !ruby/object:Gem::Dependency
48
42
  name: rspec
49
43
  requirement: !ruby/object:Gem::Requirement