tempr 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,14 @@
1
+ # Changelog
2
+
3
+ ## 0.1.1 / 2012-02-18
4
+
5
+ - add `SubRangeIterator#cover?` shortcut
6
+ - add `between_times`
7
+ - `on_day` renamed `each_day_of_month`; deprecation warning
8
+ - `at_time` renamed `each_time_of_day`; deprecation warning
9
+ - add utc_offset param to `each_time_of_day`, `time_range`
10
+ - fix error where time subranges forced to local time
11
+
12
+ ## 0.1.0 / 2012-02-16
13
+
14
+ - Initial gem release
@@ -2,4 +2,15 @@
2
2
  ## a Ruby temporal expressions library
3
3
 
4
4
 
5
- This is a work in progress. See date_time_range.rb for usage examples.
5
+ This is a work in progress. See date_time_range.rb for usage examples.
6
+
7
+
8
+ ### License:
9
+
10
+ Copyright (c) 2012 Eric Gjertsen
11
+
12
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
13
+
14
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -103,9 +103,9 @@ module Tempr
103
103
  build_subrange do |s|
104
104
  s.step = n
105
105
  s.adjust_range { |r| time_range(r) }
106
- s.offset { |tm| tm.to_time + offset }
107
- s.increment { |tm,i| tm.to_time + i }
108
- s.span { |tm| tm.to_time + dur }
106
+ s.offset { |tm| tm + offset }
107
+ s.increment { |tm,i| tm + i }
108
+ s.span { |tm| tm + dur }
109
109
  end
110
110
  end
111
111
  alias each_second each_seconds
@@ -353,7 +353,7 @@ module Tempr
353
353
  #
354
354
  # if no +dur+ parameter passed,
355
355
  # "every +nday+th day of the month, grouped into one-day intervals"
356
- def on_day(nday,dur=1)
356
+ def each_day_of_month(nday,dur=1)
357
357
  build_subrange do |s|
358
358
  s.step = 1
359
359
  s.adjust_range { |r| day_range(r) }
@@ -366,28 +366,61 @@ module Tempr
366
366
  end
367
367
  end
368
368
 
369
+ def on_day(nday,dur=1)
370
+ warn "Note #on_day is deprecated and will be removed in 0.2.0; use #each_day_of_month instead"
371
+ each_day_of_month(nday,dur)
372
+ end
373
+
369
374
  # time-of-day iterator:
370
- # "every day at +tm+, grouped into +dur+ second intervals"
375
+ # "every day at +tm+, grouped into +dur+ second intervals, in +utc_offset+"
371
376
  #
372
- # +tm+ is any string that can be Time.parse'd. Note the date portion is ignored, if given.
377
+ # +tm+ is any string that can be Time.parse'd - relative to the local time and zone.
378
+ # (Note the date portion is ignored, if given.)
373
379
  #
374
- # if no +dur+ parameter passed, intervals are 'instantaneous' time ranges
375
- def at_time(tm,dur=0)
380
+ # if no +dur+ passed, intervals are 'instantaneous' time ranges
381
+ # if no +utc_offset+ passed, process-local timezone is used.
382
+ def each_time_of_day(tm,dur=0,utc_offset=nil)
376
383
  tm_p = Time.parse(tm)
377
384
  build_subrange do |s|
378
385
  s.step = 60*60*24
379
- s.adjust_range { |r| time_range(r) }
380
- s.offset do |tm|
386
+ s.adjust_range { |r| time_range(r,utc_offset) }
387
+ s.offset do |t|
381
388
  Time.new(
382
- tm.year, tm.month, tm.day,
383
- tm_p.hour, tm_p.min, tm_p.sec, (tm + s.step).utc_offset
389
+ t.year, t.month, t.day,
390
+ tm_p.hour, tm_p.min, tm_p.sec,
391
+ (t + s.step).utc_offset
384
392
  )
385
393
  end
386
- s.increment { |tm,i| tm.to_time + i }
387
- s.span { |tm| tm.to_time + dur }
394
+ s.increment { |t,i| t + i }
395
+ s.span { |t| t + dur }
388
396
  end
