fugit 1.12.2 → 1.13.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 +10 -0
- data/CREDITS.md +2 -1
- data/README.md +23 -0
- data/fugit.gemspec +1 -0
- data/lib/fugit/cron.rb +82 -38
- data/lib/fugit/nat.rb +1 -1
- data/lib/fugit/parse.rb +5 -5
- data/lib/fugit.rb +1 -1
- metadata +16 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 15c7da961f6616177b4837e7db2b8d2cbef8a73af0e39d482bbde88ca927d420
|
|
4
|
+
data.tar.gz: bdb787682ba39f3bd873c935378d7097291ff275c9c2443a4bf42990e71dfa92
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c920de689a07b8e5b082afa2d67ff26a2ad3422e0a35afc0c02fee71d92d6a76da391d7ec6d03e55d62cbc1a46cbf1296bccb4a37ec9df9d72f35d3e4ecaf0ad
|
|
7
|
+
data.tar.gz: 11694200215f7506f64f862d7f63e86b3fc79be5ad4a4ddfe9278143c40991bcd2c89356e9e314d5caad125b0b26623daf3da64ee2e92b49ca1480b59fc220f3
|
data/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
# CHANGELOG.md
|
|
3
3
|
|
|
4
4
|
|
|
5
|
+
## fugit 1.13.0 released 2026-07-10
|
|
6
|
+
|
|
7
|
+
* Random values support in cron string `"10 ~ * * *"` gh-121 Nicols Bachschmidt
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## fugit 1.12.3 released 2026-07-01
|
|
11
|
+
|
|
12
|
+
* Wrap-around for weekday ranges with step value, gh-119 Nicolas Bachschmidt
|
|
13
|
+
|
|
14
|
+
|
|
5
15
|
## fugit 1.12.2 released 2026-05-28
|
|
6
16
|
|
|
7
17
|
* Fix "divide by zero" gh-117
|
data/CREDITS.md
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
|
|
2
2
|
# fugit credits
|
|
3
3
|
|
|
4
|
+
* Nicolas Bachschmidt https://github.com/baarde gh-120 weekday wrap fix
|
|
4
5
|
* JebeenLee https://github.com/JebeenLee gh-117 divide by zero
|
|
5
6
|
* Eric Claerhout https://github.com/swebra gh-114 rweek readme clarity
|
|
6
7
|
* Hugh Kelsey https://github.com/hughkelsey gh-114 rweek/rday / wed%4+1,wed%4
|
|
7
8
|
* Mark R. James, https://github.com/mrj, AM vs America/Los_Angeles, gh-113
|
|
8
9
|
* Tejas Bubane, https://github.com/tejasbubane, r3.4 in test matrix, gh-109
|
|
9
10
|
* Luis Castillo, https://github.com/lekastillo, nice_hash gh-108
|
|
10
|
-
* Geremia Taglialatela, https://github.com/tagliala, gh-105 gh-107
|
|
11
|
+
* Geremia Taglialatela, https://github.com/tagliala, gh-105 gh-107 gh-122
|
|
11
12
|
* https://github.com/personnumber3377, gh-104 Fugit.parse choke on long input
|
|
12
13
|
* Michael Scrivo, https://github.com/mscrivo, gh-103
|
|
13
14
|
* Benjamin Darcet, https://github.com/bdarcet gh-95 gh-96 et-orbi #rweek
|
data/README.md
CHANGED
|
@@ -486,6 +486,29 @@ Here's the output:
|
|
|
486
486
|
```
|
|
487
487
|
|
|
488
488
|
|
|
489
|
+
### the random extension
|
|
490
|
+
|
|
491
|
+
Fugit accepts the `~` character to specify a range from which a random value is picked. Either or both the start and the end of the range may be omitted.
|
|
492
|
+
|
|
493
|
+
```ruby
|
|
494
|
+
'~ * * * *' # every hour on a random minute
|
|
495
|
+
'~29 * * * *' # every hour on a random minute between 0 and 29
|
|
496
|
+
'30~ * * * *' # every hour on a random minute between 30 and 59
|
|
497
|
+
'~/10 * * * *' # every 10 minutes starting on a random minute between 0 and 9
|
|
498
|
+
'0 12 * * 1~5' # every week at noon on a random workday
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
You may customize how random values are generated by adding the `:random` option with a boolean or an object that implements the `#rand` method.
|
|
502
|
+
|
|
503
|
+
```ruby
|
|
504
|
+
Fugit.parse('~ * * * *', random: false) # on a non-random minute ==> '0 * * * *'
|
|
505
|
+
Fugit.parse('~ * * * *', random: true) # on a random minute (default)
|
|
506
|
+
Fugit.parse('~ * * * *', random: SecureRandom) # on a cryptographically secure random minute
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
Please note that the random values are determined at parse time. So `"~29 * * * *"` at parse time could become `"17 * * * *"` for example and go on triggering every hour at minute 17, until a new parsing occurs and another (or the same) minute is picked. This aligns with the OpenBSD [`man 5 crontab`](https://man.openbsd.org/crontab.5), OpenBSD `crond` rolling the dice at load time.
|
|
510
|
+
|
|
511
|
+
|
|
489
512
|
### the second extension
|
|
490
513
|
|
|
491
514
|
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`.
|
data/fugit.gemspec
CHANGED
|
@@ -45,6 +45,7 @@ Time tools for flor and the floraison project. Cron parsing and occurrence compu
|
|
|
45
45
|
|
|
46
46
|
s.add_development_dependency 'rspec', '~> 3.8'
|
|
47
47
|
s.add_development_dependency 'chronic', '~> 0.10'
|
|
48
|
+
s.add_development_dependency 'ostruct'
|
|
48
49
|
|
|
49
50
|
s.require_path = 'lib'
|
|
50
51
|
end
|
data/lib/fugit/cron.rb
CHANGED
|
@@ -29,7 +29,7 @@ module Fugit
|
|
|
29
29
|
parse(original)
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
-
def parse(s)
|
|
32
|
+
def parse(s, opts={})
|
|
33
33
|
|
|
34
34
|
return s if s.is_a?(self)
|
|
35
35
|
return nil unless s.is_a?(String)
|
|
@@ -48,12 +48,12 @@ module Fugit
|
|
|
48
48
|
#p s; Raabro.pp(Parser.parse(s, debug: 3), colors: true)
|
|
49
49
|
h = Parser.parse(s)
|
|
50
50
|
|
|
51
|
-
self.allocate.send(:init, s0, h)
|
|
51
|
+
self.allocate.send(:init, s0, h, opts)
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
def do_parse(s)
|
|
54
|
+
def do_parse(s, opts={})
|
|
55
55
|
|
|
56
|
-
parse(s) ||
|
|
56
|
+
parse(s, opts) ||
|
|
57
57
|
fail(ArgumentError.new("invalid cron string #{trunc(s)}"))
|
|
58
58
|
end
|
|
59
59
|
|
|
@@ -559,7 +559,7 @@ module Fugit
|
|
|
559
559
|
|
|
560
560
|
FREQUENCY_CACHE = {}
|
|
561
561
|
|
|
562
|
-
def init(original, h)
|
|
562
|
+
def init(original, h, opts)
|
|
563
563
|
|
|
564
564
|
return nil unless h
|
|
565
565
|
|
|
@@ -568,12 +568,12 @@ module Fugit
|
|
|
568
568
|
@day_and = h[:&]
|
|
569
569
|
|
|
570
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]) &&
|
|
571
|
+
determine_seconds(h[:sec], opts) &&
|
|
572
|
+
determine_minutes(h[:min], opts) &&
|
|
573
|
+
determine_hours(h[:hou], opts) &&
|
|
574
|
+
determine_monthdays(h[:dom], opts) &&
|
|
575
|
+
determine_months(h[:mon], opts) &&
|
|
576
|
+
determine_weekdays(h[:dow], opts) &&
|
|
577
577
|
determine_timezone(h[:tz])
|
|
578
578
|
|
|
579
579
|
return nil unless valid
|
|
@@ -582,9 +582,9 @@ module Fugit
|
|
|
582
582
|
self
|
|
583
583
|
end
|
|
584
584
|
|
|
585
|
-
def expand(min, max, r)
|
|
585
|
+
def expand(min, max, opts, r)
|
|
586
586
|
|
|
587
|
-
sta, edn, sla = r
|
|
587
|
+
sta, edn, sep, sla = r
|
|
588
588
|
|
|
589
589
|
#return false if sla && sla > max
|
|
590
590
|
#
|
|
@@ -593,6 +593,11 @@ module Fugit
|
|
|
593
593
|
|
|
594
594
|
return false if sla && sla < 1
|
|
595
595
|
|
|
596
|
+
if sep == :tilde
|
|
597
|
+
sta = random(min, max, sta, edn, sla, opts)
|
|
598
|
+
edn = nil unless sla
|
|
599
|
+
end
|
|
600
|
+
|
|
596
601
|
edn = max if sla && edn.nil?
|
|
597
602
|
|
|
598
603
|
return nil if sta.nil? && edn.nil? && sla.nil?
|
|
@@ -605,6 +610,24 @@ module Fugit
|
|
|
605
610
|
range(min, max, sta, edn, sla)
|
|
606
611
|
end
|
|
607
612
|
|
|
613
|
+
def random(min, max, sta, edn, sla, opts)
|
|
614
|
+
|
|
615
|
+
sta = min if sta.nil?
|
|
616
|
+
edn = max if edn.nil?
|
|
617
|
+
|
|
618
|
+
random = opts.fetch(:random, true)
|
|
619
|
+
random = Random if random == true
|
|
620
|
+
return sta unless random
|
|
621
|
+
|
|
622
|
+
n = edn - sta + 1
|
|
623
|
+
n = max - min + 1 + n if n <= 0
|
|
624
|
+
n = sla if sla && sla < n
|
|
625
|
+
|
|
626
|
+
r = sta + random.rand(n)
|
|
627
|
+
r = r + min - max - 1 if r > max
|
|
628
|
+
r
|
|
629
|
+
end
|
|
630
|
+
|
|
608
631
|
def range(min, max, sta, edn, sla)
|
|
609
632
|
|
|
610
633
|
return [ nil ] if sta == min && edn == max && sla == 1
|
|
@@ -647,13 +670,13 @@ module Fugit
|
|
|
647
670
|
.uniq
|
|
648
671
|
end
|
|
649
672
|
|
|
650
|
-
def do_determine(key, arr, min, max)
|
|
673
|
+
def do_determine(key, arr, min, max, opts)
|
|
651
674
|
|
|
652
675
|
null = false
|
|
653
676
|
|
|
654
677
|
r = arr
|
|
655
678
|
.collect { |v|
|
|
656
|
-
expand(min, max, v) }
|
|
679
|
+
expand(min, max, opts, v) }
|
|
657
680
|
.flatten(1)
|
|
658
681
|
.collect { |e|
|
|
659
682
|
return false if e == false
|
|
@@ -664,36 +687,44 @@ module Fugit
|
|
|
664
687
|
r.uniq.sort
|
|
665
688
|
end
|
|
666
689
|
|
|
667
|
-
def determine_seconds(arr)
|
|
668
|
-
(@seconds = do_determine(:seconds, arr || [ 0 ], 0, 59)) != false
|
|
690
|
+
def determine_seconds(arr, opts)
|
|
691
|
+
(@seconds = do_determine(:seconds, arr || [ 0 ], 0, 59, opts)) != false
|
|
669
692
|
end
|
|
670
693
|
|
|
671
|
-
def determine_minutes(arr)
|
|
672
|
-
(@minutes = do_determine(:minutes, arr, 0, 59)) != false
|
|
694
|
+
def determine_minutes(arr, opts)
|
|
695
|
+
(@minutes = do_determine(:minutes, arr, 0, 59, opts)) != false
|
|
673
696
|
end
|
|
674
697
|
|
|
675
|
-
def determine_hours(arr)
|
|
676
|
-
(@hours = do_determine(:hours, arr, 0, 23)) != false
|
|
698
|
+
def determine_hours(arr, opts)
|
|
699
|
+
(@hours = do_determine(:hours, arr, 0, 23, opts)) != false
|
|
677
700
|
end
|
|
678
701
|
|
|
679
|
-
def determine_monthdays(arr)
|
|
680
|
-
(@monthdays = do_determine(:monthdays, arr, 1, 31)) != false
|
|
702
|
+
def determine_monthdays(arr, opts)
|
|
703
|
+
(@monthdays = do_determine(:monthdays, arr, 1, 31, opts)) != false
|
|
681
704
|
end
|
|
682
705
|
|
|
683
|
-
def determine_months(arr)
|
|
684
|
-
(@months = do_determine(:months, arr, 1, 12)) != false
|
|
706
|
+
def determine_months(arr, opts)
|
|
707
|
+
(@months = do_determine(:months, arr, 1, 12, opts)) != false
|
|
685
708
|
end
|
|
686
709
|
|
|
687
|
-
def determine_weekdays(arr)
|
|
710
|
+
def determine_weekdays(arr, opts)
|
|
688
711
|
|
|
689
712
|
@weekdays = []
|
|
690
713
|
|
|
691
|
-
arr.each do |a, z, sl, ha, mo| # a to z, slash, hash, and mod
|
|
714
|
+
arr.each do |a, z, sp, sl, ha, mo| # a to z, seperator, slash, hash, and mod
|
|
715
|
+
if sp == :tilde
|
|
716
|
+
a = random(0, 6, a, z, sl, opts)
|
|
717
|
+
z = nil unless sl
|
|
718
|
+
end
|
|
719
|
+
|
|
692
720
|
if ha || mo
|
|
693
721
|
@weekdays << [ a, ha || mo ]
|
|
694
722
|
elsif sl
|
|
695
|
-
|
|
696
|
-
|
|
723
|
+
z ||= (a ? a : 6)
|
|
724
|
+
a ||= 0
|
|
725
|
+
z = z + 7 if a > z
|
|
726
|
+
(a..z).step(sl < 1 ? 1 : sl)
|
|
727
|
+
.each { |i| @weekdays << [ (i > 6) ? i - 7 : i ] }
|
|
697
728
|
elsif z
|
|
698
729
|
z = z + 7 if a > z
|
|
699
730
|
(a..z).each { |i| @weekdays << [ (i > 6) ? i - 7 : i ] }
|
|
@@ -756,7 +787,8 @@ module Fugit
|
|
|
756
787
|
|
|
757
788
|
def s(i); rex(nil, i, /[ \t]+/); end
|
|
758
789
|
def star(i); str(nil, i, '*'); end
|
|
759
|
-
def hyphen(i); str(
|
|
790
|
+
def hyphen(i); str(:hyphen, i, '-'); end
|
|
791
|
+
def tilde(i); str(:tilde, i, '~'); end
|
|
760
792
|
def comma(i); rex(nil, i, /,([ \t]*,)*/); end
|
|
761
793
|
def comma?(i); rex(nil, i, /([ \t]*,)*/); end
|
|
762
794
|
def and?(i); rex(nil, i, /&?/); end
|
|
@@ -784,12 +816,19 @@ module Fugit
|
|
|
784
816
|
def r_mon(i); seq(nil, i, :mon, :_mon, '?'); end
|
|
785
817
|
def r_dow(i); seq(nil, i, :dow, :_dow, '?'); end
|
|
786
818
|
|
|
819
|
+
# t: tilde range
|
|
820
|
+
def t_mos(i); seq(nil, i, :mos, '?', :tilde, :mos, '?'); end
|
|
821
|
+
def t_hou(i); seq(nil, i, :hou, '?', :tilde, :hou, '?'); end
|
|
822
|
+
def t_dom(i); seq(nil, i, :dom, '?', :tilde, :dom, '?'); end
|
|
823
|
+
def t_mon(i); seq(nil, i, :mon, '?', :tilde, :mon, '?'); end
|
|
824
|
+
def t_dow(i); seq(nil, i, :dow, '?', :tilde, :dow, '?'); end
|
|
825
|
+
|
|
787
826
|
# sor: star or range
|
|
788
|
-
def sor_mos(i); alt(nil, i, :star, :r_mos); end
|
|
789
|
-
def sor_hou(i); alt(nil, i, :star, :r_hou); end
|
|
790
|
-
def sor_dom(i); alt(nil, i, :star, :r_dom); end
|
|
791
|
-
def sor_mon(i); alt(nil, i, :star, :r_mon); end
|
|
792
|
-
def sor_dow(i); alt(nil, i, :star, :r_dow); end
|
|
827
|
+
def sor_mos(i); alt(nil, i, :star, :t_mos, :r_mos); end
|
|
828
|
+
def sor_hou(i); alt(nil, i, :star, :t_hou, :r_hou); end
|
|
829
|
+
def sor_dom(i); alt(nil, i, :star, :t_dom, :r_dom); end
|
|
830
|
+
def sor_mon(i); alt(nil, i, :star, :t_mon, :r_mon); end
|
|
831
|
+
def sor_dow(i); alt(nil, i, :star, :t_dow, :r_dow); end
|
|
793
832
|
|
|
794
833
|
# sorws: star or range with[out] slash
|
|
795
834
|
def sorws_mos(i); seq(nil, i, :sor_mos, :slash, '?'); end
|
|
@@ -869,12 +908,16 @@ module Fugit
|
|
|
869
908
|
|
|
870
909
|
def rewrite_elt(k, t)
|
|
871
910
|
|
|
872
|
-
at, zt, slt, hat, mot = nil
|
|
911
|
+
at, zt, spt, slt, hat, mot = nil
|
|
912
|
+
|
|
913
|
+
t.subgather(nil).each do |tt|
|
|
873
914
|
case tt.name
|
|
874
915
|
when :slash then slt = tt
|
|
875
916
|
when :hash then hat = tt
|
|
876
917
|
when :mod then mot = tt
|
|
877
|
-
|
|
918
|
+
when :hyphen then spt = tt
|
|
919
|
+
when :tilde then spt = tt
|
|
920
|
+
else if spt; zt ||= tt; else; at = tt; end
|
|
878
921
|
end
|
|
879
922
|
end
|
|
880
923
|
|
|
@@ -888,11 +931,12 @@ module Fugit
|
|
|
888
931
|
|
|
889
932
|
a = at ? rewrite_bound(k, at) : nil
|
|
890
933
|
z = zt ? rewrite_bound(k, zt) : nil
|
|
934
|
+
sp = spt ? spt.name : nil
|
|
891
935
|
|
|
892
936
|
#a, z = z, a if a && z && a > z
|
|
893
937
|
# handled downstream since gh-27
|
|
894
938
|
|
|
895
|
-
[ a, z, sl, ha, mo ]
|
|
939
|
+
[ a, z, sp, sl, ha, mo ]
|
|
896
940
|
end
|
|
897
941
|
|
|
898
942
|
def rewrite_entry(t)
|
data/lib/fugit/nat.rb
CHANGED
data/lib/fugit/parse.rb
CHANGED
|
@@ -6,13 +6,13 @@ module Fugit
|
|
|
6
6
|
|
|
7
7
|
class << self
|
|
8
8
|
|
|
9
|
-
def parse_cron(s); ::Fugit::Cron.parse(s); end
|
|
9
|
+
def parse_cron(s, opts={}); ::Fugit::Cron.parse(s, opts || {}); end
|
|
10
10
|
def parse_duration(s); ::Fugit::Duration.parse(s); end
|
|
11
11
|
def parse_nat(s, opts={}); ::Fugit::Nat.parse(s, opts || {}); end
|
|
12
12
|
def parse_at(s, opts={}); ::Fugit::At.parse(s, opts || {}); end
|
|
13
13
|
def parse_in(s); parse_duration(s); end
|
|
14
14
|
|
|
15
|
-
def do_parse_cron(s); ::Fugit::Cron.do_parse(s); end
|
|
15
|
+
def do_parse_cron(s, opts={}); ::Fugit::Cron.do_parse(s, opts || {}); end
|
|
16
16
|
def do_parse_duration(s); ::Fugit::Duration.do_parse(s); end
|
|
17
17
|
def do_parse_nat(s, opts={}); ::Fugit::Nat.do_parse(s, opts || {}); end
|
|
18
18
|
def do_parse_at(s); ::Fugit::At.do_parse(s); end
|
|
@@ -22,7 +22,7 @@ module Fugit
|
|
|
22
22
|
|
|
23
23
|
opts[:at] = opts[:in] if opts.has_key?(:in)
|
|
24
24
|
|
|
25
|
-
(opts[:cron] != false && parse_cron(s)) || # 542ms 616ms
|
|
25
|
+
(opts[:cron] != false && parse_cron(s, opts || {})) || # 542ms 616ms
|
|
26
26
|
(opts[:duration] != false && parse_duration(s)) || # 645ms # 534ms
|
|
27
27
|
(opts[:nat] != false && parse_nat(s, opts || {})) || # 2s # 35s
|
|
28
28
|
(opts[:at] != false && parse_at(s, opts || {})) || # 568ms 622ms
|
|
@@ -54,14 +54,14 @@ module Fugit
|
|
|
54
54
|
|
|
55
55
|
def parse_cronish(s, opts={})
|
|
56
56
|
|
|
57
|
-
r = parse_cron(s) || parse_nat(s, opts)
|
|
57
|
+
r = parse_cron(s, opts) || parse_nat(s, opts)
|
|
58
58
|
|
|
59
59
|
r.is_a?(::Fugit::Cron) ? r : nil
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
def do_parse_cronish(s, opts={})
|
|
63
63
|
|
|
64
|
-
parse_cronish(s) ||
|
|
64
|
+
parse_cronish(s, opts) ||
|
|
65
65
|
fail(ArgumentError.new("not cron or 'natural' cron string: #{s.inspect}"))
|
|
66
66
|
end
|
|
67
67
|
|
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.13.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: 2026-
|
|
11
|
+
date: 2026-07-10 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: raabro
|
|
@@ -66,6 +66,20 @@ dependencies:
|
|
|
66
66
|
- - "~>"
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
68
|
version: '0.10'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: ostruct
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
69
83
|
description: Time tools for flor and the floraison project. Cron parsing and occurrence
|
|
70
84
|
computing. Timestamps and more.
|
|
71
85
|
email:
|