timespan 0.1.4 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +4 -1
- data/Gemfile.lock +76 -12
- data/README.md +119 -12
- data/Rakefile +2 -2
- data/VERSION +1 -1
- data/config/locales/timespan/da.yml +5 -0
- data/lib/timespan.rb +160 -166
- data/lib/timespan/compare.rb +59 -0
- data/lib/timespan/mongoid.rb +31 -0
- data/lib/timespan/printer.rb +71 -0
- data/lib/timespan/rails/engine.rb +9 -0
- data/lib/timespan/span.rb +43 -0
- data/lib/timespan/units.rb +92 -0
- data/spec/timespan/compare_spec.rb +50 -0
- data/spec/timespan/locales/duration_da.yml +20 -0
- data/spec/timespan/printer_spec.rb +24 -0
- data/spec/timespan/span_spec.rb +67 -0
- data/spec/timespan/units_spec.rb +54 -0
- data/spec/timespan_spec.rb +81 -46
- data/timespan.gemspec +22 -4
- metadata +49 -5
data/Gemfile
CHANGED
@@ -1,11 +1,14 @@
|
|
1
1
|
source :rubygems
|
2
2
|
|
3
3
|
gem 'chronic'
|
4
|
+
gem 'chronic_duration'
|
4
5
|
gem 'spanner'
|
5
|
-
gem 'ruby-duration'
|
6
|
+
gem 'ruby-duration', :git => 'git://github.com/kristianmandrup/ruby-duration.git'
|
6
7
|
|
7
8
|
group :test, :development do
|
8
9
|
gem "rspec", ">= 2.8.0"
|
10
|
+
gem 'rails', '~> 3.2'
|
11
|
+
# gem 'i18n'
|
9
12
|
end
|
10
13
|
|
11
14
|
group :development do
|
data/Gemfile.lock
CHANGED
@@ -1,31 +1,88 @@
|
|
1
|
+
GIT
|
2
|
+
remote: git://github.com/kristianmandrup/ruby-duration.git
|
3
|
+
revision: 002f43a5ad5b6191ae5ee218e532176005279384
|
4
|
+
specs:
|
5
|
+
ruby-duration (2.1.4)
|
6
|
+
activesupport (>= 3.0.0)
|
7
|
+
i18n
|
8
|
+
|
1
9
|
GEM
|
2
10
|
remote: http://rubygems.org/
|
3
11
|
specs:
|
12
|
+
actionmailer (3.2.3)
|
13
|
+
actionpack (= 3.2.3)
|
14
|
+
mail (~> 2.4.4)
|
15
|
+
actionpack (3.2.3)
|
16
|
+
activemodel (= 3.2.3)
|
17
|
+
activesupport (= 3.2.3)
|
18
|
+
builder (~> 3.0.0)
|
19
|
+
erubis (~> 2.7.0)
|
20
|
+
journey (~> 1.0.1)
|
21
|
+
rack (~> 1.4.0)
|
22
|
+
rack-cache (~> 1.2)
|
23
|
+
rack-test (~> 0.6.1)
|
24
|
+
sprockets (~> 2.1.2)
|
4
25
|
activemodel (3.2.3)
|
5
26
|
activesupport (= 3.2.3)
|
6
27
|
builder (~> 3.0.0)
|
28
|
+
activerecord (3.2.3)
|
29
|
+
activemodel (= 3.2.3)
|
30
|
+
activesupport (= 3.2.3)
|
31
|
+
arel (~> 3.0.2)
|
32
|
+
tzinfo (~> 0.3.29)
|
33
|
+
activeresource (3.2.3)
|
34
|
+
activemodel (= 3.2.3)
|
35
|
+
activesupport (= 3.2.3)
|
7
36
|
activesupport (3.2.3)
|
8
37
|
i18n (~> 0.6)
|
9
38
|
multi_json (~> 1.0)
|
10
|
-
|
39
|
+
arel (3.0.2)
|
11
40
|
builder (3.0.0)
|
12
41
|
chronic (0.6.7)
|
42
|
+
chronic_duration (0.9.6)
|
43
|
+
numerizer (~> 0.1.1)
|
13
44
|
diff-lcs (1.1.3)
|
45
|
+
erubis (2.7.0)
|
14
46
|
git (1.2.5)
|
47
|
+
hike (1.2.1)
|
15
48
|
i18n (0.6.0)
|
16
49
|
jeweler (1.8.3)
|
17
50
|
bundler (~> 1.0)
|
18
51
|
git (>= 1.2.5)
|
19
52
|
rake
|
20
53
|
rdoc
|
54
|
+
journey (1.0.3)
|
21
55
|
json (1.6.6)
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
tzinfo (~> 0.3.22)
|
56
|
+
mail (2.4.4)
|
57
|
+
i18n (>= 0.4.0)
|
58
|
+
mime-types (~> 1.16)
|
59
|
+
treetop (~> 1.4.8)
|
60
|
+
mime-types (1.18)
|
28
61
|
multi_json (1.3.2)
|
62
|
+
numerizer (0.1.1)
|
63
|
+
polyglot (0.3.3)
|
64
|
+
rack (1.4.1)
|
65
|
+
rack-cache (1.2)
|
66
|
+
rack (>= 0.4)
|
67
|
+
rack-ssl (1.3.2)
|
68
|
+
rack
|
69
|
+
rack-test (0.6.1)
|
70
|
+
rack (>= 1.0)
|
71
|
+
rails (3.2.3)
|
72
|
+
actionmailer (= 3.2.3)
|
73
|
+
actionpack (= 3.2.3)
|
74
|
+
activerecord (= 3.2.3)
|
75
|
+
activeresource (= 3.2.3)
|
76
|
+
activesupport (= 3.2.3)
|
77
|
+
bundler (~> 1.0)
|
78
|
+
railties (= 3.2.3)
|
79
|
+
railties (3.2.3)
|
80
|
+
actionpack (= 3.2.3)
|
81
|
+
activesupport (= 3.2.3)
|
82
|
+
rack-ssl (~> 1.3.2)
|
83
|
+
rake (>= 0.8.7)
|
84
|
+
rdoc (~> 3.4)
|
85
|
+
thor (~> 0.14.6)
|
29
86
|
rake (0.9.2.2)
|
30
87
|
rdoc (3.12)
|
31
88
|
json (~> 1.4)
|
@@ -37,15 +94,20 @@ GEM
|
|
37
94
|
rspec-expectations (2.9.1)
|
38
95
|
diff-lcs (~> 1.1.3)
|
39
96
|
rspec-mocks (2.9.0)
|
40
|
-
ruby-duration (2.1.3)
|
41
|
-
activesupport (>= 3.0.0)
|
42
|
-
i18n
|
43
|
-
mongoid (~> 2.4.0)
|
44
97
|
simplecov (0.6.2)
|
45
98
|
multi_json (~> 1.3)
|
46
99
|
simplecov-html (~> 0.5.3)
|
47
100
|
simplecov-html (0.5.3)
|
48
101
|
spanner (0.0.2)
|
102
|
+
sprockets (2.1.2)
|
103
|
+
hike (~> 1.2)
|
104
|
+
rack (~> 1.0)
|
105
|
+
tilt (~> 1.1, != 1.3.0)
|
106
|
+
thor (0.14.6)
|
107
|
+
tilt (1.3.3)
|
108
|
+
treetop (1.4.10)
|
109
|
+
polyglot
|
110
|
+
polyglot (>= 0.3.1)
|
49
111
|
tzinfo (0.3.33)
|
50
112
|
|
51
113
|
PLATFORMS
|
@@ -54,9 +116,11 @@ PLATFORMS
|
|
54
116
|
DEPENDENCIES
|
55
117
|
bundler (>= 1.0.0)
|
56
118
|
chronic
|
119
|
+
chronic_duration
|
57
120
|
jeweler (>= 1.8.3)
|
121
|
+
rails (~> 3.2)
|
58
122
|
rdoc (>= 3.12)
|
59
123
|
rspec (>= 2.8.0)
|
60
|
-
ruby-duration
|
124
|
+
ruby-duration!
|
61
125
|
simplecov (>= 0.5)
|
62
126
|
spanner
|
data/README.md
CHANGED
@@ -1,30 +1,137 @@
|
|
1
1
|
# Timespan
|
2
2
|
|
3
|
-
Use
|
3
|
+
Use Timespans in Ruby :)
|
4
4
|
|
5
|
-
Will calculate time diff
|
5
|
+
Will calculate time diff between two dates, then allow you to get the time difference in some time unit as a number.
|
6
6
|
|
7
7
|
```ruby
|
8
|
-
t =
|
8
|
+
t = Timespan.new(:start => Date.today, :duration => 3.days.ago)
|
9
9
|
t.to_days # => 3
|
10
10
|
t.to_weeks # => 0
|
11
11
|
t.to_secs # => 259200
|
12
12
|
t.to_hours = 10800
|
13
13
|
|
14
|
-
|
14
|
+
t = Timespan.new("2 days") # from today
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
t = Timespan.new("3 hrs").from(2.days.from_now)
|
17
|
+
|
18
|
+
t = Timespan.new(:from => Date.today, :to => "6 weeks from now")
|
19
|
+
|
20
|
+
t = Timespan.new(:from => Date.today, :duration => "7 weeks 3 days")
|
21
|
+
t = Timespan.new(:from => 2.days.ago, :duration => "5 months and 2 weeks")
|
22
|
+
```
|
19
23
|
|
20
24
|
See specs for more examples of usage
|
21
25
|
|
22
|
-
##
|
26
|
+
## Spanner
|
27
|
+
|
28
|
+
Internally Timespan uses Spanner to parse duration strings.
|
29
|
+
|
30
|
+
`Spanner.parse('23 hours 12 minutes')
|
31
|
+
|
32
|
+
## Duration (ruby-duration)
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
Duration.new(100) => #<Duration: minutes=1, seconds=40, total=100>
|
36
|
+
Duration.new(:hours => 5, :minutes => 70) => #<Duration: hours=6, minutes=10, total=22200>
|
37
|
+
|
38
|
+
Duration.new(:weeks => 3, :days => 1).format("%w %~w and %d %~d") => "3 weeks and 1 day"
|
39
|
+
Duration.new(:weeks => 1, :days => 20).format("%w %~w and %d %~d") => "3 weeks and 6 days"
|
40
|
+
```
|
41
|
+
|
42
|
+
Duration locale file
|
43
|
+
|
44
|
+
```yaml
|
45
|
+
da:
|
46
|
+
ruby_duration:
|
47
|
+
second: sekond
|
48
|
+
seconds: sekonder
|
49
|
+
minute: minut
|
50
|
+
minutes: minutter
|
51
|
+
hour: time
|
52
|
+
hours: timer
|
53
|
+
day: dag
|
54
|
+
days: dage
|
55
|
+
week: uge
|
56
|
+
weeks: uges
|
57
|
+
month: måned
|
58
|
+
months: måneder
|
59
|
+
year: år
|
60
|
+
years: år
|
61
|
+
```
|
62
|
+
|
63
|
+
Duration datatype for Mongoid
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
require 'duration/mongoid'
|
67
|
+
|
68
|
+
class MyModel
|
69
|
+
include Mongoid::Document
|
70
|
+
field :duration, type => Duration
|
71
|
+
end
|
72
|
+
```
|
73
|
+
|
74
|
+
## Timespan i18n
|
23
75
|
|
24
|
-
|
25
|
-
|
76
|
+
Timespan locale file
|
77
|
+
|
78
|
+
```yaml
|
79
|
+
da:
|
80
|
+
timespan:
|
81
|
+
from: fra
|
82
|
+
to: til
|
83
|
+
lasting: der varer ialt
|
84
|
+
```
|
85
|
+
|
86
|
+
## Timespan for Mongoid
|
87
|
+
|
88
|
+
Custom Timespan datatype
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
require 'timespan/mongoid'
|
92
|
+
|
93
|
+
class MyModel
|
94
|
+
include Mongoid::Document
|
95
|
+
field :period, type => Timespan
|
96
|
+
end
|
97
|
+
```
|
98
|
+
|
99
|
+
## Chronic duration
|
100
|
+
|
101
|
+
Is used to parse duration strings if Spanner can't be handle it
|
102
|
+
|
103
|
+
`ChronicDuration.parse('4 minutes and 30 seconds')
|
104
|
+
|
105
|
+
### Endure
|
106
|
+
|
107
|
+
Use the 'endure' gem based on the old "days_and_times".
|
108
|
+
|
109
|
+
See: [days_and_times](https://github.com/kristianmandrup/days_and_times)
|
110
|
+
|
111
|
+
Currently it also uses Duration, which conflicts with the 'ruby-duration' gem.
|
112
|
+
|
113
|
+
```
|
114
|
+
1.day #=> A duration of 1 day
|
115
|
+
7.days #=> A duration of 7 days
|
116
|
+
1.week #=> A duration of 1 week
|
117
|
+
1.week - 2.days #=> A duration of 5 days
|
118
|
+
1.week.from(Now()) #=> The time of 1 week from this moment
|
119
|
+
1.week.from(Today()) #=> The time of 1 week from the beginning of today
|
120
|
+
3.minutes.ago.until(7.minutes.from(Now())) #=> duration 3 minutes ago to 7 minutes from now
|
121
|
+
3.minutes.ago.until(7.minutes.from(Now())) - 2.minutes #=> duration 3 minutes ago to 5 minutes from now
|
122
|
+
4.weeks.from(2.days.from(Now())).until(8.weeks.from(Yesterday())) #=> A duration, starting in 4 weeks and 2 days, and ending 8 weeks from yesterday
|
123
|
+
1.week - 1.second #=> A duration of 6 days, 23 hours, 59 minutes, and 59 seconds
|
124
|
+
4.weeks / 2 #=> A duration of 2 weeks
|
125
|
+
4.weeks / 2.weeks #=> The integer 2
|
126
|
+
8.weeks.each {|week| ...} #=> Runs code for each week contained in the duration (of 8 weeks)
|
127
|
+
8.weeks.starting(Now()).each {|week| ...} #=> Runs code for each week in the duration, but each week is also anchored to a starting time, in sequence through the duration.
|
128
|
+
1.week.each {|week| ...} #=> Automatically chooses week as its iterator
|
129
|
+
7.days.each {|day| ...} #=> Automatically chooses day as its iterator
|
130
|
+
1.week.each_day {|day| ...} #=> Forcing the week to iterate through days
|
131
|
+
1.week.each(10.hours) {|ten_hour_segment| ...} #=> Using a custom iterator of 10 hours. There would be 17 of them, but notice that the last iteration will only be 8 hours.
|
132
|
+
``
|
26
133
|
|
27
|
-
## Contributing to
|
134
|
+
## Contributing to Timespan
|
28
135
|
|
29
136
|
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
30
137
|
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
@@ -34,7 +141,7 @@ See specs for more examples of usage
|
|
34
141
|
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
35
142
|
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
36
143
|
|
37
|
-
|
144
|
+
## Copyright
|
38
145
|
|
39
146
|
Copyright (c) 2012 Kristian Mandrup. See LICENSE.txt for
|
40
147
|
further details.
|
data/Rakefile
CHANGED
@@ -17,8 +17,8 @@ Jeweler::Tasks.new do |gem|
|
|
17
17
|
gem.name = "timespan"
|
18
18
|
gem.homepage = "http://github.com/kristianmandrup/timespan"
|
19
19
|
gem.license = "MIT"
|
20
|
-
gem.summary = %Q{Use
|
21
|
-
gem.description = %Q{
|
20
|
+
gem.summary = %Q{Use timespans in ruby}
|
21
|
+
gem.description = %Q{Makes it easy to calculate time distance in different units}
|
22
22
|
gem.email = "kmandrup@gmail.com"
|
23
23
|
gem.authors = ["Kristian Mandrup"]
|
24
24
|
# dependencies defined in Gemfile
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/lib/timespan.rb
CHANGED
@@ -1,217 +1,211 @@
|
|
1
1
|
require 'duration'
|
2
|
+
require 'chronic'
|
3
|
+
require 'chronic_duration'
|
2
4
|
require 'spanner'
|
3
5
|
|
4
|
-
|
5
|
-
|
6
|
+
require 'timespan/units'
|
7
|
+
require 'timespan/compare'
|
8
|
+
require 'timespan/printer'
|
9
|
+
require 'timespan/span'
|
6
10
|
|
7
|
-
|
11
|
+
if defined?(Rails) && Rails::VERSION::STRING.to >= '3.1'
|
12
|
+
require 'duration/rails/engine'
|
13
|
+
end
|
14
|
+
|
15
|
+
class Timespan
|
16
|
+
include Span
|
17
|
+
include Printer
|
18
|
+
include Compare
|
19
|
+
include Units
|
20
|
+
|
21
|
+
class TimeParseError < StandardError; end
|
22
|
+
|
23
|
+
attr_reader :start_time, :end_time
|
8
24
|
|
9
25
|
alias_method :start_date, :start_time
|
10
26
|
alias_method :end_date, :end_time
|
11
27
|
|
28
|
+
START_KEYS = [:start, :from]
|
29
|
+
END_KEYS = [:to, :end]
|
30
|
+
DURATION_KEYS = [:duration, :lasting]
|
31
|
+
|
32
|
+
ALL_KEYS = START_KEYS + END_KEYS + DURATION_KEYS
|
33
|
+
|
12
34
|
def initialize options = {}
|
13
35
|
@is_new = true
|
14
36
|
|
15
|
-
|
37
|
+
case options
|
38
|
+
when Numeric, Duration, String
|
39
|
+
options = {:duration => options}
|
40
|
+
end
|
41
|
+
|
42
|
+
configure options
|
16
43
|
|
17
44
|
@is_new = false
|
18
45
|
end
|
19
46
|
|
20
|
-
def start_time=
|
21
|
-
@start_time =
|
22
|
-
|
47
|
+
def start_time= time
|
48
|
+
@start_time = convert_to_time time
|
49
|
+
unless is_new?
|
50
|
+
refresh!
|
51
|
+
add_dirty :start
|
52
|
+
calculate!
|
53
|
+
end
|
23
54
|
end
|
24
55
|
alias_method :start_date=, :start_time=
|
25
|
-
|
26
|
-
def
|
27
|
-
|
28
|
-
|
56
|
+
|
57
|
+
def from time
|
58
|
+
self.start_time = time
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
def end_time= time
|
63
|
+
@end_time = convert_to_time time
|
64
|
+
unless is_new?
|
65
|
+
add_dirty :end
|
66
|
+
refresh!
|
67
|
+
calculate!
|
68
|
+
end
|
29
69
|
end
|
30
70
|
alias_method :end_date=, :end_time=
|
31
71
|
|
32
|
-
def
|
33
|
-
|
34
|
-
|
72
|
+
def until time
|
73
|
+
self.end_time = time
|
74
|
+
self
|
35
75
|
end
|
36
76
|
|
37
|
-
def
|
38
|
-
|
39
|
-
end
|
40
|
-
|
41
|
-
def duration= duration
|
42
|
-
@duration = case duration
|
43
|
-
when Duration
|
44
|
-
duration
|
45
|
-
when Integer, Hash
|
46
|
-
Duration.new duration
|
77
|
+
def convert_to_time time
|
78
|
+
case time
|
47
79
|
when String
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
80
|
+
Chronic.parse(time)
|
81
|
+
when Date, DateTime
|
82
|
+
time.to_time
|
83
|
+
when Time
|
84
|
+
time
|
53
85
|
else
|
54
|
-
raise ArgumentError, "
|
55
|
-
end
|
56
|
-
refresh! unless is_new?
|
57
|
-
end
|
58
|
-
|
59
|
-
def to_s
|
60
|
-
if duration
|
61
|
-
"TimeSpan: from #{start_time} lasting #{duration} = #{seconds} secs" if start_time
|
62
|
-
"TimeSpan: from #{end_time} to #{duration} before = #{seconds} secs" if end_time
|
63
|
-
return
|
86
|
+
raise ArgumentError, "A valid time must be either a String, Date, Time or DateTime, was: #{time.inspect}"
|
64
87
|
end
|
88
|
+
end
|
65
89
|
|
66
|
-
|
67
|
-
"TimeSpan: #{start_time} to #{end_time} = #{seconds} secs"
|
68
|
-
return
|
69
|
-
end
|
90
|
+
protected
|
70
91
|
|
71
|
-
|
72
|
-
|
73
|
-
end
|
92
|
+
def first_from keys, options = {}
|
93
|
+
keys.select {|key| options[key] }.first
|
74
94
|
end
|
75
95
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
case time
|
81
|
-
when TimeSpan
|
82
|
-
millis <=> time.seconds
|
83
|
-
when Time
|
84
|
-
millis <=> time.to_i
|
85
|
-
when Date, DateTime
|
86
|
-
time.to_time.to_i
|
87
|
-
when Integer
|
88
|
-
millis <=> time
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
alias_method :to_secs, :seconds
|
93
|
-
alias_method :to_seconds, :seconds
|
94
|
-
|
95
|
-
def to_milliseconds
|
96
|
-
@to_seconds ||= (seconds * 1000.0).round
|
97
|
-
end
|
98
|
-
alias_method :to_mils, :to_milliseconds
|
99
|
-
alias_method :millis, :to_mils
|
100
|
-
alias_method :milliseconds, :to_mils
|
101
|
-
|
102
|
-
def to_minutes
|
103
|
-
@to_minutes ||= (to_seconds / 60.0).round
|
104
|
-
end
|
105
|
-
alias_method :to_m, :to_minutes
|
106
|
-
alias_method :to_mins, :to_minutes
|
107
|
-
alias_method :minutes, :to_minutes
|
108
|
-
|
109
|
-
def to_hours
|
110
|
-
@to_hours ||= (to_minutes / 60.0).round
|
111
|
-
end
|
112
|
-
alias_method :to_h, :to_hours
|
113
|
-
alias_method :hrs, :to_hours
|
114
|
-
alias_method :hours, :to_hours
|
115
|
-
|
116
|
-
def to_days
|
117
|
-
@to_days ||= (to_hours / 24.0).round
|
118
|
-
end
|
119
|
-
alias_method :to_d, :to_days
|
120
|
-
alias_method :days, :to_days
|
121
|
-
|
122
|
-
def to_weeks
|
123
|
-
@to_weeks ||= (to_days / 7.0).round
|
124
|
-
end
|
125
|
-
alias_method :to_w, :to_weeks
|
126
|
-
alias_method :weeks, :to_days
|
127
|
-
|
128
|
-
def to_months
|
129
|
-
@to_months ||= (to_days / 30.0).round
|
130
|
-
end
|
131
|
-
alias_method :to_mon, :to_months
|
132
|
-
alias_method :months, :to_months
|
133
|
-
|
134
|
-
def to_years
|
135
|
-
@to_years ||= (to_days.to_f / 365.25).round
|
136
|
-
end
|
137
|
-
alias_method :to_y, :to_years
|
138
|
-
alias_method :years, :to_years
|
139
|
-
|
140
|
-
def to_decades
|
141
|
-
@to_decades ||= (to_years / 10.0).round
|
142
|
-
end
|
143
|
-
alias_method :decades, :to_decades
|
144
|
-
|
145
|
-
def to_centuries
|
146
|
-
@to_centuries ||= (to_decades / 10.0).round
|
147
|
-
end
|
148
|
-
alias_method :centuries, :to_centuries
|
149
|
-
|
150
|
-
def units
|
151
|
-
%w{seconds minutes hours days weeks months years}
|
152
|
-
end
|
96
|
+
def configure options = {}
|
97
|
+
from = options[first_from START_KEYS, options]
|
98
|
+
to = options[first_from END_KEYS, options]
|
99
|
+
dur = options[first_from DURATION_KEYS, options]
|
153
100
|
|
154
|
-
|
101
|
+
self.duration = dur if dur
|
102
|
+
self.start_time = from if from
|
103
|
+
self.end_time = to if to
|
155
104
|
|
156
|
-
|
157
|
-
case options
|
158
|
-
when Hash
|
159
|
-
duration = options[:duration]
|
160
|
-
@start_time = options[:from] || options[:start]
|
161
|
-
@end_time = options[:to] || options[:end]
|
162
|
-
when Integer, String
|
163
|
-
duration = options
|
164
|
-
else
|
165
|
-
raise ArgumentError, "Timespan must take Hash or Integer, was: #{options}"
|
166
|
-
end
|
105
|
+
default_from_now! unless start_time || end_time
|
167
106
|
|
168
|
-
|
107
|
+
calculate_miss!
|
108
|
+
rescue Exception => e
|
109
|
+
calculate_miss!
|
110
|
+
validate!
|
169
111
|
end
|
170
112
|
|
171
|
-
def
|
172
|
-
|
113
|
+
def default_from_now!
|
114
|
+
self.start_time = Time.now
|
115
|
+
end
|
173
116
|
|
174
|
-
|
175
|
-
|
176
|
-
@seconds ||= (@end_time - @start_time).to_i
|
177
|
-
end
|
178
|
-
else
|
179
|
-
@seconds ||= @duration.total
|
180
|
-
end
|
117
|
+
def validate!
|
118
|
+
raise ArgumentError, "#{valid_requirement}, was: #{current_config}" unless valid?
|
181
119
|
end
|
182
120
|
|
183
|
-
def
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
121
|
+
def valid_requirement
|
122
|
+
"Timespan must take a :start and :end time or any of :start and :end time and a :duration"
|
123
|
+
end
|
124
|
+
|
125
|
+
def current_config
|
126
|
+
"end time: #{end_time}, start time: #{start_time}, duration: #{duration}"
|
127
|
+
end
|
128
|
+
|
129
|
+
def valid?
|
130
|
+
(end_time && start_time) || (end_time || start_time && duration)
|
191
131
|
end
|
192
132
|
|
193
133
|
def is_new?
|
194
134
|
@is_new
|
195
135
|
end
|
196
136
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
137
|
+
def dirty
|
138
|
+
@dirty ||= []
|
139
|
+
end
|
140
|
+
|
141
|
+
def add_dirty type
|
142
|
+
reset_dirty if dirty.size > 2
|
143
|
+
dirty << type
|
144
|
+
end
|
145
|
+
|
146
|
+
def reset_dirty
|
147
|
+
@dirty = []
|
148
|
+
end
|
149
|
+
|
150
|
+
def dirty? type
|
151
|
+
dirty.include? type
|
152
|
+
end
|
153
|
+
|
154
|
+
def calculate!
|
155
|
+
set_duration unless dirty? :duration
|
156
|
+
set_start_time unless dirty? :start
|
157
|
+
set_end_time unless dirty? :end
|
158
|
+
end
|
159
|
+
|
160
|
+
def calculate_miss!
|
161
|
+
set_end_time_miss
|
162
|
+
set_start_time_miss
|
163
|
+
set_duration_miss
|
164
|
+
set_end_time_miss
|
165
|
+
set_start_time_miss
|
166
|
+
end
|
167
|
+
|
168
|
+
def set_end_time_miss
|
169
|
+
set_end_time if missing_end_time?
|
170
|
+
end
|
171
|
+
|
172
|
+
def set_end_time
|
173
|
+
self.end_time = start_time - duration.total
|
174
|
+
end
|
175
|
+
|
176
|
+
def set_start_time_miss
|
177
|
+
set_start_time if missing_start_time?
|
178
|
+
end
|
179
|
+
|
180
|
+
def set_start_time
|
181
|
+
self.start_time = end_time - duration.total
|
182
|
+
end
|
183
|
+
|
184
|
+
def set_duration_miss
|
185
|
+
set_duration if missing_duration?
|
186
|
+
end
|
187
|
+
|
188
|
+
def set_duration
|
189
|
+
self.duration = end_time - start_time
|
204
190
|
end
|
205
191
|
|
206
|
-
def
|
207
|
-
|
192
|
+
def missing_end_time?
|
193
|
+
start_time && duration && !end_time
|
208
194
|
end
|
209
195
|
|
210
|
-
def
|
211
|
-
|
196
|
+
def missing_start_time?
|
197
|
+
end_time && duration && !start_time
|
212
198
|
end
|
213
199
|
|
214
|
-
def
|
215
|
-
|
200
|
+
def missing_duration?
|
201
|
+
start_time && end_time && !duration
|
202
|
+
end
|
203
|
+
|
204
|
+
# reset all stored instance vars for units
|
205
|
+
def refresh!
|
206
|
+
units.each do |unit|
|
207
|
+
var_name = :"@#{unit}"
|
208
|
+
instance_variable_set var_name, nil
|
209
|
+
end
|
216
210
|
end
|
217
211
|
end
|