fugit 1.8.1 → 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 +4 -4
- data/CHANGELOG.md +48 -0
- data/CREDITS.md +12 -0
- data/LICENSE.txt +1 -1
- data/README.md +331 -98
- data/fugit.gemspec +5 -5
- data/lib/fugit/cron.rb +139 -53
- data/lib/fugit/duration.rb +15 -6
- data/lib/fugit/nat.rb +52 -8
- data/lib/fugit/parse.rb +43 -6
- 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: 15a3ea665c020e35ce51dcbc82be9a74e6f4abcaa2441316b514c630ffdbc094
|
|
4
|
+
data.tar.gz: f99246722de255325b57ea46f7b3f24bbfc11d65f1eb98fde9a2a43a3c249df0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cc55e7332f5f111ddf27d145fed7fbfeaeed30f492ba343d1f4067e776c96874b42bad9488b9683203040cc50333f74bab56fa6e61744ef92fb059f2a2c9f076
|
|
7
|
+
data.tar.gz: 5ee65240dd04fddb7777bee327f8fee0cfef5260ed55db9ec71ece64c5e5e6b53283aa7d26ba52facbca3882aee0920ff0dd3715fea5ad9153caca82ea712450
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,54 @@
|
|
|
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
|
+
|
|
17
|
+
## fugit 1.11.2 released 2025-08-22
|
|
18
|
+
|
|
19
|
+
* Fix "every day at midnight America/Los_Angeles", gh-113, Mark R. James
|
|
20
|
+
* Avoid Hash#partition https://bugs.ruby-lang.org/issues/16252
|
|
21
|
+
* Fix Fugit::Nat "zero dark forty", gh-107
|
|
22
|
+
* Ensure @yearly and other specials accept a timezone
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## fugit 1.11.1 released 2024-08-15
|
|
26
|
+
|
|
27
|
+
* Prevent nat parsing chocking on long input (> 256 chars), gh-104
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
## fugit 1.11.0 released 2024-04-24
|
|
31
|
+
|
|
32
|
+
* Revert gh-86 ban on `every 27 hours` / `* */27 * * *` for gh-103
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
## fugit 1.10.1 released 2024-02-29
|
|
36
|
+
|
|
37
|
+
* Fix on Ruby 2.2.6 thanks to @aunghtain, gh-93
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
## fugit 1.10.0 released 2024-02-22
|
|
41
|
+
|
|
42
|
+
* Implement `Fugit::Cron#within(time_start, time_end)`
|
|
43
|
+
* Implement `Fugit::Cron#within(time_range)`
|
|
44
|
+
* Implement iterator-returning `Fugit::Cron#next` and `#prev`
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
## fugit 1.9.0 released 2023-10-24
|
|
48
|
+
|
|
49
|
+
* Let nat parse "last", gh-88
|
|
50
|
+
* Change that I am not sure about, gh-86
|
|
51
|
+
|
|
52
|
+
|
|
5
53
|
## fugit 1.8.1 released 2023-01-20
|
|
6
54
|
|
|
7
55
|
* Fix for month subtraction, gh-84, @mreinsch
|
data/CREDITS.md
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
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
|
|
6
|
+
* Mark R. James, https://github.com/mrj, AM vs America/Los_Angeles, gh-113
|
|
7
|
+
* Tejas Bubane, https://github.com/tejasbubane, r3.4 in test matrix, gh-109
|
|
8
|
+
* Luis Castillo, https://github.com/lekastillo, nice_hash gh-108
|
|
9
|
+
* Geremia Taglialatela, https://github.com/tagliala, gh-105 gh-107
|
|
10
|
+
* https://github.com/personnumber3377, gh-104 Fugit.parse choke on long input
|
|
11
|
+
* Michael Scrivo, https://github.com/mscrivo, gh-103
|
|
12
|
+
* Benjamin Darcet, https://github.com/bdarcet gh-95 gh-96 et-orbi #rweek
|
|
13
|
+
* https://github.com/franckduche gh-95 gh-96 et-orbi #rweek
|
|
14
|
+
* https://hithub.com/aunghtain, gh-93, include oneliner vs Ruby 2.6.6
|
|
15
|
+
* Marcos Belluci, https://github.com/delbetu, gh-88, 1st and last nat
|
|
4
16
|
* Michael Reinsch, https://github.com/mreinsch, gh-84 and gh-85
|
|
5
17
|
* Marc Anguera, https://github.com/markets, gh-70 and Sidekiq-Cron
|
|
6
18
|
* ski-nine, https://github.com/ski-nine, gh-81
|
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
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
|
|
4
4
|
[](https://github.com/floraison/fugit/actions)
|
|
5
5
|
[](http://badge.fury.io/rb/fugit)
|
|
6
|
-
[](https://gitter.im/floraison/fugit?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
|
7
6
|
|
|
8
7
|
Time tools for [flor](https://github.com/floraison/flor) and the floraison group.
|
|
9
8
|
|
|
@@ -18,26 +17,33 @@ Fugit is a core dependency of [rufus-scheduler](https://github.com/jmettraux/ruf
|
|
|
18
17
|
|
|
19
18
|
The intersection of those two projects is where fugit is born:
|
|
20
19
|
|
|
21
|
-
* [rufus-scheduler](https://github.com/jmettraux/rufus-scheduler)
|
|
22
|
-
* [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
|
|
23
22
|
|
|
24
23
|
### Similar, sometimes overlapping projects
|
|
25
24
|
|
|
26
|
-
* [chronic](https://github.com/mojombo/chronic)
|
|
27
|
-
* [parse-cron](https://github.com/siebertm/parse-cron)
|
|
28
|
-
* [ice_cube](https://github.com/seejohnrun/ice_cube)
|
|
29
|
-
* [ISO8601](https://github.com/arnau/ISO8601)
|
|
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
|
|
30
33
|
* ...
|
|
31
34
|
|
|
32
35
|
### Projects using fugit
|
|
33
36
|
|
|
34
|
-
* [arask](https://github.com/Ebbe/arask)
|
|
35
|
-
* [sidekiq-cron](https://github.com/ondrejbartas/sidekiq-cron)
|
|
36
|
-
* [rufus-scheduler](https://github.com/jmettraux/rufus-scheduler)
|
|
37
|
-
* [flor](https://github.com/floraison/flor)
|
|
38
|
-
* [que-scheduler](https://github.com/hlascelles/que-scheduler)
|
|
39
|
-
* [serial_scheduler](https://github.com/grosser/serial_scheduler)
|
|
40
|
-
* [delayed_cron_job](https://github.com/codez/delayed_cron_job)
|
|
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
|
|
41
47
|
* ...
|
|
42
48
|
|
|
43
49
|
## `Fugit.parse(s)`
|
|
@@ -93,7 +99,7 @@ As `Fugit.parse(s)` returns nil when it doesn't grok its input, and `Fugit.do_pa
|
|
|
93
99
|
|
|
94
100
|
Sometimes you know a cron expression or an "every" natural expression will come in and you want to discard the rest.
|
|
95
101
|
|
|
96
|
-
```
|
|
102
|
+
```ruby
|
|
97
103
|
require 'fugit'
|
|
98
104
|
|
|
99
105
|
Fugit.parse_cronish('0 0 1 jan *').class # ==> ::Fugit::Cron
|
|
@@ -108,6 +114,7 @@ Fugit.parse_cronish('12y12M') # ==> nil
|
|
|
108
114
|
|
|
109
115
|
Introduced in fugit 1.8.0.
|
|
110
116
|
|
|
117
|
+
|
|
111
118
|
## `Fugit::Cron`
|
|
112
119
|
|
|
113
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.
|
|
@@ -123,8 +130,48 @@ c = Fugit::Cron.new('0 0 * * sun')
|
|
|
123
130
|
|
|
124
131
|
p Time.now # => 2017-01-03 09:53:27 +0900
|
|
125
132
|
|
|
126
|
-
p c.next_time # => 2017-01-08 00:00:00 +0900
|
|
127
|
-
p c.previous_time # => 2017-01-01 00:00:00 +0900
|
|
133
|
+
p c.next_time.to_s # => 2017-01-08 00:00:00 +0900
|
|
134
|
+
p c.previous_time.to_s # => 2017-01-01 00:00:00 +0900
|
|
135
|
+
|
|
136
|
+
p c.next_time(Time.parse('2024-06-01')).to_s
|
|
137
|
+
# => "2024-06-02 00:00:00 +0900"
|
|
138
|
+
p c.previous_time(Time.parse('2024-06-01')).to_s
|
|
139
|
+
# => "2024-05-26 00:00:00 +0900"
|
|
140
|
+
#
|
|
141
|
+
# `Fugit::Cron#next_time` and `#previous_time` accept a "start time"
|
|
142
|
+
|
|
143
|
+
c = Fugit.parse_cron('0 12 * * mon#2')
|
|
144
|
+
|
|
145
|
+
# `#next` and `#prev` return Enumerable instances
|
|
146
|
+
#
|
|
147
|
+
# These two methods are available since fugit 1.10.0.
|
|
148
|
+
#
|
|
149
|
+
c.next(Time.parse('2024-02-16 12:00:00'))
|
|
150
|
+
.take(3)
|
|
151
|
+
.map(&:to_s)
|
|
152
|
+
# => [ '2024-03-11 12:00:00',
|
|
153
|
+
# '2024-04-08 12:00:00',
|
|
154
|
+
# '2024-05-13 12:00:00' ]
|
|
155
|
+
c.prev(Time.parse('2024-02-16 12:00:00'))
|
|
156
|
+
.take(3)
|
|
157
|
+
.map(&:to_s)
|
|
158
|
+
# => [ '2024-02-12 12:00:00',
|
|
159
|
+
# '2024-01-08 12:00:00',
|
|
160
|
+
# '2023-12-11 12:00:00' ]
|
|
161
|
+
|
|
162
|
+
# `#within` accepts a time range and returns an array of Eo::EoTime
|
|
163
|
+
# instances that correspond to the occurrences of the cron within
|
|
164
|
+
# the time range
|
|
165
|
+
#
|
|
166
|
+
# This method is available since fugit 1.10.0.
|
|
167
|
+
#
|
|
168
|
+
c.within(Time.parse('2024-02-16 12:00')..Time.parse('2024-08-01 12:00'))
|
|
169
|
+
.map(&:to_s)
|
|
170
|
+
# => [ '2024-03-11 12:00:00',
|
|
171
|
+
# '2024-04-08 12:00:00',
|
|
172
|
+
# '2024-05-13 12:00:00',
|
|
173
|
+
# '2024-06-10 12:00:00',
|
|
174
|
+
# '2024-07-08 12:00:00' ]
|
|
128
175
|
|
|
129
176
|
p c.brute_frequency # => [ 604800, 604800, 53 ]
|
|
130
177
|
# [ delta min, delta max, occurrence count ]
|
|
@@ -160,13 +207,28 @@ Example of cron strings understood by fugit:
|
|
|
160
207
|
|
|
161
208
|
Please note that `'15/30 * * * *'` is interpreted as `'15-59/30 * * * *'` since fugit 1.4.6.
|
|
162
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
|
+
|
|
163
225
|
### the first Monday of the month
|
|
164
226
|
|
|
165
227
|
Fugit tries to follow the `man 5 crontab` documentation.
|
|
166
228
|
|
|
167
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 `*`).
|
|
168
230
|
|
|
169
|
-
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.
|
|
170
232
|
|
|
171
233
|
The man page says:
|
|
172
234
|
|
|
@@ -212,6 +274,8 @@ p Fugit.parse_cron('59 6 1-7& * 2&').next_time('2020-03-15').to_s
|
|
|
212
274
|
|
|
213
275
|
Fugit understands `0 5 * * 1#1` or `0 5 * * mon#1` as "each first Monday of the month, at 05:00".
|
|
214
276
|
|
|
277
|
+
The hash extension can only be used in the day-of-week field.
|
|
278
|
+
|
|
215
279
|
```ruby
|
|
216
280
|
'0 5 * * 1#1' #
|
|
217
281
|
'0 5 * * mon#1' # the first Monday of the month at 05:00
|
|
@@ -232,125 +296,201 @@ Fugit understands `0 5 * * 1#1` or `0 5 * * mon#1` as "each first Monday of the
|
|
|
232
296
|
|
|
233
297
|
### the modulo extension
|
|
234
298
|
|
|
235
|
-
|
|
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
|
+
|
|
301
|
+
The modulo extension can only be used in the day-of-week field.
|
|
236
302
|
|
|
237
303
|
For odd Sundays, one can write `9 0 * * sun%2+1`.
|
|
238
304
|
|
|
239
|
-
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).
|
|
240
306
|
|
|
241
|
-
|
|
307
|
+
What does `sun%2` actually mean?
|
|
308
|
+
```ruby
|
|
309
|
+
t.wday == 0 && t.rweek % 2 == 0
|
|
310
|
+
```
|
|
242
311
|
|
|
312
|
+
What does `tue%3+2` mean?
|
|
243
313
|
```ruby
|
|
244
|
-
|
|
314
|
+
t.wday == 2 && t.rweek % 3 == 2
|
|
315
|
+
```
|
|
245
316
|
|
|
246
|
-
|
|
247
|
-
p EtOrbi.parse('2019-01-01').wday # => 2
|
|
248
|
-
p EtOrbi.parse('2019-01-01').rweek # => 1
|
|
249
|
-
p EtOrbi.parse('2019-01-01').rweek % 2 # => 1
|
|
317
|
+
#### et-orbi < 1.4.0 : reference set on Tuesday 2019-01-01
|
|
250
318
|
|
|
251
|
-
|
|
252
|
-
p EtOrbi.parse('2019-04-11').wday # => 4
|
|
253
|
-
p EtOrbi.parse('2019-04-11').rweek # => 15
|
|
254
|
-
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.
|
|
255
320
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
321
|
+
Consider this iteration through the days around 2019-01-01.
|
|
322
|
+
```ruby
|
|
323
|
+
require 'fugit'
|
|
259
324
|
|
|
260
|
-
|
|
261
|
-
c.match?('2019-01-01') # => true, since (rweek + 1) % 2 == 0
|
|
262
|
-
c.match?('2019-01-08') # => false, since (rweek + 1) % 2 == 1
|
|
325
|
+
t = EtOrbi.parse('2018-12-28 12:00')
|
|
263
326
|
|
|
264
|
-
|
|
265
|
-
```
|
|
327
|
+
15.times do |i|
|
|
266
328
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
`tue%x+y` matches if Tuesday and `current_date.rweek + y % x == 0`
|
|
329
|
+
puts " * %14s / rday: %5d / rweek: %5d" % [
|
|
330
|
+
t.strftime('%F %a'), t.rday, t.rweek ]
|
|
270
331
|
|
|
332
|
+
w = t.rweek
|
|
333
|
+
t = t.add(24 * 3600)
|
|
334
|
+
puts if t.rweek != w
|
|
271
335
|
|
|
272
|
-
|
|
336
|
+
if i == 7
|
|
337
|
+
puts "\n (...)\n\n"
|
|
338
|
+
t = EtOrbi.parse('2025-10-04 12:00')
|
|
339
|
+
end
|
|
340
|
+
end
|
|
341
|
+
```
|
|
273
342
|
|
|
274
|
-
|
|
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
|
+
```
|
|
275
366
|
|
|
276
|
-
|
|
367
|
+
This [was problematic](https://github.com/floraison/fugit/issues/114), since the week started on, well, Tuesday.
|
|
277
368
|
|
|
278
|
-
```ruby
|
|
279
|
-
require 'fugit'
|
|
280
369
|
|
|
281
|
-
|
|
370
|
+
#### et-orbi >= 1.4.0 : reference set on Monday 2018-12-31
|
|
282
371
|
|
|
283
|
-
|
|
284
|
-
p d.to_iso_s # => "P1Y2M1DT4H" ISO 8601 duration
|
|
285
|
-
p d.to_long_s # => "1 year, 2 months, 1 day, and 4 hours"
|
|
372
|
+
Since 1.4.0, et-orbi starts by default on Monday (2018-12-31), as rday 0 with rweek 0.
|
|
286
373
|
|
|
287
|
-
|
|
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
|
+
```
|
|
288
397
|
|
|
289
|
-
p d.to_long_s # => "2 years, 2 months, 1 day, and 5 hours"
|
|
290
398
|
|
|
291
|
-
|
|
399
|
+
#### modulo and et-orbi >= 1.4.0 sanity check
|
|
292
400
|
|
|
293
|
-
|
|
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.
|
|
294
402
|
|
|
295
|
-
|
|
296
|
-
|
|
403
|
+
```ruby
|
|
404
|
+
require 'fugit'
|
|
297
405
|
|
|
298
|
-
|
|
406
|
+
c = Fugit.parse_cron('0 12 * * mon%2,wed%3+1')
|
|
299
407
|
|
|
300
|
-
|
|
301
|
-
Fugit::Duration.parse(1000).to_plain_s # => "1000s"
|
|
302
|
-
Fugit::Duration.parse(3600).to_plain_s # => "3600s"
|
|
303
|
-
Fugit::Duration.parse(1000).deflate.to_plain_s # => "16m40s"
|
|
304
|
-
Fugit::Duration.parse(3600).deflate.to_plain_s # => "1h"
|
|
408
|
+
t = EtOrbi.parse('2025-09-20 12:00')
|
|
305
409
|
|
|
306
|
-
|
|
307
|
-
Fugit.parse(1000).deflate.to_plain_s # => "16m40s"
|
|
308
|
-
Fugit.parse(3600).deflate.to_plain_s # => "1h"
|
|
309
|
-
```
|
|
410
|
+
44.times do
|
|
310
411
|
|
|
311
|
-
|
|
412
|
+
wd = t.strftime('%a')
|
|
413
|
+
wd = %w[ Mon Wed ].include?(wd) ? '*' + wd.upcase : ' ' + wd.downcase
|
|
312
414
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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 }
|
|
316
421
|
|
|
317
|
-
|
|
318
|
-
|
|
422
|
+
w = t.rweek
|
|
423
|
+
t = t.add(24 * 3600)
|
|
424
|
+
puts if t.rweek != w
|
|
425
|
+
end
|
|
319
426
|
```
|
|
320
427
|
|
|
321
|
-
|
|
322
|
-
```
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
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
|
|
329
480
|
```
|
|
330
481
|
|
|
331
|
-
## `Fugit::At`
|
|
332
|
-
|
|
333
|
-
Points in time are parsed and given back as EtOrbi::EoTime instances.
|
|
334
482
|
|
|
335
|
-
|
|
336
|
-
Fugit::At.parse('2017-12-12').to_s
|
|
337
|
-
# ==> "2017-12-12 00:00:00 +0900" (at least here in Hiroshima)
|
|
483
|
+
### the second extension
|
|
338
484
|
|
|
339
|
-
Fugit
|
|
340
|
-
# ==> "2017-12-12 12:00:00 -0500"
|
|
341
|
-
```
|
|
485
|
+
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`.
|
|
342
486
|
|
|
343
|
-
Directly with `Fugit.parse_at(s)` is OK too:
|
|
344
487
|
```ruby
|
|
345
|
-
Fugit.
|
|
346
|
-
|
|
488
|
+
c = Fugit.parse('* * * * *') # every minute
|
|
489
|
+
c = Fugit.parse('5 * * * *') # every hour at minute 5
|
|
490
|
+
c = Fugit.parse('* * * * * *') # every second
|
|
491
|
+
c = Fugit.parse('5 * * * * *') # every minute at second 5
|
|
347
492
|
```
|
|
348
493
|
|
|
349
|
-
Directly with `Fugit.parse(s)` is OK too:
|
|
350
|
-
```ruby
|
|
351
|
-
Fugit.parse('2017-12-12 12:00:00 America/New_York').to_s
|
|
352
|
-
# ==> "2017-12-12 12:00:00 -0500"
|
|
353
|
-
```
|
|
354
494
|
|
|
355
495
|
## `Fugit::Nat`
|
|
356
496
|
|
|
@@ -402,7 +542,8 @@ Fugit::Nat.parse('every day at 16:15 and 18:30', multi: true)
|
|
|
402
542
|
# ==> [ '15 16 * * *', '30 18 * * *' ] (two Fugit::Cron instances)
|
|
403
543
|
|
|
404
544
|
Fugit::Nat.parse('every day at 16:15 and 18:30', multi: :fail)
|
|
405
|
-
# ==> ArgumentError: multiple crons in "every day at 16:15 and 18:30"
|
|
545
|
+
# ==> ArgumentError: multiple crons in "every day at 16:15 and 18:30"
|
|
546
|
+
# (15 16 * * * | 30 18 * * *)
|
|
406
547
|
Fugit::Nat.parse('every day at 16:15 nada 18:30', multi: true)
|
|
407
548
|
# ==> nil
|
|
408
549
|
```
|
|
@@ -413,6 +554,8 @@ Fugit::Nat.parse('every day at 16:15 nada 18:30', multi: true)
|
|
|
413
554
|
|
|
414
555
|
`multi: false` is the default behaviour, return a single `Fugit::Cron` instance or nil when it cannot parse.
|
|
415
556
|
|
|
557
|
+
Please note that "nat" input is limited to 256 characters (fugit 1.11.1).
|
|
558
|
+
|
|
416
559
|
### Nat Midnight
|
|
417
560
|
|
|
418
561
|
`"Every day at midnight"` is supported, but `"Every monday at midnight"` will be interpreted (as of Fugit <= 1.4.x) as `"Every monday at 00:00"`. Sorry about that.
|
|
@@ -448,6 +591,96 @@ p Fugit.parse('every day at 12:15 midnight').original # ==> "15 24 * * *"
|
|
|
448
591
|
```
|
|
449
592
|
|
|
450
593
|
|
|
594
|
+
## `Fugit::Duration`
|
|
595
|
+
|
|
596
|
+
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).
|
|
597
|
+
|
|
598
|
+
Provides duration arithmetic tools.
|
|
599
|
+
|
|
600
|
+
```ruby
|
|
601
|
+
require 'fugit'
|
|
602
|
+
|
|
603
|
+
d = Fugit::Duration.parse('1y2M1d4h')
|
|
604
|
+
|
|
605
|
+
p d.to_plain_s # => "1Y2M1D4h"
|
|
606
|
+
p d.to_iso_s # => "P1Y2M1DT4H" ISO 8601 duration
|
|
607
|
+
p d.to_long_s # => "1 year, 2 months, 1 day, and 4 hours"
|
|
608
|
+
|
|
609
|
+
d += Fugit::Duration.parse('1y1h')
|
|
610
|
+
|
|
611
|
+
p d.to_long_s # => "2 years, 2 months, 1 day, and 5 hours"
|
|
612
|
+
|
|
613
|
+
d += 3600
|
|
614
|
+
|
|
615
|
+
p d.to_plain_s # => "2Y2M1D5h3600s"
|
|
616
|
+
|
|
617
|
+
p Fugit::Duration.parse('1y2M1d4h').to_sec # => 36820800
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
There is a `#deflate` method
|
|
621
|
+
|
|
622
|
+
```ruby
|
|
623
|
+
Fugit::Duration.parse(1000).to_plain_s # => "1000s"
|
|
624
|
+
Fugit::Duration.parse(3600).to_plain_s # => "3600s"
|
|
625
|
+
Fugit::Duration.parse(1000).deflate.to_plain_s # => "16m40s"
|
|
626
|
+
Fugit::Duration.parse(3600).deflate.to_plain_s # => "1h"
|
|
627
|
+
|
|
628
|
+
# or event shorter
|
|
629
|
+
Fugit.parse(1000).deflate.to_plain_s # => "16m40s"
|
|
630
|
+
Fugit.parse(3600).deflate.to_plain_s # => "1h"
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
There is also an `#inflate` method
|
|
634
|
+
|
|
635
|
+
```ruby
|
|
636
|
+
Fugit::Duration.parse('1h30m12').inflate.to_plain_s # => "5412s"
|
|
637
|
+
Fugit.parse('1h30m12').inflate.to_plain_s # => "5412s"
|
|
638
|
+
|
|
639
|
+
Fugit.parse('1h30m12').to_sec # => 5412
|
|
640
|
+
Fugit.parse('1h30m12').to_sec.to_s + 's' # => "5412s"
|
|
641
|
+
```
|
|
642
|
+
|
|
643
|
+
The `to_*_s` methods are also available as class methods:
|
|
644
|
+
```ruby
|
|
645
|
+
p Fugit::Duration.to_plain_s('1y2M1d4h')
|
|
646
|
+
# => "1Y2M1D4h"
|
|
647
|
+
p Fugit::Duration.to_iso_s('1y2M1d4h')
|
|
648
|
+
# => "P1Y2M1DT4H" ISO 8601 duration
|
|
649
|
+
p Fugit::Duration.to_long_s('1y2M1d4h')
|
|
650
|
+
# => "1 year, 2 months, 1 day, and 4 hours"
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
## `Fugit::At`
|
|
655
|
+
|
|
656
|
+
Points in time are parsed and given back as EtOrbi::EoTime instances.
|
|
657
|
+
|
|
658
|
+
```ruby
|
|
659
|
+
Fugit::At.parse('2017-12-12').to_s
|
|
660
|
+
# ==> "2017-12-12 00:00:00 +0900" (at least here in Hiroshima)
|
|
661
|
+
|
|
662
|
+
Fugit::At.parse('2017-12-12 12:00:00 America/New_York').to_s
|
|
663
|
+
# ==> "2017-12-12 12:00:00 -0500"
|
|
664
|
+
```
|
|
665
|
+
|
|
666
|
+
Directly with `Fugit.parse_at(s)` is OK too:
|
|
667
|
+
```ruby
|
|
668
|
+
Fugit.parse_at('2017-12-12 12:00:00 America/New_York').to_s
|
|
669
|
+
# ==> "2017-12-12 12:00:00 -0500"
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
Directly with `Fugit.parse(s)` is OK too:
|
|
673
|
+
```ruby
|
|
674
|
+
Fugit.parse('2017-12-12 12:00:00 America/New_York').to_s
|
|
675
|
+
# ==> "2017-12-12 12:00:00 -0500"
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
|
|
679
|
+
## KNOWN ISSUES
|
|
680
|
+
|
|
681
|
+
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.
|
|
682
|
+
|
|
683
|
+
|
|
451
684
|
## LICENSE
|
|
452
685
|
|
|
453
686
|
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,23 +32,42 @@ 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
|
-
|
|
44
|
-
|
|
45
|
-
self.allocate.send(:init, s, h)
|
|
51
|
+
self.allocate.send(:init, s0, h)
|
|
46
52
|
end
|
|
47
53
|
|
|
48
54
|
def do_parse(s)
|
|
49
55
|
|
|
50
56
|
parse(s) ||
|
|
51
|
-
fail(ArgumentError.new("invalid cron string #{s
|
|
57
|
+
fail(ArgumentError.new("invalid cron string #{trunc(s)}"))
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
protected
|
|
61
|
+
|
|
62
|
+
def trunc(s)
|
|
63
|
+
|
|
64
|
+
if s.is_a?(String)
|
|
65
|
+
r = s.length > 28 ? s[0, 28] + "... len #{s.length}" : s
|
|
66
|
+
r.inspect
|
|
67
|
+
else
|
|
68
|
+
r = s.inspect
|
|
69
|
+
r.length > 35 ? s[0, 35] + '...' : r
|
|
70
|
+
end
|
|
52
71
|
end
|
|
53
72
|
end
|
|
54
73
|
|
|
@@ -156,34 +175,30 @@ module Fugit
|
|
|
156
175
|
|
|
157
176
|
def weekday_hash_match?(nt, hsh)
|
|
158
177
|
|
|
178
|
+
return false unless hsh.is_a?(Integer)
|
|
179
|
+
|
|
159
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
|
|
160
184
|
|
|
161
|
-
|
|
162
|
-
hsh == phsh # positive wday, from the beginning of the month
|
|
163
|
-
else
|
|
164
|
-
hsh == nhsh # negative wday, from the end of the month, -1 == last
|
|
165
|
-
end
|
|
185
|
+
(hsh == phsh) || (hsh == nhsh)
|
|
166
186
|
end
|
|
167
187
|
|
|
168
188
|
def weekday_modulo_match?(nt, mod)
|
|
169
189
|
|
|
170
|
-
(
|
|
190
|
+
mod.is_a?(Array) &&
|
|
191
|
+
((nt.rweek % mod[0]) == (mod[1] % mod[0]))
|
|
171
192
|
end
|
|
172
193
|
|
|
173
194
|
def weekday_match?(nt)
|
|
174
195
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
if hom.is_a?(Array)
|
|
183
|
-
weekday_modulo_match?(nt, hom)
|
|
184
|
-
else
|
|
185
|
-
weekday_hash_match?(nt, hom)
|
|
186
|
-
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)) }
|
|
187
202
|
end
|
|
188
203
|
|
|
189
204
|
def monthday_match?(nt)
|
|
@@ -266,6 +281,8 @@ module Fugit
|
|
|
266
281
|
"please fill an issue at https://git.io/fjJC9"
|
|
267
282
|
) if (i += 1) > MAX_ITERATION_COUNT
|
|
268
283
|
|
|
284
|
+
#tt = t.time;
|
|
285
|
+
#puts " #{tt.strftime('%F %T %:z %A')} #{tt.rweek} #{tt.rweek % 2}"
|
|
269
286
|
(ifrom == t.to_i) && (t.inc(1); next)
|
|
270
287
|
month_match?(t) || (t.inc_month; next)
|
|
271
288
|
day_match?(t) || (t.inc_day; next)
|
|
@@ -309,6 +326,8 @@ module Fugit
|
|
|
309
326
|
"please fill an issue at https://git.io/fjJCQ"
|
|
310
327
|
) if (i += 1) > MAX_ITERATION_COUNT
|
|
311
328
|
|
|
329
|
+
#tt = t.time;
|
|
330
|
+
#puts " #{tt.strftime('%F %T %:z %A')} #{tt.rweek} #{tt.rweek % 4}"
|
|
312
331
|
month_match?(t) || (t.dec_month; next)
|
|
313
332
|
day_match?(t) || (t.dec_day; next)
|
|
314
333
|
hour_match?(t) || (t.dec_hour; next)
|
|
@@ -320,6 +339,60 @@ module Fugit
|
|
|
320
339
|
t.time.translate(from.zone)
|
|
321
340
|
end
|
|
322
341
|
|
|
342
|
+
# Used by Fugit::Cron#next and Fugit::Cron#prev
|
|
343
|
+
#
|
|
344
|
+
class CronIterator
|
|
345
|
+
include ::Enumerable
|
|
346
|
+
|
|
347
|
+
attr_reader :cron, :start, :current, :direction
|
|
348
|
+
|
|
349
|
+
def initialize(cron, direction, start)
|
|
350
|
+
|
|
351
|
+
@cron = cron
|
|
352
|
+
@start = start
|
|
353
|
+
@current = start.dup
|
|
354
|
+
@direction = direction
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
def each
|
|
358
|
+
|
|
359
|
+
loop do
|
|
360
|
+
|
|
361
|
+
yield(@current = @cron.send(@direction, @current))
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
# Returns an ::Enumerable instance that yields each "next time" in
|
|
367
|
+
# succession
|
|
368
|
+
#
|
|
369
|
+
def next(from=::EtOrbi::EoTime.now)
|
|
370
|
+
|
|
371
|
+
CronIterator.new(self, :next_time, from)
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
# Returns an ::Enumerable instance that yields each "previous time" in
|
|
375
|
+
# succession
|
|
376
|
+
#
|
|
377
|
+
def prev(from=::EtOrbi::EoTime.now)
|
|
378
|
+
|
|
379
|
+
CronIterator.new(self, :previous_time, from)
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
# Returns an array of EtOrbi::EoTime instances that correspond to
|
|
383
|
+
# the occurrences of the cron within the given time range
|
|
384
|
+
#
|
|
385
|
+
def within(time_range, time_end=nil)
|
|
386
|
+
|
|
387
|
+
sta, ned =
|
|
388
|
+
time_range.is_a?(::Range) ? [ time_range.begin, time_range.end ] :
|
|
389
|
+
[ ::EtOrbi.make_time(time_range), ::EtOrbi.make_time(time_end) ]
|
|
390
|
+
|
|
391
|
+
CronIterator
|
|
392
|
+
.new(self, :next_time, sta)
|
|
393
|
+
.take_while { |eot| eot.to_t < ned }
|
|
394
|
+
end
|
|
395
|
+
|
|
323
396
|
# Mostly used as a #next_time sanity check.
|
|
324
397
|
# Avoid for "business" use, it's slow.
|
|
325
398
|
#
|
|
@@ -488,18 +561,22 @@ module Fugit
|
|
|
488
561
|
|
|
489
562
|
def init(original, h)
|
|
490
563
|
|
|
564
|
+
return nil unless h
|
|
565
|
+
|
|
491
566
|
@original = original
|
|
492
567
|
@cron_s = nil # just to be sure
|
|
493
568
|
@day_and = h[:&]
|
|
494
569
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
570
|
+
valid =
|
|
571
|
+
determine_seconds(h[:sec]) &&
|
|
572
|
+
determine_minutes(h[:min]) &&
|
|
573
|
+
determine_hours(h[:hou]) &&
|
|
574
|
+
determine_monthdays(h[:dom]) &&
|
|
575
|
+
determine_months(h[:mon]) &&
|
|
576
|
+
determine_weekdays(h[:dow]) &&
|
|
577
|
+
determine_timezone(h[:tz])
|
|
502
578
|
|
|
579
|
+
return nil unless valid
|
|
503
580
|
return nil unless compact_month_days
|
|
504
581
|
|
|
505
582
|
self
|
|
@@ -509,10 +586,15 @@ module Fugit
|
|
|
509
586
|
|
|
510
587
|
sta, edn, sla = r
|
|
511
588
|
|
|
589
|
+
#return false if sla && sla > max
|
|
590
|
+
#
|
|
591
|
+
# let it go, "* */24 * * *" and "* */27 * * *" are okay
|
|
592
|
+
# gh-86 and gh-103
|
|
593
|
+
|
|
512
594
|
edn = max if sla && edn.nil?
|
|
513
595
|
|
|
514
|
-
return
|
|
515
|
-
return
|
|
596
|
+
return nil if sta.nil? && edn.nil? && sla.nil?
|
|
597
|
+
return sta if sta && edn.nil?
|
|
516
598
|
|
|
517
599
|
sla = 1 if sla == nil
|
|
518
600
|
sta = min if sta == nil
|
|
@@ -563,42 +645,41 @@ module Fugit
|
|
|
563
645
|
.uniq
|
|
564
646
|
end
|
|
565
647
|
|
|
566
|
-
def
|
|
648
|
+
def do_determine(key, arr, min, max)
|
|
567
649
|
|
|
568
|
-
|
|
650
|
+
null = false
|
|
569
651
|
|
|
570
|
-
|
|
571
|
-
|
|
652
|
+
r = arr
|
|
653
|
+
.collect { |v|
|
|
654
|
+
expand(min, max, v) }
|
|
655
|
+
.flatten(1)
|
|
656
|
+
.collect { |e|
|
|
657
|
+
return false if e == false
|
|
658
|
+
null = null || e == nil
|
|
659
|
+
(key == :hours && e == 24) ? 0 : e }
|
|
572
660
|
|
|
573
|
-
|
|
574
|
-
|
|
661
|
+
return nil if null
|
|
662
|
+
r.uniq.sort
|
|
575
663
|
end
|
|
576
664
|
|
|
577
665
|
def determine_seconds(arr)
|
|
578
|
-
@seconds = (arr || [ 0 ]
|
|
579
|
-
compact(:@seconds)
|
|
666
|
+
(@seconds = do_determine(:seconds, arr || [ 0 ], 0, 59)) != false
|
|
580
667
|
end
|
|
581
668
|
|
|
582
669
|
def determine_minutes(arr)
|
|
583
|
-
@minutes =
|
|
584
|
-
compact(:@minutes)
|
|
670
|
+
(@minutes = do_determine(:minutes, arr, 0, 59)) != false
|
|
585
671
|
end
|
|
586
672
|
|
|
587
673
|
def determine_hours(arr)
|
|
588
|
-
@hours = arr
|
|
589
|
-
.inject([]) { |a, h| a.concat(expand(0, 23, h)) }
|
|
590
|
-
.collect { |h| h == 24 ? 0 : h }
|
|
591
|
-
compact(:@hours)
|
|
674
|
+
(@hours = do_determine(:hours, arr, 0, 23)) != false
|
|
592
675
|
end
|
|
593
676
|
|
|
594
677
|
def determine_monthdays(arr)
|
|
595
|
-
@monthdays =
|
|
596
|
-
compact(:@monthdays)
|
|
678
|
+
(@monthdays = do_determine(:monthdays, arr, 1, 31)) != false
|
|
597
679
|
end
|
|
598
680
|
|
|
599
681
|
def determine_months(arr)
|
|
600
|
-
@months =
|
|
601
|
-
compact(:@months)
|
|
682
|
+
(@months = do_determine(:months, arr, 1, 12)) != false
|
|
602
683
|
end
|
|
603
684
|
|
|
604
685
|
def determine_weekdays(arr)
|
|
@@ -624,11 +705,15 @@ module Fugit
|
|
|
624
705
|
@weekdays.uniq!
|
|
625
706
|
@weekdays.sort!
|
|
626
707
|
@weekdays = nil if @weekdays.empty?
|
|
708
|
+
|
|
709
|
+
true
|
|
627
710
|
end
|
|
628
711
|
|
|
629
712
|
def determine_timezone(z)
|
|
630
713
|
|
|
631
714
|
@zone, @timezone = z
|
|
715
|
+
|
|
716
|
+
true
|
|
632
717
|
end
|
|
633
718
|
|
|
634
719
|
def weekdays_to_cron_s
|
|
@@ -646,7 +731,8 @@ module Fugit
|
|
|
646
731
|
else
|
|
647
732
|
a.collect(&:to_s).join('#')
|
|
648
733
|
end }
|
|
649
|
-
.join(',')
|
|
734
|
+
.join(',') +
|
|
735
|
+
(@day_and ? '&' : '')
|
|
650
736
|
end
|
|
651
737
|
|
|
652
738
|
module Parser include Raabro
|
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
|
|
|
@@ -258,7 +267,7 @@ module Fugit
|
|
|
258
267
|
when Numeric then add_numeric(a)
|
|
259
268
|
when Fugit::Duration then add_duration(a)
|
|
260
269
|
when String then add_duration(self.class.parse(a))
|
|
261
|
-
when ::Time, EtOrbi::EoTime then add_to_time(a)
|
|
270
|
+
when ::Time, ::EtOrbi::EoTime then add_to_time(a)
|
|
262
271
|
else fail ArgumentError.new(
|
|
263
272
|
"cannot add #{a.class} instance to a Fugit::Duration")
|
|
264
273
|
end
|
data/lib/fugit/nat.rb
CHANGED
|
@@ -7,6 +7,8 @@ module Fugit
|
|
|
7
7
|
#
|
|
8
8
|
module Nat
|
|
9
9
|
|
|
10
|
+
MAX_INPUT_LENGTH = 256
|
|
11
|
+
|
|
10
12
|
class << self
|
|
11
13
|
|
|
12
14
|
def parse(s, opts={})
|
|
@@ -17,6 +19,16 @@ module Fugit
|
|
|
17
19
|
|
|
18
20
|
s = s.strip
|
|
19
21
|
|
|
22
|
+
if s.length > MAX_INPUT_LENGTH
|
|
23
|
+
|
|
24
|
+
fail ArgumentError.new(
|
|
25
|
+
"input too long for a nat string, " +
|
|
26
|
+
"#{s.length} > #{MAX_INPUT_LENGTH}"
|
|
27
|
+
) if opts[:do_parse]
|
|
28
|
+
|
|
29
|
+
return nil
|
|
30
|
+
end
|
|
31
|
+
|
|
20
32
|
#p s; Raabro.pp(Parser.parse(s, debug: 3), colours: true)
|
|
21
33
|
#(p s; Raabro.pp(Parser.parse(s, debug: 1), colours: true)) rescue nil
|
|
22
34
|
|
|
@@ -29,7 +41,7 @@ module Fugit
|
|
|
29
41
|
|
|
30
42
|
def do_parse(s, opts={})
|
|
31
43
|
|
|
32
|
-
parse(s, opts) ||
|
|
44
|
+
parse(s, opts.merge(do_parse: true)) ||
|
|
33
45
|
fail(ArgumentError.new("could not parse a nat #{s.inspect}"))
|
|
34
46
|
end
|
|
35
47
|
end
|
|
@@ -42,7 +54,7 @@ module Fugit
|
|
|
42
54
|
%w[ zero ] + one_to_nine +
|
|
43
55
|
%w[ ten eleven twelve thirteen fourteen fifteen sixteen seventeen
|
|
44
56
|
eighteen nineteen ] +
|
|
45
|
-
%w[ twenty thirty
|
|
57
|
+
%w[ twenty thirty forty fifty ]
|
|
46
58
|
.collect { |a|
|
|
47
59
|
([ nil ] + one_to_nine)
|
|
48
60
|
.collect { |b| [ a, b ].compact.join('-') } }
|
|
@@ -198,7 +210,7 @@ module Fugit
|
|
|
198
210
|
end
|
|
199
211
|
|
|
200
212
|
def ampm(i)
|
|
201
|
-
rex(:ampm, i, /[ \t]*(am|pm|
|
|
213
|
+
rex(:ampm, i, /[ \t]*(am|AM|pm|PM|[Nn]oon|[Mm]idday|[Mm]idnight)/)
|
|
202
214
|
end
|
|
203
215
|
def dark(i)
|
|
204
216
|
rex(:dark, i, /[ \t]*dark/i)
|
|
@@ -642,9 +654,9 @@ module Fugit
|
|
|
642
654
|
fail(ArgumentError.new(
|
|
643
655
|
"multiple crons in #{opts[:_s].inspect} - #{@slots.inspect}"))
|
|
644
656
|
elsif multi == true
|
|
645
|
-
hms.collect { |hm| parse_cron(hm) }
|
|
657
|
+
hms.collect { |hm| parse_cron(hm, opts) }
|
|
646
658
|
else
|
|
647
|
-
parse_cron(hms.first)
|
|
659
|
+
parse_cron(hms.first, opts)
|
|
648
660
|
end
|
|
649
661
|
end
|
|
650
662
|
|
|
@@ -678,7 +690,7 @@ module Fugit
|
|
|
678
690
|
.to_a
|
|
679
691
|
end
|
|
680
692
|
|
|
681
|
-
def parse_cron(hm)
|
|
693
|
+
def parse_cron(hm, opts)
|
|
682
694
|
|
|
683
695
|
a = [
|
|
684
696
|
slot(:second, '0'),
|
|
@@ -691,14 +703,46 @@ module Fugit
|
|
|
691
703
|
a << tz.data0 if tz
|
|
692
704
|
a.shift if a.first == [ '0' ]
|
|
693
705
|
|
|
706
|
+
letters_last = lambda { |x| x.is_a?(Numeric) ? x : 999_999 }
|
|
707
|
+
|
|
694
708
|
s = a
|
|
695
|
-
.collect { |e|
|
|
709
|
+
.collect { |e|
|
|
710
|
+
e.uniq.sort_by(&letters_last).collect(&:to_s).join(',') }
|
|
696
711
|
.join(' ')
|
|
697
712
|
|
|
698
|
-
Fugit::Cron.parse(s)
|
|
713
|
+
c = Fugit::Cron.parse(s)
|
|
714
|
+
|
|
715
|
+
if opts[:strict]
|
|
716
|
+
restrict(a, c)
|
|
717
|
+
else
|
|
718
|
+
c
|
|
719
|
+
end
|
|
720
|
+
end
|
|
721
|
+
|
|
722
|
+
SLASH_REGEX = /^(\d+|\*)\/(\d+)$/.freeze
|
|
723
|
+
|
|
724
|
+
# Return nil if the cron is "not strict"
|
|
725
|
+
#
|
|
726
|
+
# For example, "0 0/17 * * *" (gh-86) is a perfectly valid
|
|
727
|
+
# cron string, but makes not much sense when derived via `.parse_nat`
|
|
728
|
+
# from "every 17 hours".
|
|
729
|
+
#
|
|
730
|
+
# It happens here because it's nat being strict, not cron.
|
|
731
|
+
#
|
|
732
|
+
def restrict(a, cron)
|
|
733
|
+
|
|
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)
|
|
739
|
+
end
|
|
740
|
+
|
|
741
|
+
cron
|
|
699
742
|
end
|
|
700
743
|
|
|
701
744
|
def slot(key, default)
|
|
745
|
+
|
|
702
746
|
s = @slots[key]
|
|
703
747
|
s ? s.data0 : [ default ]
|
|
704
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
|
|
@@ -20,17 +22,34 @@ module Fugit
|
|
|
20
22
|
|
|
21
23
|
opts[:at] = opts[:in] if opts.has_key?(:in)
|
|
22
24
|
|
|
23
|
-
(opts[:cron] != false && parse_cron(s)) ||
|
|
24
|
-
(opts[:duration] != false && parse_duration(s)) ||
|
|
25
|
-
(opts[:nat] != false && parse_nat(s, opts)) ||
|
|
26
|
-
(opts[:at] != false && parse_at(s)) ||
|
|
25
|
+
(opts[:cron] != false && parse_cron(s)) || # 542ms 616ms
|
|
26
|
+
(opts[:duration] != false && parse_duration(s)) || # 645ms # 534ms
|
|
27
|
+
(opts[:nat] != false && parse_nat(s, opts)) || # 2s # 35s
|
|
28
|
+
(opts[:at] != false && parse_at(s)) || # 568ms 622ms
|
|
27
29
|
nil
|
|
28
30
|
end
|
|
29
31
|
|
|
30
32
|
def do_parse(s, opts={})
|
|
31
33
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
opts[:at] = opts[:in] if opts.has_key?(:in)
|
|
35
|
+
|
|
36
|
+
result = nil
|
|
37
|
+
errors = []
|
|
38
|
+
|
|
39
|
+
DO_PARSE_ORDER
|
|
40
|
+
.each { |k|
|
|
41
|
+
begin
|
|
42
|
+
result ||= (opts[k] != false && self.send("do_parse_#{k}", s))
|
|
43
|
+
rescue => err
|
|
44
|
+
errors << err
|
|
45
|
+
end }
|
|
46
|
+
|
|
47
|
+
return result if result
|
|
48
|
+
|
|
49
|
+
raise(
|
|
50
|
+
errors.find { |r| r.class != ArgumentError } ||
|
|
51
|
+
errors.first ||
|
|
52
|
+
ArgumentError.new("found no time information in #{s.inspect}"))
|
|
34
53
|
end
|
|
35
54
|
|
|
36
55
|
def parse_cronish(s, opts={})
|
|
@@ -46,6 +65,24 @@ module Fugit
|
|
|
46
65
|
fail(ArgumentError.new("not cron or 'natural' cron string: #{s.inspect}"))
|
|
47
66
|
end
|
|
48
67
|
|
|
68
|
+
def parse_max(s, opts={})
|
|
69
|
+
|
|
70
|
+
s0 = s.lines.first
|
|
71
|
+
|
|
72
|
+
(0..[ ::Fugit::Nat::MAX_INPUT_LENGTH, s0.length - 1 ].min).each do |i|
|
|
73
|
+
|
|
74
|
+
s1 =
|
|
75
|
+
s0[0, s0.length - i].rstrip
|
|
76
|
+
f =
|
|
77
|
+
opts[:cronish] ? parse_cronish(s1, opts) :
|
|
78
|
+
parse(s1, opts)
|
|
79
|
+
|
|
80
|
+
return [ s1, f ] if f
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
nil
|
|
84
|
+
end
|
|
85
|
+
|
|
49
86
|
def determine_type(s)
|
|
50
87
|
|
|
51
88
|
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.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:
|
|
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.7
|
|
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.7
|
|
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.
|
|
114
|
+
rubygems_version: 3.4.19
|
|
120
115
|
signing_key:
|
|
121
116
|
specification_version: 4
|
|
122
117
|
summary: time tools for flor
|