fugit 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of fugit might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +18 -1
- data/CREDITS.md +7 -1
- data/LICENSE.txt +4 -1
- data/README.md +65 -0
- data/fugit.gemspec +4 -2
- data/lib/fugit.rb +2 -1
- data/lib/fugit/at.rb +17 -0
- data/lib/fugit/cron.rb +103 -44
- data/lib/fugit/duration.rb +123 -45
- data/lib/fugit/parse.rb +4 -12
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c52b936431c2d332e82c316fb52d7e786c577027
|
4
|
+
data.tar.gz: 8d82d60d76b8f041771852d688364eab445f2575
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 24c0e708a7f42af660d14e3718ec395e7dd2e791cb7f2bb5fa2bb3f9d0d2f850a9c78712772854fd182f47bb479a89658513f7fa9eb6d66f55cdca46b788f34d
|
7
|
+
data.tar.gz: b8ba24ce4f558c3cd174bb1b70cf96b2e444999313aee67b0920e7616725cbfaeb98e75964351802b1a6487cb97044288636f450222ed6424dbb2e6833767de6
|
data/CHANGELOG.md
CHANGED
@@ -2,9 +2,26 @@
|
|
2
2
|
# fugit CHANGELOG.md
|
3
3
|
|
4
4
|
|
5
|
+
## fugit 1.1.0 released 2018-03-27
|
6
|
+
|
7
|
+
* Travel in Cron zone in #next_time and #previous_time, return from zone
|
8
|
+
* Parse and store timezone in Fugit::Cron
|
9
|
+
* Introduce Fugit::Duration#deflate month: d / year: d
|
10
|
+
* Introduce Fugit::Duration#drop_seconds
|
11
|
+
* Alias Fugit::Duration#to_h to Fugit::Duration#h
|
12
|
+
* Introduce to_rufus_s (1y2M3d) vs to_plain_s (1Y2M3D)
|
13
|
+
* Ensure Duration#deflate preserves at least `{ sec: 0 }`
|
14
|
+
* Stringify 0 seconds as "0s"
|
15
|
+
* Ignore "-5" and "-5.", only accept "-5s" and "-5.s"
|
16
|
+
* Introduce "signed durations", "-1Y+2Y-3m"
|
17
|
+
* Ensure `1.0d1.0w1.0d` gets parsed correctly
|
18
|
+
* Ensure Fugit::Cron.next_time returns plain seconds (.0, not .1234...)
|
19
|
+
* Introduce Fugit::Frequency for cron
|
20
|
+
|
21
|
+
|
5
22
|
## fugit 1.0.0 released 2017-06-23
|
6
23
|
|
7
|
-
*
|
24
|
+
* Introduce et-orbi dependency (1.0.5 or better)
|
8
25
|
* Wire #deflate into Duration.to_long_s / .to_iso_s / .to_plain_s
|
9
26
|
|
10
27
|
|
data/CREDITS.md
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
|
2
2
|
# fugit credits
|
3
3
|
|
4
|
-
|
4
|
+
* Harry Lascelles https://github.com/hlascelles timezone reminder
|
5
|
+
|
6
|
+
|
7
|
+
## rufus-scheduler credits
|
8
|
+
|
9
|
+
As fugit originates in rufus-scheduler, many thanks to all the
|
10
|
+
rufus-scheduler contributors and people who gave feedback.
|
5
11
|
|
6
12
|
https://github.com/jmettraux/rufus-scheduler/blob/master/CREDITS.txt
|
7
13
|
|
data/LICENSE.txt
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
|
2
|
-
Copyright (c) 2017-
|
2
|
+
Copyright (c) 2017-2018, 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
|
@@ -19,3 +19,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
19
19
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
20
|
THE SOFTWARE.
|
21
21
|
|
22
|
+
|
23
|
+
Made in Japan
|
24
|
+
|
data/README.md
CHANGED
@@ -6,6 +6,8 @@
|
|
6
6
|
|
7
7
|
Time tools for [flor](https://github.com/floraison/flor) and the floraison group.
|
8
8
|
|
9
|
+
It uses [et-orbi](https://github.com/floraison/et-orbi) to represent time instances and [raabro](https://github.com/floraison/raabro) as a basis for its parsers.
|
10
|
+
|
9
11
|
Fugit will probably become the foundation for [rufus-scheduler](https://github.com/jmettraux/rufus-scheduler) 4.x
|
10
12
|
|
11
13
|
|
@@ -25,6 +27,22 @@ Fugit will probably become the foundation for [rufus-scheduler](https://github.c
|
|
25
27
|
* ...
|
26
28
|
|
27
29
|
|
30
|
+
## `Fugit.parse(s)`
|
31
|
+
|
32
|
+
The simplest way to use fugit is via `Fugit.parse(s)`.
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
require 'fugit'
|
36
|
+
|
37
|
+
Fugit.parse('0 0 1 jan *').class # ==> ::Fugit::Cron
|
38
|
+
Fugit.parse('12y12M').class # ==> ::Fugit::Duration
|
39
|
+
|
40
|
+
Fugit.parse('2017-12-12').class # ==> ::EtOrbi::EoTime
|
41
|
+
Fugit.parse('2017-12-12 UTC').class # ==> ::EtOrbi::EoTime
|
42
|
+
|
43
|
+
Fugit.parse('every day at noon').class # ==> ::Fugit::Cron
|
44
|
+
```
|
45
|
+
|
28
46
|
## `Fugit::Cron`
|
29
47
|
|
30
48
|
A class `Fugit::Cron` to parse cron strings and then `#next_time` and `#previous_time` to compute the next or the previous occurrence respectively.
|
@@ -108,6 +126,53 @@ p Fugit::Duration.to_long_s('1y2M1d4h')
|
|
108
126
|
# => "1 year, 2 months, 1 day, and 4 hours"
|
109
127
|
```
|
110
128
|
|
129
|
+
## `Fugit::At`
|
130
|
+
|
131
|
+
Points in time are parsed and given back as EtOrbi::EoTime instances.
|
132
|
+
|
133
|
+
```ruby
|
134
|
+
Fugit::At.parse('2017-12-12').to_s
|
135
|
+
# ==> "2017-12-12 00:00:00 +0900" (at least here in Hiroshima)
|
136
|
+
|
137
|
+
Fugit::At.parse('2017-12-12 12:00:00 America/New_York').to_s
|
138
|
+
# ==> "2017-12-12 12:00:00 -0500"
|
139
|
+
```
|
140
|
+
|
141
|
+
Directly with `Fugit.parse_at(s)` is OK too:
|
142
|
+
```ruby
|
143
|
+
Fugit.parse_at('2017-12-12 12:00:00 America/New_York').to_s
|
144
|
+
# ==> "2017-12-12 12:00:00 -0500"
|
145
|
+
```
|
146
|
+
|
147
|
+
Directly with `Fugit.parse(s)` is OK too:
|
148
|
+
```ruby
|
149
|
+
Fugit.parse('2017-12-12 12:00:00 America/New_York').to_s
|
150
|
+
# ==> "2017-12-12 12:00:00 -0500"
|
151
|
+
```
|
152
|
+
|
153
|
+
## `Fugit::Nat`
|
154
|
+
|
155
|
+
Fugit understand some kind of "natural" language:
|
156
|
+
|
157
|
+
For example, those "every" get turned into `Fugit::Cron` instances:
|
158
|
+
```ruby
|
159
|
+
Fugit::Nat.parse('every day at five') # ==> '0 5 * * *'
|
160
|
+
Fugit::Nat.parse('every weekday at five') # ==> '0 5 * * 1,2,3,4,5'
|
161
|
+
Fugit::Nat.parse('every day at 5 pm') # ==> '0 17 * * *'
|
162
|
+
Fugit::Nat.parse('every tuesday at 5 pm') # ==> '0 17 * * 2'
|
163
|
+
Fugit::Nat.parse('every wed at 5 pm') # ==> '0 17 * * 3'
|
164
|
+
Fugit::Nat.parse('every day at 16:30') # ==> '30 16 * * *'
|
165
|
+
Fugit::Nat.parse('every day at noon') # ==> '0 12 * * *'
|
166
|
+
Fugit::Nat.parse('every day at midnight') # ==> '0 0 * * *'
|
167
|
+
Fugit::Nat.parse('every tuesday and monday at 5pm') # ==> '0 17 * * 1,2'
|
168
|
+
Fugit::Nat.parse('every wed or Monday at 5pm and 11') # ==> '0 11,17 * * 1,3'
|
169
|
+
```
|
170
|
+
|
171
|
+
Directly with `Fugit.parse(s)` is OK too:
|
172
|
+
```ruby
|
173
|
+
Fugit.parse('every day at five') # ==> Fugit::Cron instance '0 5 * * *'
|
174
|
+
```
|
175
|
+
|
111
176
|
|
112
177
|
## LICENSE
|
113
178
|
|
data/fugit.gemspec
CHANGED
@@ -20,14 +20,16 @@ Time tools for flor and the floraison project. Cron parsing and occurence comput
|
|
20
20
|
|
21
21
|
#s.files = `git ls-files`.split("\n")
|
22
22
|
s.files = Dir[
|
23
|
+
'README.{md,txt}',
|
24
|
+
'CHANGELOG.{md,txt}', 'CREDITS.{md,txt}', 'LICENSE.{md,txt}',
|
23
25
|
'Makefile',
|
24
26
|
'lib/**/*.rb', #'spec/**/*.rb', 'test/**/*.rb',
|
25
|
-
|
27
|
+
"#{s.name}.gemspec",
|
26
28
|
]
|
27
29
|
|
28
30
|
#s.add_runtime_dependency 'tzinfo'
|
29
31
|
s.add_runtime_dependency 'raabro', '~> 1.1'
|
30
|
-
s.add_runtime_dependency 'et-orbi', '>= 1.0
|
32
|
+
s.add_runtime_dependency 'et-orbi', '>= 1.1.0'
|
31
33
|
|
32
34
|
s.add_development_dependency 'rspec', '~> 3.4'
|
33
35
|
|
data/lib/fugit.rb
CHANGED
data/lib/fugit/at.rb
ADDED
data/lib/fugit/cron.rb
CHANGED
@@ -14,54 +14,60 @@ module Fugit
|
|
14
14
|
'@hourly' => '0 * * * *',
|
15
15
|
}
|
16
16
|
|
17
|
-
attr_reader :original
|
17
|
+
attr_reader :original, :zone
|
18
|
+
attr_reader :minutes, :hours, :monthdays, :months, :weekdays, :timezone
|
18
19
|
|
19
|
-
|
20
|
+
class << self
|
20
21
|
|
21
|
-
|
22
|
+
def new(original)
|
22
23
|
|
23
|
-
|
24
|
-
|
24
|
+
parse(original)
|
25
|
+
end
|
25
26
|
|
26
|
-
|
27
|
+
def parse(s)
|
27
28
|
|
28
|
-
|
29
|
-
[
|
30
|
-
@seconds == [ 0 ] ? nil : (@seconds || [ '*' ]).join(','),
|
31
|
-
(@minutes || [ '*' ]).join(','),
|
32
|
-
(@hours || [ '*' ]).join(','),
|
33
|
-
(@monthdays || [ '*' ]).join(','),
|
34
|
-
(@months || [ '*' ]).join(','),
|
35
|
-
(@weekdays || [ [ '*' ] ]).map { |d| d.compact.join('#') }.join(',')
|
36
|
-
].compact.join(' ')
|
37
|
-
end
|
29
|
+
return s if s.is_a?(self)
|
38
30
|
|
39
|
-
|
31
|
+
original = s
|
32
|
+
s = SPECIALS[s] || s
|
40
33
|
|
41
|
-
|
34
|
+
return nil unless s.is_a?(String)
|
42
35
|
|
43
|
-
|
44
|
-
|
36
|
+
#p s; Raabro.pp(Parser.parse(s, debug: 3), colors: true)
|
37
|
+
h = Parser.parse(s)
|
45
38
|
|
46
|
-
|
39
|
+
return nil unless h
|
47
40
|
|
48
|
-
|
49
|
-
|
41
|
+
self.allocate.send(:init, s, h)
|
42
|
+
end
|
50
43
|
|
51
|
-
|
44
|
+
def do_parse(s)
|
52
45
|
|
53
|
-
|
46
|
+
parse(s) ||
|
47
|
+
fail(ArgumentError.new("not a cron string #{s.inspect}"))
|
48
|
+
end
|
54
49
|
end
|
55
50
|
|
56
|
-
def
|
51
|
+
def to_cron_s
|
57
52
|
|
58
|
-
|
53
|
+
@cron_s ||= begin
|
54
|
+
[
|
55
|
+
@seconds == [ 0 ] ? nil : (@seconds || [ '*' ]).join(','),
|
56
|
+
(@minutes || [ '*' ]).join(','),
|
57
|
+
(@hours || [ '*' ]).join(','),
|
58
|
+
(@monthdays || [ '*' ]).join(','),
|
59
|
+
(@months || [ '*' ]).join(','),
|
60
|
+
(@weekdays || [ [ '*' ] ]).map { |d| d.compact.join('#') }.join(','),
|
61
|
+
@timezone ? @timezone.to_s : nil
|
62
|
+
].compact.join(' ')
|
63
|
+
end
|
59
64
|
end
|
60
65
|
|
61
66
|
class TimeCursor
|
62
67
|
|
63
68
|
def initialize(t)
|
64
|
-
@t = t.is_a?(TimeCursor) ? t.time :
|
69
|
+
@t = t.is_a?(TimeCursor) ? t.time : t
|
70
|
+
@t.seconds = @t.seconds.to_i
|
65
71
|
end
|
66
72
|
|
67
73
|
def time; @t; end
|
@@ -163,7 +169,8 @@ module Fugit
|
|
163
169
|
|
164
170
|
def next_time(from=::EtOrbi::EoTime.now)
|
165
171
|
|
166
|
-
|
172
|
+
from = ::EtOrbi.make_time(from)
|
173
|
+
t = TimeCursor.new(from.translate(@timezone))
|
167
174
|
|
168
175
|
loop do
|
169
176
|
#p [ :l, Fugit.time_to_s(t.time) ]
|
@@ -176,12 +183,13 @@ module Fugit
|
|
176
183
|
break
|
177
184
|
end
|
178
185
|
|
179
|
-
t.time
|
186
|
+
t.time.translate(from.zone)
|
180
187
|
end
|
181
188
|
|
182
189
|
def previous_time(from=::EtOrbi::EoTime.now)
|
183
190
|
|
184
|
-
|
191
|
+
from = ::EtOrbi.make_time(from)
|
192
|
+
t = TimeCursor.new(from.translate(@timezone))
|
185
193
|
|
186
194
|
loop do
|
187
195
|
#p [ :l, Fugit.time_to_s(t.time) ]
|
@@ -194,13 +202,14 @@ module Fugit
|
|
194
202
|
break
|
195
203
|
end
|
196
204
|
|
197
|
-
t.time
|
205
|
+
t.time.translate(from.zone)
|
198
206
|
end
|
199
207
|
|
200
208
|
# Mostly used as a #next_time sanity check.
|
201
209
|
# Avoid for "business" use, it's slow.
|
202
210
|
#
|
203
|
-
# 2017 is non leap year (though it is preceded by
|
211
|
+
# 2017 is a non leap year (though it is preceded by
|
212
|
+
# a leap second on 2016-12-31)
|
204
213
|
#
|
205
214
|
# Nota bene: cron with seconds are not supported.
|
206
215
|
#
|
@@ -223,16 +232,38 @@ module Fugit
|
|
223
232
|
t = t1
|
224
233
|
end
|
225
234
|
|
226
|
-
|
227
|
-
span = t1 - t0
|
228
|
-
span_years = span / (365 * 24 * 3600)
|
229
|
-
yearly_occurences = occurences.to_f / span_years
|
230
|
-
|
231
|
-
[ deltas.min, deltas.max, occurences,
|
232
|
-
span.to_i, span_years.to_i, yearly_occurences.to_i ]
|
235
|
+
Frequency.new(deltas, t1 - t0)
|
233
236
|
end
|
234
237
|
end
|
235
238
|
|
239
|
+
class Frequency
|
240
|
+
|
241
|
+
attr_reader :span, :delta_min, :delta_max, :occurrences
|
242
|
+
attr_reader :span_years, :yearly_occurrences
|
243
|
+
|
244
|
+
def initialize(deltas, span)
|
245
|
+
|
246
|
+
@span = span
|
247
|
+
|
248
|
+
@delta_min = deltas.min; @delta_max = deltas.max
|
249
|
+
@occurrences = deltas.size
|
250
|
+
@span_years = span / (365 * 24 * 3600)
|
251
|
+
@yearly_occurrences = @occurrences.to_f / @span_years
|
252
|
+
end
|
253
|
+
|
254
|
+
def to_debug_s
|
255
|
+
|
256
|
+
{
|
257
|
+
dmin: Fugit::Duration.new(delta_min).deflate.to_plain_s,
|
258
|
+
dmax: Fugit::Duration.new(delta_max).deflate.to_plain_s,
|
259
|
+
ocs: occurrences,
|
260
|
+
spn: Fugit::Duration.new(span.to_i).deflate.to_plain_s,
|
261
|
+
spnys: span_years.to_i,
|
262
|
+
yocs: yearly_occurrences.to_i
|
263
|
+
}.collect { |k, v| "#{k}: #{v}" }.join(', ')
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
236
267
|
def to_a
|
237
268
|
|
238
269
|
[ @seconds, @minutes, @hours, @monthdays, @months, @weekdays ]
|
@@ -264,6 +295,7 @@ module Fugit
|
|
264
295
|
determine_monthdays(h[:dom])
|
265
296
|
determine_months(h[:mon])
|
266
297
|
determine_weekdays(h[:dow])
|
298
|
+
determine_timezone(h[:tz])
|
267
299
|
|
268
300
|
self
|
269
301
|
end
|
@@ -345,6 +377,11 @@ module Fugit
|
|
345
377
|
@weekdays = nil if @weekdays.empty?
|
346
378
|
end
|
347
379
|
|
380
|
+
def determine_timezone(z)
|
381
|
+
|
382
|
+
@zone, @timezone = z
|
383
|
+
end
|
384
|
+
|
348
385
|
module Parser include Raabro
|
349
386
|
|
350
387
|
WEEKDAYS = %w[ sunday monday tuesday wednesday thursday friday saturday ]
|
@@ -420,11 +457,19 @@ module Fugit
|
|
420
457
|
def lmon_(i); seq(nil, i, :list_mon, :s); end
|
421
458
|
alias ldow list_dow
|
422
459
|
|
460
|
+
def _tz_name(i)
|
461
|
+
rex(nil, i, / +[A-Z][a-zA-Z0-9]+(\/[A-Z][a-zA-Z0-9_]+){0,2}/)
|
462
|
+
end
|
463
|
+
def _tz_delta(i)
|
464
|
+
rex(nil, i, / +[-+]([01][0-9]|2[0-4]):?(00|15|30|45)/)
|
465
|
+
end
|
466
|
+
def _tz(i); alt(:tz, i, :_tz_delta, :_tz_name); end
|
467
|
+
|
423
468
|
def classic_cron(i)
|
424
|
-
seq(:ccron, i, :lmin_, :lhou_, :ldom_, :lmon_, :ldow)
|
469
|
+
seq(:ccron, i, :lmin_, :lhou_, :ldom_, :lmon_, :ldow, :_tz, '?')
|
425
470
|
end
|
426
471
|
def second_cron(i)
|
427
|
-
seq(:scron, i, :lsec_, :lmin_, :lhou_, :ldom_, :lmon_, :ldow)
|
472
|
+
seq(:scron, i, :lsec_, :lmin_, :lhou_, :ldom_, :lmon_, :ldow, :_tz, '?')
|
428
473
|
end
|
429
474
|
|
430
475
|
def cron(i)
|
@@ -469,12 +514,26 @@ module Fugit
|
|
469
514
|
.collect { |et| rewrite_elt(t.name, et) }
|
470
515
|
end
|
471
516
|
|
517
|
+
def rewrite_tz(t)
|
518
|
+
|
519
|
+
s = t.string.strip
|
520
|
+
z = EtOrbi.get_tzone(s)
|
521
|
+
|
522
|
+
[ s, z ]
|
523
|
+
end
|
524
|
+
|
472
525
|
def rewrite_cron(t)
|
473
526
|
|
474
|
-
t
|
527
|
+
hcron = t
|
475
528
|
.sublookup(nil) # go to :ccron or :scron
|
476
529
|
.subgather(nil) # list min, hou, mon, ...
|
477
|
-
.inject({}) { |h, tt|
|
530
|
+
.inject({}) { |h, tt|
|
531
|
+
h[tt.name] = tt.name == :tz ? rewrite_tz(tt) : rewrite_entry(tt)
|
532
|
+
h }
|
533
|
+
|
534
|
+
z, tz = hcron[:tz]; return nil if z && ! tz
|
535
|
+
|
536
|
+
hcron
|
478
537
|
end
|
479
538
|
end
|
480
539
|
end
|
data/lib/fugit/duration.rb
CHANGED
@@ -3,7 +3,7 @@ module Fugit
|
|
3
3
|
|
4
4
|
class Duration
|
5
5
|
|
6
|
-
attr_reader :original, :h
|
6
|
+
attr_reader :original, :h, :options
|
7
7
|
|
8
8
|
def self.new(s)
|
9
9
|
|
@@ -16,13 +16,12 @@ module Fugit
|
|
16
16
|
|
17
17
|
original = s
|
18
18
|
|
19
|
-
s = s
|
19
|
+
s = "#{s}s" if s.is_a?(Numeric)
|
20
20
|
|
21
21
|
return nil unless s.is_a?(String)
|
22
22
|
|
23
23
|
s = s.strip
|
24
|
-
|
25
|
-
#p [ original, s ]; Raabro.pp(Parser.parse(s, debug: 3))
|
24
|
+
#p [ original, s ]; Raabro.pp(Parser.parse(s, debug: 3), colours: true)
|
26
25
|
|
27
26
|
h =
|
28
27
|
if opts[:iso]
|
@@ -32,8 +31,9 @@ module Fugit
|
|
32
31
|
else
|
33
32
|
Parser.parse(s) || IsoParser.parse(opts[:stricter] ? s : s.upcase)
|
34
33
|
end
|
34
|
+
#p h
|
35
35
|
|
36
|
-
h ? self.allocate.send(:init, original, h) : nil
|
36
|
+
h ? self.allocate.send(:init, original, opts, h) : nil
|
37
37
|
end
|
38
38
|
|
39
39
|
def self.do_parse(s, opts={})
|
@@ -42,23 +42,30 @@ module Fugit
|
|
42
42
|
end
|
43
43
|
|
44
44
|
KEYS = {
|
45
|
-
yea: { a: 'Y', i: 'Y', s: 365 * 24 * 3600, x: 0, l: 'year' },
|
46
|
-
mon: { a: 'M', i: 'M', s: 30 * 24 * 3600, x: 1, l: 'month' },
|
47
|
-
wee: { a: 'W', i: 'W', s: 7 * 24 * 3600, I: true, l: 'week' },
|
48
|
-
day: { a: 'D', i: 'D', s: 24 * 3600, I: true, l: 'day' },
|
49
|
-
hou: { a: 'h', i: 'H', s: 3600, I: true, l: 'hour' },
|
50
|
-
min: { a: 'm', i: 'M', s: 60, I: true, l: 'minute' },
|
51
|
-
sec: { a: 's', i: 'S', s: 1, I: true, l: 'second' },
|
45
|
+
yea: { a: 'Y', r: 'y', i: 'Y', s: 365 * 24 * 3600, x: 0, l: 'year' },
|
46
|
+
mon: { a: 'M', r: 'M', i: 'M', s: 30 * 24 * 3600, x: 1, l: 'month' },
|
47
|
+
wee: { a: 'W', r: 'w', i: 'W', s: 7 * 24 * 3600, I: true, l: 'week' },
|
48
|
+
day: { a: 'D', r: 'd', i: 'D', s: 24 * 3600, I: true, l: 'day' },
|
49
|
+
hou: { a: 'h', r: 'h', i: 'H', s: 3600, I: true, l: 'hour' },
|
50
|
+
min: { a: 'm', r: 'm', i: 'M', s: 60, I: true, l: 'minute' },
|
51
|
+
sec: { a: 's', r: 's', i: 'S', s: 1, I: true, l: 'second' },
|
52
52
|
}
|
53
53
|
INFLA_KEYS, NON_INFLA_KEYS =
|
54
54
|
KEYS.partition { |k, v| v[:I] }
|
55
55
|
|
56
|
-
def
|
56
|
+
def _to_s(key)
|
57
57
|
|
58
|
-
KEYS.inject(StringIO.new) { |s, (k, a)|
|
59
|
-
v = @h[k]
|
60
|
-
|
61
|
-
|
58
|
+
KEYS.inject([ StringIO.new, '+' ]) { |(s, sign), (k, a)|
|
59
|
+
v = @h[k]
|
60
|
+
next [ s, sign ] unless v
|
61
|
+
sign1 = v < 0 ? '-' : '+'
|
62
|
+
s << (sign1 != sign ? sign1 : '') << v.abs.to_s << a[key]
|
63
|
+
[ s, sign1 ]
|
64
|
+
}[0].string
|
65
|
+
end; protected :_to_s
|
66
|
+
|
67
|
+
def to_plain_s; _to_s(:a); end
|
68
|
+
def to_rufus_s; _to_s(:r); end
|
62
69
|
|
63
70
|
def to_iso_s
|
64
71
|
|
@@ -102,6 +109,15 @@ module Fugit
|
|
102
109
|
def to_long_s(o, opts={}); do_parse(o).deflate.to_long_s(opts); end
|
103
110
|
end
|
104
111
|
|
112
|
+
# For now, let's alias to #h
|
113
|
+
#
|
114
|
+
def to_h; h; end
|
115
|
+
|
116
|
+
def to_rufus_h
|
117
|
+
|
118
|
+
KEYS.inject({}) { |h, (ks, kh)| v = @h[ks]; h[kh[:r].to_sym] = v if v; h }
|
119
|
+
end
|
120
|
+
|
105
121
|
# Warning: this is an "approximation", months are 30 days and years are
|
106
122
|
# 365 days, ...
|
107
123
|
#
|
@@ -123,32 +139,54 @@ module Fugit
|
|
123
139
|
h
|
124
140
|
}
|
125
141
|
|
126
|
-
self.class.allocate.init(@original, h)
|
142
|
+
self.class.allocate.init(@original, {}, h)
|
127
143
|
end
|
128
144
|
|
129
|
-
|
145
|
+
# Round float seconds to 9 decimals when deflating
|
146
|
+
#
|
147
|
+
SECOND_ROUND = 9
|
148
|
+
|
149
|
+
def deflate(options={})
|
130
150
|
|
131
151
|
id = inflate
|
132
152
|
h = id.h.dup
|
133
153
|
s = h.delete(:sec) || 0
|
134
154
|
|
135
|
-
|
155
|
+
keys = INFLA_KEYS
|
136
156
|
|
137
|
-
|
138
|
-
|
157
|
+
mon = options[:month]
|
158
|
+
yea = options[:year]
|
159
|
+
keys = keys.dup if mon || yea
|
139
160
|
|
140
|
-
|
141
|
-
|
161
|
+
if mon
|
162
|
+
mon = 30 if mon == true
|
163
|
+
mon = "#{mon}d" if mon.is_a?(Integer)
|
164
|
+
keys.unshift([ :mon, { s: Fugit::Duration.parse(mon).to_sec } ])
|
165
|
+
end
|
166
|
+
if yea
|
167
|
+
yea = 365 if yea == true
|
168
|
+
yea = "#{yea}d" if yea.is_a?(Integer)
|
169
|
+
keys.unshift([ :yea, { s: Fugit::Duration.parse(yea).to_sec } ])
|
170
|
+
end
|
171
|
+
|
172
|
+
keys[0..-2].each do |k, v|
|
173
|
+
|
174
|
+
vs = v[:s]; next if s < vs
|
175
|
+
|
176
|
+
h[k] = (h[k] || 0) + s.to_i / vs
|
177
|
+
s = s % vs
|
142
178
|
end
|
143
179
|
|
144
|
-
|
180
|
+
h[:sec] = s.is_a?(Integer) ? s : s.round(SECOND_ROUND)
|
181
|
+
|
182
|
+
self.class.allocate.init(@original, {}, h)
|
145
183
|
end
|
146
184
|
|
147
185
|
def opposite
|
148
186
|
|
149
187
|
h = @h.inject({}) { |h, (k, v)| h[k] = -v; h }
|
150
188
|
|
151
|
-
self.class.allocate.init(nil, h)
|
189
|
+
self.class.allocate.init(nil, {}, h)
|
152
190
|
end
|
153
191
|
|
154
192
|
alias -@ opposite
|
@@ -158,14 +196,14 @@ module Fugit
|
|
158
196
|
h = @h.dup
|
159
197
|
h[:sec] = (h[:sec] || 0) + n.to_i
|
160
198
|
|
161
|
-
self.class.allocate.init(nil, h)
|
199
|
+
self.class.allocate.init(nil,{}, h)
|
162
200
|
end
|
163
201
|
|
164
202
|
def add_duration(d)
|
165
203
|
|
166
204
|
h = d.h.inject(@h.dup) { |h, (k, v)| h[k] = (h[k] || 0) + v; h }
|
167
205
|
|
168
|
-
self.class.allocate.init(nil, h)
|
206
|
+
self.class.allocate.init(nil, {}, h)
|
169
207
|
end
|
170
208
|
|
171
209
|
def add_to_time(t)
|
@@ -213,7 +251,7 @@ module Fugit
|
|
213
251
|
end
|
214
252
|
alias + add
|
215
253
|
|
216
|
-
def
|
254
|
+
def subtract(a)
|
217
255
|
|
218
256
|
case a
|
219
257
|
when Numeric then add_numeric(-a)
|
@@ -221,10 +259,10 @@ module Fugit
|
|
221
259
|
when String then add_duration(-self.class.parse(a))
|
222
260
|
when ::Time, ::EtOrbi::EoTime then add_to_time(a)
|
223
261
|
else fail ArgumentError.new(
|
224
|
-
"cannot
|
262
|
+
"cannot subtract #{a.class} instance to a Fugit::Duration")
|
225
263
|
end
|
226
264
|
end
|
227
|
-
alias -
|
265
|
+
alias - subtract
|
228
266
|
|
229
267
|
def ==(o)
|
230
268
|
|
@@ -242,14 +280,30 @@ module Fugit
|
|
242
280
|
add(from)
|
243
281
|
end
|
244
282
|
|
283
|
+
# Returns a copy of this duration, omitting its seconds.
|
284
|
+
#
|
285
|
+
def drop_seconds
|
286
|
+
|
287
|
+
h = @h.dup
|
288
|
+
h.delete(:sec)
|
289
|
+
h[:min] = 0 if h.empty?
|
290
|
+
|
291
|
+
self.class.allocate.init(nil, { literal: true }, h)
|
292
|
+
end
|
293
|
+
|
245
294
|
protected
|
246
295
|
|
247
|
-
def init(original, h)
|
296
|
+
def init(original, options, h)
|
248
297
|
|
249
298
|
@original = original
|
299
|
+
@options = options
|
250
300
|
|
251
|
-
|
252
|
-
|
301
|
+
if options[:literal]
|
302
|
+
@h = h
|
303
|
+
else
|
304
|
+
@h = h.reject { |k, v| v == 0 }
|
305
|
+
@h[:sec] = 0 if @h.empty?
|
306
|
+
end
|
253
307
|
|
254
308
|
self
|
255
309
|
end
|
@@ -272,23 +326,47 @@ module Fugit
|
|
272
326
|
|
273
327
|
def sep(i); rex(nil, i, /([ \t,]+|and)*/i); end
|
274
328
|
|
275
|
-
def yea(i); rex(:yea, i,
|
276
|
-
def mon(i); rex(:mon, i,
|
277
|
-
def wee(i); rex(:wee, i,
|
278
|
-
def day(i); rex(:day, i,
|
279
|
-
def hou(i); rex(:hou, i,
|
280
|
-
def min(i); rex(:min, i,
|
329
|
+
def yea(i); rex(:yea, i, /(\d+\.\d*|(\d*\.)?\d+) *y(ears?)?/i); end
|
330
|
+
def mon(i); rex(:mon, i, /(\d+\.\d*|(\d*\.)?\d+) *(M|months?)/); end
|
331
|
+
def wee(i); rex(:wee, i, /(\d+\.\d*|(\d*\.)?\d+) *(weeks?|w)/i); end
|
332
|
+
def day(i); rex(:day, i, /(\d+\.\d*|(\d*\.)?\d+) *(days?|d)/i); end
|
333
|
+
def hou(i); rex(:hou, i, /(\d+\.\d*|(\d*\.)?\d+) *(hours?|h)/i); end
|
334
|
+
def min(i); rex(:min, i, /(\d+\.\d*|(\d*\.)?\d+) *(mins?|minutes?|m)/); end
|
335
|
+
|
336
|
+
def sec(i); rex(:sec, i, /(\d+\.\d*|(\d*\.)?\d+) *(secs?|seconds?|s)/i); end
|
337
|
+
def sek(i); rex(:sec, i, /(\d+\.\d*|\.\d+|\d+)$/); end
|
281
338
|
|
282
|
-
def
|
283
|
-
|
339
|
+
def elt(i); alt(nil, i, :yea, :mon, :wee, :day, :hou, :min, :sec, :sek); end
|
340
|
+
def sign(i); rex(:sign, i, /[-+]?/); end
|
284
341
|
|
285
|
-
def
|
342
|
+
def sdur(i); seq(:sdur, i, :sign, '?', :elt, '+'); end
|
286
343
|
|
287
|
-
def dur(i); jseq(:dur, i, :
|
344
|
+
def dur(i); jseq(:dur, i, :sdur, :sep); end
|
288
345
|
|
289
346
|
# rewrite parsed tree
|
290
347
|
|
291
|
-
def
|
348
|
+
def merge(h0, h1)
|
349
|
+
|
350
|
+
sign = h1.delete(:sign) || 1
|
351
|
+
|
352
|
+
h1.inject(h0) { |h, (k, v)| h.merge(k => (h[k] || 0) + sign * v) }
|
353
|
+
end
|
354
|
+
|
355
|
+
def rewrite_sdur(t)
|
356
|
+
|
357
|
+
h = Fugit::Duration.common_rewrite_dur(t)
|
358
|
+
|
359
|
+
sign = t.sublookup(:sign)
|
360
|
+
sign = (sign && sign.string == '-') ? -1 : 1
|
361
|
+
|
362
|
+
h.merge(sign: sign)
|
363
|
+
end
|
364
|
+
|
365
|
+
def rewrite_dur(t)
|
366
|
+
|
367
|
+
#Raabro.pp(t, colours: true)
|
368
|
+
t.children.inject({}) { |h, ct| merge(h, ct.name ? rewrite(ct) : {}) }
|
369
|
+
end
|
292
370
|
end
|
293
371
|
|
294
372
|
module IsoParser include Raabro
|
data/lib/fugit/parse.rb
CHANGED
@@ -1,25 +1,17 @@
|
|
1
1
|
|
2
2
|
module Fugit
|
3
3
|
|
4
|
-
def self.parse_at(s)
|
5
|
-
|
6
|
-
::EtOrbi.make_time(s) rescue nil
|
7
|
-
end
|
8
|
-
|
9
|
-
def self.do_parse_at(s)
|
10
|
-
|
11
|
-
::EtOrbi.make_time(s)
|
12
|
-
end
|
13
|
-
|
14
4
|
def self.parse_cron(s); ::Fugit::Cron.parse(s); end
|
15
5
|
def self.parse_duration(s); ::Fugit::Duration.parse(s); end
|
16
|
-
def self.parse_in(s); parse_duration(s); end
|
17
6
|
def self.parse_nat(s); ::Fugit::Nat.parse(s); end
|
7
|
+
def self.parse_at(s); ::Fugit::At.parse(s); end
|
8
|
+
def self.parse_in(s); parse_duration(s); end
|
18
9
|
|
19
10
|
def self.do_parse_cron(s); ::Fugit::Cron.do_parse(s); end
|
20
11
|
def self.do_parse_duration(s); ::Fugit::Duration.do_parse(s); end
|
21
|
-
def self.do_parse_in(s); do_parse_duration(s); end
|
22
12
|
def self.do_parse_nat(s); ::Fugit::Nat.do_parse(s); end
|
13
|
+
def self.do_parse_at(s); ::Fugit::At.do_parse(s); end
|
14
|
+
def self.do_parse_in(s); do_parse_duration(s); end
|
23
15
|
|
24
16
|
def self.parse(s, opts={})
|
25
17
|
|
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.1.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: 2018-03-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: raabro
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 1.0
|
33
|
+
version: 1.1.0
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 1.0
|
40
|
+
version: 1.1.0
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rspec
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -67,6 +67,7 @@ files:
|
|
67
67
|
- README.md
|
68
68
|
- fugit.gemspec
|
69
69
|
- lib/fugit.rb
|
70
|
+
- lib/fugit/at.rb
|
70
71
|
- lib/fugit/cron.rb
|
71
72
|
- lib/fugit/duration.rb
|
72
73
|
- lib/fugit/misc.rb
|
@@ -92,7 +93,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
92
93
|
version: '0'
|
93
94
|
requirements: []
|
94
95
|
rubyforge_project:
|
95
|
-
rubygems_version: 2.
|
96
|
+
rubygems_version: 2.6.13
|
96
97
|
signing_key:
|
97
98
|
specification_version: 4
|
98
99
|
summary: time tools for flor
|