389
397
  end
390
-
398
+
399
+ def at_time(tm,dur=0,utc_offset=nil)
400
+ warn "Note #at_time is deprecated and will be removed in 0.2.0; use #each_time_of_day instead"
401
+ each_time_of_day(tm,dur,utc_offset)
402
+ end
403
+
404
+ # time-of-day iterator specifying time range:
405
+ # "every day between +tm0+ and +tm1+, in +utc_offset+"
406
+ #
407
+ # parameters are any string that can be Time.parse'd - relative to the local time and zone.
408
+ # (Note the date portion is ignored, if given.)
409
+ #
410
+ # if +tm1+ < +tm0+, ranges are interpreted to go into the next day, e.g.
411
+ # `between_times "23:00", "01:00"`
412
+ def between_times(tm0,tm1,utc_offset=nil)
413
+ tm0_p = Time.parse(tm0)
414
+ tm1_p = Time.parse(tm1)
415
+ dur = if tm0_p <= tm1_p
416
+ tm1_p - tm0_p
417
+ else
418
+ dur = 60*60*24 - (tm0_p - tm1_p)
419
+ end
420
+ each_time_of_day(tm0, dur, utc_offset)
421
+ end
422
+
423
+
391
424
  # ---
392
425
 
393
426
  # Helper methods - these are a bit hacky and possibly buggy.
@@ -415,17 +448,28 @@ module Tempr
415
448
 
416
449
  # unless already a time range,
417
450
  # convert to exclusive date range, and then to time range
451
+ #
452
+ # Times are local time unless +utc_offset+ is passed
453
+ #
418
454
  # For example,
419
455
  #
420
- # `2012-02-01..2012-02-29` becomes
421
- # `2012-02-01 00:00:00 UTC...2012-03-01 00:00:00 UTC`
456
+ # `time_range(2012-02-01..2012-02-29,'00:00')`
457
+ # #=> `2012-02-01 00:00:00 +00:00...2012-03-01 00:00:00 +00:00`
422
458
  #
423
- def time_range(rng=self)
459
+ def time_range(rng=self,utc_offset=nil)
424
460
  if rng.begin.respond_to?(:sec) && rng.end.respond_to?(:sec)
425
- rng.dup
461
+ utc_offset ||= rng.begin.utc_offset
462
+ Range.new( rng.begin.getlocal(utc_offset),
463
+ rng.end.getlocal(utc_offset),
464
+ rng.respond_to?(:exclude_end?) && rng.exclude_end?
465
+ )
426
466
  else
427
467
  adj_rng = day_range(rng)
428
- Range.new(adj_rng.begin.to_time, adj_rng.end.to_time, true)
468
+ b,e = adj_rng.begin, adj_rng.end
469
+ Range.new( Time.new(b.year,b.month,b.day,0,0,0,utc_offset),
470
+ Time.new(e.year,e.month,e.day,0,0,0,utc_offset),
471
+ true
472
+ )
429
473
  end
430
474
  end
431
475
 
@@ -511,6 +555,10 @@ module Tempr
511
555
  self
512
556
  end
513
557
 
558
+ def cover?(dt)
559
+ any? {|r| r.cover?(dt)}
560
+ end
561
+
514
562
  # Recursive madness...
515
563
  # note this could possibly use cached results stored by #all method,
516
564
  # similar to Sequel
@@ -593,8 +641,18 @@ if $0 == __FILE__
593
641
  require 'pp'
594
642
 
595
643
  range = (Date.civil(2012,1,1)...Date.civil(2013,1,1)).extend(Tempr::DateTimeRange)
596
- subrange = range.each_month.thursday(2).at_time('2:00pm',60*60)
644
+ subrange = range.each_month.thursday(2).each_time_of_day('2:00pm',60*60)
597
645
 
646
+ puts "base range time_range"
647
+ pp range.time_range(range,"+00:00")
648
+
598
649
  pp subrange.to_a
