fugit 1.11.1 → 1.12.0
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 +4 -4
- data/CHANGELOG.md +15 -0
- data/CREDITS.md +5 -0
- data/LICENSE.txt +1 -1
- data/README.md +211 -103
- data/fugit.gemspec +5 -5
- data/lib/fugit/cron.rb +26 -22
- data/lib/fugit/duration.rb +14 -5
- data/lib/fugit/nat.rb +2 -2
- data/lib/fugit/parse.rb +18 -0
- data/lib/fugit.rb +1 -1
- metadata +7 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 31f13ce15ee0d51e6029539634fd55e14401f60ba03cdc6ad90c8e47bd4b711e
|
4
|
+
data.tar.gz: 91db0bb6fd6aa714e44e4c6d2b67fdbffc70495da77bf9946cda8d859bdf510a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 604a819a90770a746f33f2d93cdab4524f036a71ac901d311fb74c438f20eaa01a4fb13187f5eca96b7a449354d7864ef3b515674088b3bc60b36710afd8c9df
|
7
|
+
data.tar.gz: c83c544fa51e1a3e8bc2f76ff163276caa61a93927db6bbfe47642d91ffa85281c8522e1b36a2e9c988c85963c34edc86b47fab98dc1ee516a4495df63839ac8
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,21 @@
|
|
2
2
|
# CHANGELOG.md
|
3
3
|
|
4
4
|
|
5
|
+
## fugit 1.12.0 released 2025-09-30
|
6
|
+
|
7
|
+
* Upgrade et-orbi to ~> 1.4.0 for EtOrbi.rweek_ref=, gh-114
|
8
|
+
this changes the rweek reference point to 2018-12-31 (Monday)
|
9
|
+
* Fix `12 0 * * wed%4+1,wed%4` issue where 1 wed was ignored, gh-114
|
10
|
+
|
11
|
+
|
12
|
+
## fugit 1.11.2 released 2025-08-22
|
13
|
+
|
14
|
+
* Fix "every day at midnight America/Los_Angeles", gh-113, Mark R. James
|
15
|
+
* Avoid Hash#partition https://bugs.ruby-lang.org/issues/16252
|
16
|
+
* Fix Fugit::Nat "zero dark forty", gh-107
|
17
|
+
* Ensure @yearly and other specials accept a timezone
|
18
|
+
|
19
|
+
|
5
20
|
## fugit 1.11.1 released 2024-08-15
|
6
21
|
|
7
22
|
* Prevent nat parsing chocking on long input (> 256 chars), gh-104
|
data/CREDITS.md
CHANGED
@@ -1,6 +1,11 @@
|
|
1
1
|
|
2
2
|
# fugit credits
|
3
3
|
|
4
|
+
* Hugh Kelsey https://github.com/hughkelsey gh-94 rweek/rday / wed%4+1,wed%4
|
5
|
+
* Mark R. James, https://github.com/mrj, AM vs America/Los_Angeles, gh-113
|
6
|
+
* Tejas Bubane, https://github.com/tejasbubane, r3.4 in test matrix, gh-109
|
7
|
+
* Luis Castillo, https://github.com/lekastillo, nice_hash gh-108
|
8
|
+
* Geremia Taglialatela, https://github.com/tagliala, gh-105 gh-107
|
4
9
|
* https://github.com/personnumber3377, gh-104 Fugit.parse choke on long input
|
5
10
|
* Michael Scrivo, https://github.com/mscrivo, gh-103
|
6
11
|
* Benjamin Darcet, https://github.com/bdarcet gh-95 gh-96 et-orbi #rweek
|
data/LICENSE.txt
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
|
2
|
-
Copyright (c) 2017-
|
2
|
+
Copyright (c) 2017-2025, 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/README.md
CHANGED
@@ -17,31 +17,33 @@ Fugit is a core dependency of [rufus-scheduler](https://github.com/jmettraux/ruf
|
|
17
17
|
|
18
18
|
The intersection of those two projects is where fugit is born:
|
19
19
|
|
20
|
-
* [rufus-scheduler](https://github.com/jmettraux/rufus-scheduler)
|
21
|
-
* [flor](https://github.com/floraison/flor)
|
20
|
+
* [rufus-scheduler](https://github.com/jmettraux/rufus-scheduler) — a cron/at/in/every/interval in-process scheduler, in fact, it's the father project to this fugit project
|
21
|
+
* [flor](https://github.com/floraison/flor) — a Ruby workflow engine, fugit provides the foundation for its time scheduling capabilities
|
22
22
|
|
23
23
|
### Similar, sometimes overlapping projects
|
24
24
|
|
25
|
-
* [chronic](https://github.com/mojombo/chronic)
|
26
|
-
* [parse-cron](https://github.com/siebertm/parse-cron)
|
27
|
-
* [ice_cube](https://github.com/seejohnrun/ice_cube)
|
28
|
-
* [ISO8601](https://github.com/arnau/ISO8601)
|
29
|
-
* [chrono](https://github.com/r7kamura/chrono)
|
30
|
-
* [CronCalc](https://github.com/mizinsky/cron_calc)
|
31
|
-
* [Recurrence](https://github.com/fnando/recurrence)
|
25
|
+
* [chronic](https://github.com/mojombo/chronic) — a pure Ruby natural language date parser
|
26
|
+
* [parse-cron](https://github.com/siebertm/parse-cron) — parses cron expressions and calculates the next occurrence after a given date
|
27
|
+
* [ice_cube](https://github.com/seejohnrun/ice_cube) — Ruby date recurrence library
|
28
|
+
* [ISO8601](https://github.com/arnau/ISO8601) — Ruby parser to work with ISO8601 dateTimes and durations
|
29
|
+
* [chrono](https://github.com/r7kamura/chrono) — a chain of logics about chronology
|
30
|
+
* [CronCalc](https://github.com/mizinsky/cron_calc) — calculates cron job occurrences
|
31
|
+
* [Recurrence](https://github.com/fnando/recurrence) — a simple library to handle recurring events
|
32
|
+
* [CronConfigParser](https://github.com/madogiwa0124/cron_config_parser) — parse the cron configuration for readability
|
32
33
|
* ...
|
33
34
|
|
34
35
|
### Projects using fugit
|
35
36
|
|
36
|
-
* [arask](https://github.com/Ebbe/arask)
|
37
|
-
* [sidekiq-cron](https://github.com/ondrejbartas/sidekiq-cron)
|
38
|
-
* [rufus-scheduler](https://github.com/jmettraux/rufus-scheduler)
|
39
|
-
* [flor](https://github.com/floraison/flor)
|
40
|
-
* [que-scheduler](https://github.com/hlascelles/que-scheduler)
|
41
|
-
* [serial_scheduler](https://github.com/grosser/serial_scheduler)
|
42
|
-
* [delayed_cron_job](https://github.com/codez/delayed_cron_job)
|
43
|
-
* [GoodJob](https://github.com/bensheldon/good_job)
|
44
|
-
* [Solid Queue](https://github.com/rails/solid_queue)
|
37
|
+
* [arask](https://github.com/Ebbe/arask) — "Automatic RAils taSKs" uses fugit to parse cron strings
|
38
|
+
* [sidekiq-cron](https://github.com/ondrejbartas/sidekiq-cron) — uses fugit to parse cron strings since version 1.0.0, it was using rufus-scheduler previously
|
39
|
+
* [rufus-scheduler](https://github.com/jmettraux/rufus-scheduler) — as seen above
|
40
|
+
* [flor](https://github.com/floraison/flor) — used in the [cron](https://github.com/floraison/flor/blob/master/doc/procedures/cron.md) procedure
|
41
|
+
* [que-scheduler](https://github.com/hlascelles/que-scheduler) — a reliable job scheduler for [que](https://github.com/chanks/que)
|
42
|
+
* [serial_scheduler](https://github.com/grosser/serial_scheduler) — ruby task scheduler without threading
|
43
|
+
* [delayed_cron_job](https://github.com/codez/delayed_cron_job) — an extension to Delayed::Job that allows you to set cron expressions for your jobs
|
44
|
+
* [GoodJob](https://github.com/bensheldon/good_job) — a multithreaded, Postgres-based, Active Job backend for Ruby on Rails
|
45
|
+
* [Solid Queue](https://github.com/rails/solid_queue) — a DB-based queuing backend for Active Job, designed with simplicity and performance in mind
|
46
|
+
* [qron](https://github.com/floraison/qron) — stupid cron thread that wakes up from time to time to do what's in its crontab
|
45
47
|
* ...
|
46
48
|
|
47
49
|
## `Fugit.parse(s)`
|
@@ -112,6 +114,7 @@ Fugit.parse_cronish('12y12M') # ==> nil
|
|
112
114
|
|
113
115
|
Introduced in fugit 1.8.0.
|
114
116
|
|
117
|
+
|
115
118
|
## `Fugit::Cron`
|
116
119
|
|
117
120
|
A class `Fugit::Cron` to parse cron strings and then `#next_time` and `#previous_time` to compute the next or the previous occurrence respectively.
|
@@ -204,13 +207,28 @@ Example of cron strings understood by fugit:
|
|
204
207
|
|
205
208
|
Please note that `'15/30 * * * *'` is interpreted as `'15-59/30 * * * *'` since fugit 1.4.6.
|
206
209
|
|
210
|
+
### time zones
|
211
|
+
|
212
|
+
Fugit accepts a IANA timezone identifier right after a cron string:
|
213
|
+
```ruby
|
214
|
+
'5 0 * * * Europe/Rome' # 5 minutes after midnight, every day, Rome tz
|
215
|
+
'0 22 * * 1-5 Asia/Tbilisi' # at 2200 on weekdays in Georgia
|
216
|
+
|
217
|
+
'@yearly Asia/Kuala_Lumpur' # turns into '0 0 1 1 * Asia/Kuala_Lumpur'
|
218
|
+
'@monthly Asia/Jakarta' # turns into '0 0 1 * * Asia/Jakarta'
|
219
|
+
#
|
220
|
+
# those two "ats" and friends since fugit 1.11.2...
|
221
|
+
```
|
222
|
+
|
223
|
+
When no time zone is specified, fugit uses Ruby's provided timezone.
|
224
|
+
|
207
225
|
### the first Monday of the month
|
208
226
|
|
209
227
|
Fugit tries to follow the `man 5 crontab` documentation.
|
210
228
|
|
211
229
|
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 `*`).
|
212
230
|
|
213
|
-
Many people (me included) [are
|
231
|
+
Many people (me included) [are surprised](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.
|
214
232
|
|
215
233
|
The man page says:
|
216
234
|
|
@@ -278,7 +296,7 @@ The hash extension can only be used in the day-of-week field.
|
|
278
296
|
|
279
297
|
### the modulo extension
|
280
298
|
|
281
|
-
Fugit, since 1.1.10, also understands cron strings like
|
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" or `12 0 * * mon%4` for "every fourth monday at noon".
|
282
300
|
|
283
301
|
The modulo extension can only be used in the day-of-week field.
|
284
302
|
|
@@ -286,131 +304,131 @@ For odd Sundays, one can write `9 0 * * sun%2+1`.
|
|
286
304
|
|
287
305
|
It can be combined, as in `9 0 * * sun%2,tue%3+2`
|
288
306
|
|
289
|
-
But what does it reference to? It starts at 1 on 2019-01-01.
|
307
|
+
But what does it reference to? It starts at 1 on 2019-01-01 (in the EtOrbi instance timezone, not the UTC "timezone").
|
308
|
+
|
309
|
+
Since [et-orbi](https://github.com/floraison/et-orbi) 1.4.0, the reference has been switched to make `Monday 2018-12-31` as the reference, so that weeks start on Monday (read a bit below on how to make it start on another day).
|
290
310
|
|
291
311
|
```ruby
|
292
|
-
require 'et-orbi' # >= 1.1.8
|
312
|
+
require 'et-orbi' # >= 1.1.8 and < 1.4.0
|
313
|
+
|
314
|
+
class EtOrbi::EoTime
|
315
|
+
def d # debug
|
316
|
+
"%14s | rday: %4d | rweek: %3d" % [ strftime('%a'), rday, rweek ]
|
317
|
+
end
|
318
|
+
end
|
293
319
|
|
294
320
|
# the reference
|
295
|
-
|
296
|
-
p EtOrbi.parse('2019-01-01').rweek # => 1
|
321
|
+
puts EtOrbi.parse('2019-01-01').d # => Tue | rday: 1 | rweek: 1
|
297
322
|
p EtOrbi.parse('2019-01-01').rweek % 2 # => 1
|
298
323
|
|
299
324
|
# today (as of this coding...)
|
300
|
-
|
301
|
-
p EtOrbi.parse('2019-04-11').rweek # => 15
|
325
|
+
puts EtOrbi.parse('2019-04-11').d # => Thu | rday: 101 | rweek: 15
|
302
326
|
p EtOrbi.parse('2019-04-11').rweek % 2 # => 1
|
303
327
|
|
304
328
|
c = Fugit.parse('* * * * tue%2')
|
305
|
-
c.match?('2019-01-01') # => false, since rweek % 2 == 1
|
306
|
-
c.match?('2019-01-08') # => true, since rweek % 2 == 0
|
329
|
+
p c.match?('2019-01-01') # => false, since rweek % 2 == 1
|
330
|
+
p c.match?('2019-01-08') # => true, since rweek % 2 == 0
|
307
331
|
|
308
332
|
c = Fugit.parse('* * * * tue%2+1')
|
309
|
-
c.match?('2019-01-01') # => true, since (rweek + 1) % 2 == 0
|
310
|
-
c.match?('2019-01-08') # => false, since (rweek + 1) % 2 == 1
|
333
|
+
p c.match?('2019-01-01') # => true, since (rweek + 1) % 2 == 0
|
334
|
+
p c.match?('2019-01-08') # => false, since (rweek + 1) % 2 == 1
|
311
335
|
|
312
336
|
# ...
|
337
|
+
|
338
|
+
class EtOrbi::EoTime
|
339
|
+
def d # debug
|
340
|
+
"%14s | rday: %4d | rweek: %3d" % [ strftime('%F %a'), rday, rweek ]
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
c = Fugit.parse_cron('20 0 * * mon%2,wed%3+1')
|
345
|
+
puts c.next('2025-09-21').take(10).map(&:d)
|
346
|
+
#
|
347
|
+
# => 2025-09-24 Wed | rday: 2459 | rweek: 352
|
348
|
+
# 2025-09-29 Mon | rday: 2464 | rweek: 352
|
349
|
+
# 2025-10-13 Mon | rday: 2478 | rweek: 354
|
350
|
+
# 2025-10-15 Wed | rday: 2480 | rweek: 355
|
351
|
+
# 2025-10-27 Mon | rday: 2492 | rweek: 356
|
352
|
+
# 2025-11-05 Wed | rday: 2501 | rweek: 358
|
353
|
+
# 2025-11-10 Mon | rday: 2506 | rweek: 358
|
354
|
+
# 2025-11-24 Mon | rday: 2520 | rweek: 360
|
355
|
+
# 2025-11-26 Wed | rday: 2522 | rweek: 361
|
356
|
+
# 2025-12-08 Mon | rday: 2534 | rweek: 362
|
313
357
|
```
|
314
358
|
|
315
359
|
`sun%2` matches if Sunday and `current_date.rweek % 2 == 0`
|
316
360
|
`tue%3+2` matches if Tuesday and `current_date.rweek + 2 % 3 == 0`
|
317
361
|
`tue%x+y` matches if Tuesday and `current_date.rweek + y % x == 0`
|
318
362
|
|
319
|
-
|
320
|
-
### the second extension
|
321
|
-
|
322
|
-
Fugit accepts cron strings with five elements, `minute hour day-of-month month day-of-week`, the standard cron format or six elements `second minute hour day-of-month month day-of-week`.
|
323
|
-
|
324
|
-
```ruby
|
325
|
-
c = Fugit.parse('* * * * *') # every minute
|
326
|
-
c = Fugit.parse('5 * * * *') # every hour at minute 5
|
327
|
-
c = Fugit.parse('* * * * * *') # every second
|
328
|
-
c = Fugit.parse('5 * * * * *') # every minute at second 5
|
329
|
-
```
|
330
|
-
|
331
|
-
|
332
|
-
## `Fugit::Duration`
|
333
|
-
|
334
|
-
A class `Fugit::Duration` to parse duration strings (vanilla [rufus-scheduler](https://github.com/jmettraux/rufus-scheduler) ones and [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) ones).
|
335
|
-
|
336
|
-
Provides duration arithmetic tools.
|
337
|
-
|
338
363
|
```ruby
|
339
|
-
require '
|
364
|
+
require 'et-orbi' # >= 1.4.0
|
340
365
|
|
341
|
-
|
366
|
+
class EtOrbi::EoTime
|
367
|
+
def d # debug
|
368
|
+
"%14s | rday: %4d | rweek: %3d" % [ strftime('%a'), rday, rweek ]
|
369
|
+
end
|
370
|
+
end
|
342
371
|
|
343
|
-
|
344
|
-
|
345
|
-
|
372
|
+
puts EtOrbi.parse('2018-12-30').d # => Sun | rday: -1 | rweek: -1
|
373
|
+
puts EtOrbi.parse('2018-12-31').d # => Mon | rday: 0 | rweek: 0 >REF<
|
374
|
+
puts EtOrbi.parse('2019-01-01').d # => Tue | rday: 1 | rweek: 0
|
375
|
+
puts EtOrbi.parse('2019-01-02').d # => Wed | rday: 2 | rweek: 0
|
376
|
+
puts EtOrbi.parse('2019-01-31').d # => Thu | rday: 31 | rweek: 4
|
346
377
|
|
347
|
-
|
378
|
+
puts EtOrbi.parse('2019-04-11').d # => Thu | rday: 101 | rweek: 14
|
379
|
+
puts EtOrbi.parse('2025-09-30').d # => Tue | rday: 2465 | rweek: 352
|
348
380
|
|
349
|
-
|
381
|
+
class EtOrbi::EoTime
|
382
|
+
def d # debug
|
383
|
+
"%14s | rday: %4d | rweek: %3d" % [ strftime('%F %a'), rday, rweek ]
|
384
|
+
end
|
385
|
+
end
|
350
386
|
|
351
|
-
|
387
|
+
p EtOrbi.rweek_ref # => "2018-12-31"
|
352
388
|
|
353
|
-
|
354
|
-
|
355
|
-
|
389
|
+
c = Fugit.parse_cron('20 0 * * mon%2,wed%3+1')
|
390
|
+
puts c.next('2025-09-21').take(10).map(&:d)
|
391
|
+
#
|
392
|
+
# => 2025-09-29 Mon | rday: 2464 | rweek: 352
|
393
|
+
# 2025-10-01 Wed | rday: 2466 | rweek: 352
|
394
|
+
# 2025-10-13 Mon | rday: 2478 | rweek: 354
|
395
|
+
# 2025-10-22 Wed | rday: 2487 | rweek: 355
|
396
|
+
# 2025-10-27 Mon | rday: 2492 | rweek: 356
|
397
|
+
# 2025-11-10 Mon | rday: 2506 | rweek: 358
|
398
|
+
# 2025-11-12 Wed | rday: 2508 | rweek: 358
|
399
|
+
# 2025-11-24 Mon | rday: 2520 | rweek: 360
|
400
|
+
# 2025-12-03 Wed | rday: 2529 | rweek: 361
|
401
|
+
# 2025-12-08 Mon | rday: 2534 | rweek: 362
|
356
402
|
```
|
357
403
|
|
358
|
-
|
359
|
-
|
404
|
+
One can tell et-orbi >= 1.4.0 which day to take as reference:
|
360
405
|
```ruby
|
361
|
-
|
362
|
-
|
363
|
-
Fugit::Duration.parse(1000).deflate.to_plain_s # => "16m40s"
|
364
|
-
Fugit::Duration.parse(3600).deflate.to_plain_s # => "1h"
|
406
|
+
EtOrbi.rweek_ref = :us # or
|
407
|
+
EtOrbi.rweek_ref = :sunday # to start on a Sunday (2018-12-30)
|
365
408
|
|
366
|
-
# or
|
367
|
-
|
368
|
-
|
369
|
-
```
|
409
|
+
EtOrbi.rweek_ref = :iso # or
|
410
|
+
EtOrbi.rweek_ref = :monday # or
|
411
|
+
EtOrbi.rweek_ref = :default # to start on a Monday (2019-12-31)
|
370
412
|
|
371
|
-
|
413
|
+
EtOrbi.rweek_ref = :saturday # to start on, well, a Saturday (2019-01-05)
|
372
414
|
|
373
|
-
|
374
|
-
Fugit::Duration.parse('1h30m12').inflate.to_plain_s # => "5412s"
|
375
|
-
Fugit.parse('1h30m12').inflate.to_plain_s # => "5412s"
|
415
|
+
EtOrbi.rweek_ref = '2025-09-30' # to start on this very Tuesday...
|
376
416
|
|
377
|
-
|
378
|
-
Fugit.parse('1h30m12').to_sec.to_s + 's' # => "5412s"
|
417
|
+
p EtOrbi.rweek_ref # to determine what the current setting is...
|
379
418
|
```
|
380
419
|
|
381
|
-
The `to_*_s` methods are also available as class methods:
|
382
|
-
```ruby
|
383
|
-
p Fugit::Duration.to_plain_s('1y2M1d4h')
|
384
|
-
# => "1Y2M1D4h"
|
385
|
-
p Fugit::Duration.to_iso_s('1y2M1d4h')
|
386
|
-
# => "P1Y2M1DT4H" ISO 8601 duration
|
387
|
-
p Fugit::Duration.to_long_s('1y2M1d4h')
|
388
|
-
# => "1 year, 2 months, 1 day, and 4 hours"
|
389
|
-
```
|
390
420
|
|
391
|
-
|
421
|
+
### the second extension
|
392
422
|
|
393
|
-
|
423
|
+
Fugit accepts cron strings with five elements, `minute hour day-of-month month day-of-week`, the standard cron format or six elements `second minute hour day-of-month month day-of-week`.
|
394
424
|
|
395
425
|
```ruby
|
396
|
-
Fugit
|
397
|
-
|
398
|
-
|
399
|
-
Fugit
|
400
|
-
# ==> "2017-12-12 12:00:00 -0500"
|
426
|
+
c = Fugit.parse('* * * * *') # every minute
|
427
|
+
c = Fugit.parse('5 * * * *') # every hour at minute 5
|
428
|
+
c = Fugit.parse('* * * * * *') # every second
|
429
|
+
c = Fugit.parse('5 * * * * *') # every minute at second 5
|
401
430
|
```
|
402
431
|
|
403
|
-
Directly with `Fugit.parse_at(s)` is OK too:
|
404
|
-
```ruby
|
405
|
-
Fugit.parse_at('2017-12-12 12:00:00 America/New_York').to_s
|
406
|
-
# ==> "2017-12-12 12:00:00 -0500"
|
407
|
-
```
|
408
|
-
|
409
|
-
Directly with `Fugit.parse(s)` is OK too:
|
410
|
-
```ruby
|
411
|
-
Fugit.parse('2017-12-12 12:00:00 America/New_York').to_s
|
412
|
-
# ==> "2017-12-12 12:00:00 -0500"
|
413
|
-
```
|
414
432
|
|
415
433
|
## `Fugit::Nat`
|
416
434
|
|
@@ -511,6 +529,96 @@ p Fugit.parse('every day at 12:15 midnight').original # ==> "15 24 * * *"
|
|
511
529
|
```
|
512
530
|
|
513
531
|
|
532
|
+
## `Fugit::Duration`
|
533
|
+
|
534
|
+
A class `Fugit::Duration` to parse duration strings (vanilla [rufus-scheduler](https://github.com/jmettraux/rufus-scheduler) ones and [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) ones).
|
535
|
+
|
536
|
+
Provides duration arithmetic tools.
|
537
|
+
|
538
|
+
```ruby
|
539
|
+
require 'fugit'
|
540
|
+
|
541
|
+
d = Fugit::Duration.parse('1y2M1d4h')
|
542
|
+
|
543
|
+
p d.to_plain_s # => "1Y2M1D4h"
|
544
|
+
p d.to_iso_s # => "P1Y2M1DT4H" ISO 8601 duration
|
545
|
+
p d.to_long_s # => "1 year, 2 months, 1 day, and 4 hours"
|
546
|
+
|
547
|
+
d += Fugit::Duration.parse('1y1h')
|
548
|
+
|
549
|
+
p d.to_long_s # => "2 years, 2 months, 1 day, and 5 hours"
|
550
|
+
|
551
|
+
d += 3600
|
552
|
+
|
553
|
+
p d.to_plain_s # => "2Y2M1D5h3600s"
|
554
|
+
|
555
|
+
p Fugit::Duration.parse('1y2M1d4h').to_sec # => 36820800
|
556
|
+
```
|
557
|
+
|
558
|
+
There is a `#deflate` method
|
559
|
+
|
560
|
+
```ruby
|
561
|
+
Fugit::Duration.parse(1000).to_plain_s # => "1000s"
|
562
|
+
Fugit::Duration.parse(3600).to_plain_s # => "3600s"
|
563
|
+
Fugit::Duration.parse(1000).deflate.to_plain_s # => "16m40s"
|
564
|
+
Fugit::Duration.parse(3600).deflate.to_plain_s # => "1h"
|
565
|
+
|
566
|
+
# or event shorter
|
567
|
+
Fugit.parse(1000).deflate.to_plain_s # => "16m40s"
|
568
|
+
Fugit.parse(3600).deflate.to_plain_s # => "1h"
|
569
|
+
```
|
570
|
+
|
571
|
+
There is also an `#inflate` method
|
572
|
+
|
573
|
+
```ruby
|
574
|
+
Fugit::Duration.parse('1h30m12').inflate.to_plain_s # => "5412s"
|
575
|
+
Fugit.parse('1h30m12').inflate.to_plain_s # => "5412s"
|
576
|
+
|
577
|
+
Fugit.parse('1h30m12').to_sec # => 5412
|
578
|
+
Fugit.parse('1h30m12').to_sec.to_s + 's' # => "5412s"
|
579
|
+
```
|
580
|
+
|
581
|
+
The `to_*_s` methods are also available as class methods:
|
582
|
+
```ruby
|
583
|
+
p Fugit::Duration.to_plain_s('1y2M1d4h')
|
584
|
+
# => "1Y2M1D4h"
|
585
|
+
p Fugit::Duration.to_iso_s('1y2M1d4h')
|
586
|
+
# => "P1Y2M1DT4H" ISO 8601 duration
|
587
|
+
p Fugit::Duration.to_long_s('1y2M1d4h')
|
588
|
+
# => "1 year, 2 months, 1 day, and 4 hours"
|
589
|
+
```
|
590
|
+
|
591
|
+
|
592
|
+
## `Fugit::At`
|
593
|
+
|
594
|
+
Points in time are parsed and given back as EtOrbi::EoTime instances.
|
595
|
+
|
596
|
+
```ruby
|
597
|
+
Fugit::At.parse('2017-12-12').to_s
|
598
|
+
# ==> "2017-12-12 00:00:00 +0900" (at least here in Hiroshima)
|
599
|
+
|
600
|
+
Fugit::At.parse('2017-12-12 12:00:00 America/New_York').to_s
|
601
|
+
# ==> "2017-12-12 12:00:00 -0500"
|
602
|
+
```
|
603
|
+
|
604
|
+
Directly with `Fugit.parse_at(s)` is OK too:
|
605
|
+
```ruby
|
606
|
+
Fugit.parse_at('2017-12-12 12:00:00 America/New_York').to_s
|
607
|
+
# ==> "2017-12-12 12:00:00 -0500"
|
608
|
+
```
|
609
|
+
|
610
|
+
Directly with `Fugit.parse(s)` is OK too:
|
611
|
+
```ruby
|
612
|
+
Fugit.parse('2017-12-12 12:00:00 America/New_York').to_s
|
613
|
+
# ==> "2017-12-12 12:00:00 -0500"
|
614
|
+
```
|
615
|
+
|
616
|
+
|
617
|
+
## KNOWN ISSUES
|
618
|
+
|
619
|
+
The gem [nice_hash](https://github.com/MarioRuiz/nice_hash) gets in the way of `fugit`, as seen in [issue 108](https://github.com/floraison/fugit/issues/108). It prevents `fugit` from correctly parsing cron strings.
|
620
|
+
|
621
|
+
|
514
622
|
## LICENSE
|
515
623
|
|
516
624
|
MIT, see [LICENSE.txt](LICENSE.txt)
|
data/fugit.gemspec
CHANGED
@@ -20,18 +20,18 @@ Time tools for flor and the floraison project. Cron parsing and occurrence compu
|
|
20
20
|
|
21
21
|
s.metadata = {
|
22
22
|
'changelog_uri' => s.homepage + '/blob/master/CHANGELOG.md',
|
23
|
-
'documentation_uri' => s.homepage,
|
24
23
|
'bug_tracker_uri' => s.homepage + '/issues',
|
25
|
-
|
24
|
+
'documentation_uri' => s.homepage,
|
26
25
|
'homepage_uri' => s.homepage,
|
27
26
|
'source_code_uri' => s.homepage,
|
27
|
+
#'mailing_list_uri' => 'https://groups.google.com/forum/#!forum/floraison',
|
28
28
|
#'wiki_uri' => s.homepage + '/wiki',
|
29
|
+
'rubygems_mfa_required' => 'true',
|
29
30
|
}
|
30
31
|
|
31
32
|
#s.files = `git ls-files`.split("\n")
|
32
33
|
s.files = Dir[
|
33
|
-
'README.{md,txt}',
|
34
|
-
'CHANGELOG.{md,txt}', 'CREDITS.{md,txt}', 'LICENSE.{md,txt}',
|
34
|
+
'{README,CHANGELOG,CREDITS,LICENSE}.{md,txt}',
|
35
35
|
#'Makefile',
|
36
36
|
'lib/**/*.rb', #'spec/**/*.rb', 'test/**/*.rb',
|
37
37
|
"#{s.name}.gemspec",
|
@@ -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
|
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
@@ -32,15 +32,23 @@ module Fugit
|
|
32
32
|
def parse(s)
|
33
33
|
|
34
34
|
return s if s.is_a?(self)
|
35
|
+
return nil unless s.is_a?(String)
|
35
36
|
|
36
|
-
|
37
|
+
s0 = s
|
38
|
+
s = s.strip
|
37
39
|
|
38
|
-
|
40
|
+
s =
|
41
|
+
if s[0, 1] == '@'
|
42
|
+
ss = s.split(/\s+/, 2)
|
43
|
+
[ SPECIALS[ss[0]] || ss, *ss[1..-1] ].join(' ')
|
44
|
+
else
|
45
|
+
s
|
46
|
+
end
|
39
47
|
|
40
48
|
#p s; Raabro.pp(Parser.parse(s, debug: 3), colors: true)
|
41
|
-
h = Parser.parse(s
|
49
|
+
h = Parser.parse(s)
|
42
50
|
|
43
|
-
self.allocate.send(:init,
|
51
|
+
self.allocate.send(:init, s0, h)
|
44
52
|
end
|
45
53
|
|
46
54
|
def do_parse(s)
|
@@ -167,34 +175,30 @@ module Fugit
|
|
167
175
|
|
168
176
|
def weekday_hash_match?(nt, hsh)
|
169
177
|
|
178
|
+
return false unless hsh.is_a?(Integer)
|
179
|
+
|
170
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
|
171
184
|
|
172
|
-
|
173
|
-
hsh == phsh # positive wday, from the beginning of the month
|
174
|
-
else
|
175
|
-
hsh == nhsh # negative wday, from the end of the month, -1 == last
|
176
|
-
end
|
185
|
+
(hsh == phsh) || (hsh == nhsh)
|
177
186
|
end
|
178
187
|
|
179
188
|
def weekday_modulo_match?(nt, mod)
|
180
189
|
|
181
|
-
(
|
190
|
+
mod.is_a?(Array) &&
|
191
|
+
((nt.rweek % mod[0]) == (mod[1] % mod[0]))
|
182
192
|
end
|
183
193
|
|
184
194
|
def weekday_match?(nt)
|
185
195
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
if hom.is_a?(Array)
|
194
|
-
weekday_modulo_match?(nt, hom)
|
195
|
-
else
|
196
|
-
weekday_hash_match?(nt, hom)
|
197
|
-
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)) }
|
198
202
|
end
|
199
203
|
|
200
204
|
def monthday_match?(nt)
|
data/lib/fugit/duration.rb
CHANGED
@@ -67,11 +67,20 @@ module Fugit
|
|
67
67
|
day: { a: 'D', r: 'd', i: 'D', s: DAY_S, I: true, l: 'day' },
|
68
68
|
hou: { a: 'h', r: 'h', i: 'H', s: 3600, I: true, l: 'hour' },
|
69
69
|
min: { a: 'm', r: 'm', i: 'M', s: 60, I: true, l: 'minute' },
|
70
|
-
sec: { a: 's', r: 's', i: 'S', s: 1, I: true, l: 'second' }
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
70
|
+
sec: { a: 's', r: 's', i: 'S', s: 1, I: true, l: 'second' }
|
71
|
+
}.freeze
|
72
|
+
|
73
|
+
#INFLA_KEYS, NON_INFLA_KEYS = KEYS
|
74
|
+
# .partition { |k, v| v[:I] }
|
75
|
+
# .collect(&:freeze)
|
76
|
+
#
|
77
|
+
# https://bugs.ruby-lang.org/issues/16252
|
78
|
+
#
|
79
|
+
kes = KEYS.entries
|
80
|
+
INFLA_KEYS,
|
81
|
+
NON_INFLA_KEYS =
|
82
|
+
kes.select { |_, v| v[:I] }.freeze,
|
83
|
+
kes.select { |_, v| ! v[:I] }.freeze
|
75
84
|
|
76
85
|
def _to_s(key)
|
77
86
|
|
data/lib/fugit/nat.rb
CHANGED
@@ -54,7 +54,7 @@ module Fugit
|
|
54
54
|
%w[ zero ] + one_to_nine +
|
55
55
|
%w[ ten eleven twelve thirteen fourteen fifteen sixteen seventeen
|
56
56
|
eighteen nineteen ] +
|
57
|
-
%w[ twenty thirty
|
57
|
+
%w[ twenty thirty forty fifty ]
|
58
58
|
.collect { |a|
|
59
59
|
([ nil ] + one_to_nine)
|
60
60
|
.collect { |b| [ a, b ].compact.join('-') } }
|
@@ -210,7 +210,7 @@ module Fugit
|
|
210
210
|
end
|
211
211
|
|
212
212
|
def ampm(i)
|
213
|
-
rex(:ampm, i, /[ \t]*(am|pm|
|
213
|
+
rex(:ampm, i, /[ \t]*(am|AM|pm|PM|[Nn]oon|[Mm]idday|[Mm]idnight)/)
|
214
214
|
end
|
215
215
|
def dark(i)
|
216
216
|
rex(:dark, i, /[ \t]*dark/i)
|
data/lib/fugit/parse.rb
CHANGED
@@ -63,6 +63,24 @@ module Fugit
|
|
63
63
|
fail(ArgumentError.new("not cron or 'natural' cron string: #{s.inspect}"))
|
64
64
|
end
|
65
65
|
|
66
|
+
def parse_max(s, opts={})
|
67
|
+
|
68
|
+
s0 = s.lines.first
|
69
|
+
|
70
|
+
(0..[ ::Fugit::Nat::MAX_INPUT_LENGTH, s0.length - 1 ].min).each do |i|
|
71
|
+
|
72
|
+
s1 =
|
73
|
+
s0[0, s0.length - i].rstrip
|
74
|
+
f =
|
75
|
+
opts[:cronish] ? parse_cronish(s1, opts) :
|
76
|
+
parse(s1, opts)
|
77
|
+
|
78
|
+
return [ s1, f ] if f
|
79
|
+
end
|
80
|
+
|
81
|
+
nil
|
82
|
+
end
|
83
|
+
|
66
84
|
def determine_type(s)
|
67
85
|
|
68
86
|
case self.parse(s)
|
data/lib/fugit.rb
CHANGED
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.
|
4
|
+
version: 1.12.0
|
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: 2025-09-30 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
|
@@ -97,10 +91,11 @@ licenses:
|
|
97
91
|
- MIT
|
98
92
|
metadata:
|
99
93
|
changelog_uri: https://github.com/floraison/fugit/blob/master/CHANGELOG.md
|
100
|
-
documentation_uri: https://github.com/floraison/fugit
|
101
94
|
bug_tracker_uri: https://github.com/floraison/fugit/issues
|
95
|
+
documentation_uri: https://github.com/floraison/fugit
|
102
96
|
homepage_uri: https://github.com/floraison/fugit
|
103
97
|
source_code_uri: https://github.com/floraison/fugit
|
98
|
+
rubygems_mfa_required: 'true'
|
104
99
|
post_install_message:
|
105
100
|
rdoc_options: []
|
106
101
|
require_paths:
|
@@ -116,7 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
116
111
|
- !ruby/object:Gem::Version
|
117
112
|
version: '0'
|
118
113
|
requirements: []
|
119
|
-
rubygems_version: 3.4.
|
114
|
+
rubygems_version: 3.4.19
|
120
115
|
signing_key:
|
121
116
|
specification_version: 4
|
122
117
|
summary: time tools for flor
|