650
+ puts
651
+
652
+ subrange2 = range.each_month.thursday(2).between_times('2:00pm','3:00pm')
653
+
654
+ puts "between_times"
655
+ pp subrange2.to_a
656
+ puts
599
657
 
600
658
  end
@@ -3,7 +3,7 @@ module Tempr
3
3
  module Version
4
4
  MAJOR = 0
5
5
  MINOR = 1
6
- TINY = 0
6
+ TINY = 1
7
7
  STRING = "#{MAJOR}.#{MINOR}.#{TINY}"
8
8
  end
9
9
  end
@@ -0,0 +1,94 @@
1
+ require File.expand_path('test_helper', File.dirname(__FILE__))
2
+
3
+ module EachTimeOfDayTests
4
+ module Fixtures
5
+
6
+ BaseRanges = {:year => Date.civil(2012,01,01)...
7
+ Date.civil(2013,01,01)
8
+ }
9
+
10
+ end
11
+
12
+ describe 'DateTimeRange#each_time_of_day' do
13
+
14
+ describe "specified time zone" do
15
+ let(:subject) { Fixtures::BaseRanges[:year].extend(Tempr::DateTimeRange) }
16
+ let(:offset_string) { '+09:00' }
17
+ let(:offset) { 9*60*60 }
18
+
19
+ it 'subranges should have the specified timezone offset' do
20
+ subject.each_day.each_time_of_day("11:20pm",60*60,offset_string).each do |range|
21
+ assert_equal offset, range.begin.utc_offset
22
+ assert_equal offset, range.end.utc_offset
23
+ end
24
+ end
25
+ end
26
+
27
+ describe "across daylight savings time boundaries" do
28
+ let(:subject) { Fixtures::BaseRanges[:year].extend(Tempr::DateTimeRange) }
29
+ let(:time_string) { "2:00pm" }
30
+ let(:begin_hour_min) { [14,0] }
31
+ let(:end_hour_min) { [15,0] }
32
+
33
+ it 'should be at the same time of day regardless of time zone' do
34
+ subject.each_day.each_time_of_day(time_string,60*60).each do |range|
35
+ offset = range.begin.utc_offset
36
+ actual_begin = range.begin.getlocal(offset)
37
+ actual_end = range.end.getlocal(offset)
38
+ #puts "#{range.inspect}"
39
+ assert_equal begin_hour_min,
40
+ [actual_begin.hour, actual_begin.min],
41
+ "for range: #{range}"
42
+ assert_equal end_hour_min,
43
+ [actual_end.hour, actual_end.min],
44
+ "for range: #{range}"
45
+ end
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+
52
+ describe 'DateTimeRange#between_times' do
53
+
54
+ let(:subject) { Fixtures::BaseRanges[:year].extend(Tempr::DateTimeRange) }
55
+
56
+ def assert_range_matches( exp_begin, exp_end, range )
57
+ offset = range.begin.utc_offset
58
+ actual_begin = range.begin.getlocal(offset)
59
+ actual_end = range.end.getlocal(offset)
60
+ assert_equal exp_begin,
61
+ [actual_begin.hour, actual_begin.min],
62
+ "for range: #{range}"
63
+ assert_equal exp_end,
64
+ [actual_end.hour, actual_end.min],
65
+ "for range: #{range}"
66
+ end
67
+
68
+ it 'should return correct ranges within a single day' do
69
+ begin_hour_min = [16,45]
70
+ end_hour_min = [19,35]
71
+ begin_string = "#{begin_hour_min[0]}:#{begin_hour_min[1]}"
72
+ end_string = "#{end_hour_min[0]}:#{end_hour_min[1]}"
73
+
74
+ subject.each_day.between_times(begin_string, end_string).each do |range|
75
+ assert_range_matches begin_hour_min, end_hour_min, range
76
+ end
77
+ end
78
+
79
+ it 'should return correct ranges across days' do
80
+ begin_hour_min = [23,30]
81
+ end_hour_min = [02,17]
82
+ begin_string = "#{begin_hour_min[0]}:#{begin_hour_min[1]}"
83
+ end_string = "#{end_hour_min[0]}:#{end_hour_min[1]}"
84
+
85
+ subject.each_day.between_times(begin_string, end_string).each do |range|
86
+ assert_range_matches begin_hour_min, end_hour_min, range
87
+ assert_equal 1, (range.end.to_date - range.begin.to_date),
88
+ "for range: #{range}"
89
+ end
90
+ end
91
+
92
+ end
93
+
94
+ end # namespace
@@ -1,3 +1,3 @@
1
- %w[ time_subrange at_time ].each do |test|
1
+ %w[ time_subrange each_time_of_day ].each do |test|
2
2
  require File.expand_path(test,File.dirname(__FILE__))
3
3
  end
@@ -215,7 +215,7 @@ module TimeSubRangeTests
215
215
  last_result
216
216
  end
217
217
 
218
- it 'each_minutes must return ranges starting up to 23:00:00 of the end date' do
218
+ it 'each_hours must return ranges starting up to 23:00:00 of the end date' do
219
219
  last_result = subject.each_hours.to_a.last
220
220
  assert_equal ( (Fixtures::ExclusiveDateRange.end.to_time - 60*60)...
221
221
  Fixtures::ExclusiveDateRange.end.to_time ),
@@ -242,7 +242,7 @@ module TimeSubRangeTests
242
242
  last_result
243
243
  end
244
244
 
245
- it 'each_minutes must return ranges starting up to 23:00:00 of the end date + 1' do
245
+ it 'each_hours must return ranges starting up to 23:00:00 of the end date + 1' do
246
246
  last_result = subject.each_hours.to_a.last
247
247
  assert_equal ( ((Fixtures::NonExclusiveDateRange.end+1).to_time - 60*60)...
248
248
  (Fixtures::NonExclusiveDateRange.end+1).to_time ),
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tempr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-02-16 00:00:00.000000000Z
12
+ date: 2012-02-19 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: minitest
16
- requirement: &16129140 !ruby/object:Gem::Requirement
16
+ requirement: &10703460 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,7 +21,7 @@ dependencies:
21
21
  version: '0'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *16129140
24
+ version_requirements: *10703460
25
25
  description: ''
26
26
  email:
27
27
  - ericgj72@gmail.com
@@ -30,12 +30,13 @@ extensions: []
30
30
  extra_rdoc_files: []
31
31
  files:
32
32
  - .gitignore
33
+ - CHANGELOG.markdown
33
34
  - Gemfile
34
35
  - README.markdown
35
36
  - lib/tempr.rb
36
37
  - lib/tempr/date_time_range.rb
37
38
  - lib/tempr/version.rb
38
- - test/at_time.rb
39
+ - test/each_time_of_day.rb
39
40
  - test/suite.rb
40
41
  - test/test_helper.rb
41
42
  - test/time_subrange.rb
@@ -1,36 +0,0 @@
1
- require File.expand_path('test_helper', File.dirname(__FILE__))
2
-
3
- module AtTimeTests
4
- module Fixtures
5
-
6
- BaseRanges = {:local => Time.parse('2012-01-01 00:00:00 -0500')...
7
- Time.parse('2013-01-01 00:00:00 -0500'),
8
- :utc => Time.parse('2012-01-01 00:00:00 UTC')...
9
- Time.parse('2013-01-01 00:00:00 UTC')
10
- }
11
-
12
- end
13
-
14
- describe 'DateTimeRange#at_time' do
15
-
16
- [:local, :utc].each do |time_type|
17
-
18
- describe "across daylight savings time boundaries for #{time_type} times" do
19
-
20
- let(:subject) { Fixtures::BaseRanges[time_type].extend(Tempr::DateTimeRange) }
21
-
22
- it 'should be at the same time of day regardless of time zone' do
23
- subject.each_day.at_time("2:00pm",60*60).each do |range|
24
- offset = range.begin.utc_offset
25
- actual = range.begin.getlocal(offset).hour
26
- #puts "#{range}"
27
- assert_equal 14, actual, "for range: #{range}"
28
- end
29
- end
30
-
31
- end
32
-
33
- end
34
- end
35
-
36
- end # namespace