chronic 0.2.2 → 0.10.2

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.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +6 -0
  3. data/.travis.yml +8 -0
  4. data/HISTORY.md +243 -0
  5. data/LICENSE +21 -0
  6. data/README.md +182 -0
  7. data/Rakefile +62 -15
  8. data/chronic.gemspec +23 -0
  9. data/lib/chronic/date.rb +82 -0
  10. data/lib/chronic/grabber.rb +24 -17
  11. data/lib/chronic/handler.rb +97 -0
  12. data/lib/chronic/handlers.rb +490 -312
  13. data/lib/chronic/mini_date.rb +38 -0
  14. data/lib/chronic/numerizer.rb +130 -0
  15. data/lib/chronic/ordinal.rb +33 -24
  16. data/lib/chronic/parser.rb +268 -0
  17. data/lib/chronic/pointer.rb +22 -17
  18. data/lib/chronic/repeater.rb +137 -107
  19. data/lib/chronic/repeaters/repeater_day.rb +51 -44
  20. data/lib/chronic/repeaters/repeater_day_name.rb +50 -43
  21. data/lib/chronic/repeaters/repeater_day_portion.rb +97 -81
  22. data/lib/chronic/repeaters/repeater_fortnight.rb +61 -54
  23. data/lib/chronic/repeaters/repeater_hour.rb +51 -44
  24. data/lib/chronic/repeaters/repeater_minute.rb +51 -44
  25. data/lib/chronic/repeaters/repeater_month.rb +79 -60
  26. data/lib/chronic/repeaters/repeater_month_name.rb +85 -83
  27. data/lib/chronic/repeaters/repeater_season.rb +110 -22
  28. data/lib/chronic/repeaters/repeater_season_name.rb +41 -22
  29. data/lib/chronic/repeaters/repeater_second.rb +41 -34
  30. data/lib/chronic/repeaters/repeater_time.rb +128 -104
  31. data/lib/chronic/repeaters/repeater_week.rb +65 -58
  32. data/lib/chronic/repeaters/repeater_weekday.rb +86 -0
  33. data/lib/chronic/repeaters/repeater_weekend.rb +59 -52
  34. data/lib/chronic/repeaters/repeater_year.rb +72 -52
  35. data/lib/chronic/scalar.rb +53 -46
  36. data/lib/chronic/season.rb +26 -0
  37. data/lib/chronic/separator.rb +174 -43
  38. data/lib/chronic/sign.rb +49 -0
  39. data/lib/chronic/span.rb +31 -0
  40. data/lib/chronic/tag.rb +37 -0
  41. data/lib/chronic/time.rb +40 -0
  42. data/lib/chronic/time_zone.rb +21 -11
  43. data/lib/chronic/token.rb +51 -0
  44. data/lib/chronic.rb +94 -69
  45. data/test/helper.rb +12 -0
  46. data/test/test_chronic.rb +183 -0
  47. data/test/test_daylight_savings.rb +118 -0
  48. data/test/{test_Handler.rb → test_handler.rb} +73 -55
  49. data/test/test_mini_date.rb +32 -0
  50. data/test/test_numerizer.rb +86 -0
  51. data/test/test_parsing.rb +891 -248
  52. data/test/{test_RepeaterDayName.rb → test_repeater_day_name.rb} +16 -17
  53. data/test/test_repeater_day_portion.rb +254 -0
  54. data/test/{test_RepeaterFortnight.rb → test_repeater_fortnight.rb} +17 -18
  55. data/test/{test_RepeaterHour.rb → test_repeater_hour.rb} +23 -20
  56. data/test/test_repeater_minute.rb +34 -0
  57. data/test/{test_RepeaterMonth.rb → test_repeater_month.rb} +21 -18
  58. data/test/{test_RepeaterMonthName.rb → test_repeater_month_name.rb} +18 -19
  59. data/test/test_repeater_season.rb +40 -0
  60. data/test/{test_RepeaterTime.rb → test_repeater_time.rb} +39 -23
  61. data/test/{test_RepeaterWeek.rb → test_repeater_week.rb} +17 -18
  62. data/test/test_repeater_weekday.rb +55 -0
  63. data/test/{test_RepeaterWeekend.rb → test_repeater_weekend.rb} +22 -23
  64. data/test/{test_RepeaterYear.rb → test_repeater_year.rb} +25 -19
  65. data/test/{test_Span.rb → test_span.rb} +7 -8
  66. data/test/{test_Token.rb → test_token.rb} +6 -7
  67. metadata +163 -85
  68. data/History.txt +0 -49
  69. data/Manifest.txt +0 -47
  70. data/README.txt +0 -149
  71. data/lib/chronic/chronic.rb +0 -239
  72. data/lib/numerizer/numerizer.rb +0 -103
  73. data/test/suite.rb +0 -9
  74. data/test/test_Chronic.rb +0 -50
  75. data/test/test_Numerizer.rb +0 -48
  76. data/test/test_Time.rb +0 -50
@@ -1,115 +1,145 @@
1
- class Chronic::Repeater < Chronic::Tag #:nodoc:
2
- def self.scan(tokens, options)
3
- # for each token
4
- tokens.each_index do |i|
5
- if t = self.scan_for_month_names(tokens[i]) then tokens[i].tag(t); next end
6
- if t = self.scan_for_day_names(tokens[i]) then tokens[i].tag(t); next end
7
- if t = self.scan_for_day_portions(tokens[i]) then tokens[i].tag(t); next end
8
- if t = self.scan_for_times(tokens[i], options) then tokens[i].tag(t); next end
9
- if t = self.scan_for_units(tokens[i]) then tokens[i].tag(t); next end
1
+ module Chronic
2
+ class Repeater < Tag
3
+
4
+ # Scan an Array of Token objects and apply any necessary Repeater
5
+ # tags to each token.
6
+ #
7
+ # tokens - An Array of tokens to scan.
8
+ # options - The Hash of options specified in Chronic::parse.
9
+ #
10
+ # Returns an Array of tokens.
11
+ def self.scan(tokens, options)
12
+ tokens.each do |token|
13
+ if t = scan_for_season_names(token, options) then token.tag(t); next end
14
+ if t = scan_for_month_names(token, options) then token.tag(t); next end
15
+ if t = scan_for_day_names(token, options) then token.tag(t); next end
16
+ if t = scan_for_day_portions(token, options) then token.tag(t); next end
17
+ if t = scan_for_times(token, options) then token.tag(t); next end
18
+ if t = scan_for_units(token, options) then token.tag(t); next end
19
+ end
10
20
  end
11
- tokens
12
- end
13
-
14
- def self.scan_for_month_names(token)
15
- scanner = {/^jan\.?(uary)?$/ => :january,
16
- /^feb\.?(ruary)?$/ => :february,
17
- /^mar\.?(ch)?$/ => :march,
18
- /^apr\.?(il)?$/ => :april,
19
- /^may$/ => :may,
20
- /^jun\.?e?$/ => :june,
21
- /^jul\.?y?$/ => :july,
22
- /^aug\.?(ust)?$/ => :august,
23
- /^sep\.?(t\.?|tember)?$/ => :september,
24
- /^oct\.?(ober)?$/ => :october,
25
- /^nov\.?(ember)?$/ => :november,
26
- /^dec\.?(ember)?$/ => :december}
27
- scanner.keys.each do |scanner_item|
28
- return Chronic::RepeaterMonthName.new(scanner[scanner_item]) if scanner_item =~ token.word
21
+
22
+ # token - The Token object we want to scan.
23
+ #
24
+ # Returns a new Repeater object.
25
+ def self.scan_for_season_names(token, options = {})
26
+ scan_for token, RepeaterSeasonName,
27
+ {
28
+ /^springs?$/ => :spring,
29
+ /^summers?$/ => :summer,
30
+ /^(autumn)|(fall)s?$/ => :autumn,
31
+ /^winters?$/ => :winter
32
+ }, options
29
33
  end
30
- return nil
31
- end
32
-
33
- def self.scan_for_day_names(token)
34
- scanner = {/^m[ou]n(day)?$/ => :monday,
35
- /^t(ue|eu|oo|u|)s(day)?$/ => :tuesday,
36
- /^tue$/ => :tuesday,
37
- /^we(dnes|nds|nns)day$/ => :wednesday,
38
- /^wed$/ => :wednesday,
39
- /^th(urs|ers)day$/ => :thursday,
40
- /^thu$/ => :thursday,
41
- /^fr[iy](day)?$/ => :friday,
42
- /^sat(t?[ue]rday)?$/ => :saturday,
43
- /^su[nm](day)?$/ => :sunday}
44
- scanner.keys.each do |scanner_item|
45
- return Chronic::RepeaterDayName.new(scanner[scanner_item]) if scanner_item =~ token.word
34
+
35
+ # token - The Token object we want to scan.
36
+ #
37
+ # Returns a new Repeater object.
38
+ def self.scan_for_month_names(token, options = {})
39
+ scan_for token, RepeaterMonthName,
40
+ {
41
+ /^jan[:\.]?(uary)?$/ => :january,
42
+ /^feb[:\.]?(ruary)?$/ => :february,
43
+ /^mar[:\.]?(ch)?$/ => :march,
44
+ /^apr[:\.]?(il)?$/ => :april,
45
+ /^may$/ => :may,
46
+ /^jun[:\.]?e?$/ => :june,
47
+ /^jul[:\.]?y?$/ => :july,
48
+ /^aug[:\.]?(ust)?$/ => :august,
49
+ /^sep[:\.]?(t[:\.]?|tember)?$/ => :september,
50
+ /^oct[:\.]?(ober)?$/ => :october,
51
+ /^nov[:\.]?(ember)?$/ => :november,
52
+ /^dec[:\.]?(ember)?$/ => :december
53
+ }, options
46
54
  end
47
- return nil
48
- end
49
-
50
- def self.scan_for_day_portions(token)
51
- scanner = {/^ams?$/ => :am,
52
- /^pms?$/ => :pm,
53
- /^mornings?$/ => :morning,
54
- /^afternoons?$/ => :afternoon,
55
- /^evenings?$/ => :evening,
56
- /^(night|nite)s?$/ => :night}
57
- scanner.keys.each do |scanner_item|
58
- return Chronic::RepeaterDayPortion.new(scanner[scanner_item]) if scanner_item =~ token.word
55
+
56
+ # token - The Token object we want to scan.
57
+ #
58
+ # Returns a new Repeater object.
59
+ def self.scan_for_day_names(token, options = {})
60
+ scan_for token, RepeaterDayName,
61
+ {
62
+ /^m[ou]n(day)?$/ => :monday,
63
+ /^t(ue|eu|oo|u|)s?(day)?$/ => :tuesday,
64
+ /^we(d|dnes|nds|nns)(day)?$/ => :wednesday,
65
+ /^th(u|ur|urs|ers)(day)?$/ => :thursday,
66
+ /^fr[iy](day)?$/ => :friday,
67
+ /^sat(t?[ue]rday)?$/ => :saturday,
68
+ /^su[nm](day)?$/ => :sunday
69
+ }, options
59
70
  end
60
- return nil
61
- end
62
-
63
- def self.scan_for_times(token, options)
64
- if token.word =~ /^\d{1,2}(:?\d{2})?([\.:]?\d{2})?$/
65
- return Chronic::RepeaterTime.new(token.word, options)
71
+
72
+ # token - The Token object we want to scan.
73
+ #
74
+ # Returns a new Repeater object.
75
+ def self.scan_for_day_portions(token, options = {})
76
+ scan_for token, RepeaterDayPortion,
77
+ {
78
+ /^ams?$/ => :am,
79
+ /^pms?$/ => :pm,
80
+ /^mornings?$/ => :morning,
81
+ /^afternoons?$/ => :afternoon,
82
+ /^evenings?$/ => :evening,
83
+ /^(night|nite)s?$/ => :night
84
+ }, options
66
85
  end
67
- return nil
68
- end
69
-
70
- def self.scan_for_units(token)
71
- scanner = {/^years?$/ => :year,
72
- /^seasons?$/ => :season,
73
- /^months?$/ => :month,
74
- /^fortnights?$/ => :fortnight,
75
- /^weeks?$/ => :week,
76
- /^weekends?$/ => :weekend,
77
- /^days?$/ => :day,
78
- /^hours?$/ => :hour,
79
- /^minutes?$/ => :minute,
80
- /^seconds?$/ => :second}
81
- scanner.keys.each do |scanner_item|
82
- if scanner_item =~ token.word
83
- klass_name = 'Chronic::Repeater' + scanner[scanner_item].to_s.capitalize
84
- klass = eval(klass_name)
85
- return klass.new(scanner[scanner_item])
86
+
87
+ # token - The Token object we want to scan.
88
+ #
89
+ # Returns a new Repeater object.
90
+ def self.scan_for_times(token, options = {})
91
+ scan_for token, RepeaterTime, /^\d{1,2}(:?\d{1,2})?([\.:]?\d{1,2}([\.:]\d{1,6})?)?$/, options
92
+ end
93
+
94
+ # token - The Token object we want to scan.
95
+ #
96
+ # Returns a new Repeater object.
97
+ def self.scan_for_units(token, options = {})
98
+ {
99
+ /^years?$/ => :year,
100
+ /^seasons?$/ => :season,
101
+ /^months?$/ => :month,
102
+ /^fortnights?$/ => :fortnight,
103
+ /^weeks?$/ => :week,
104
+ /^weekends?$/ => :weekend,
105
+ /^(week|business)days?$/ => :weekday,
106
+ /^days?$/ => :day,
107
+ /^hrs?$/ => :hour,
108
+ /^hours?$/ => :hour,
109
+ /^mins?$/ => :minute,
110
+ /^minutes?$/ => :minute,
111
+ /^secs?$/ => :second,
112
+ /^seconds?$/ => :second
113
+ }.each do |item, symbol|
114
+ if item =~ token.word
115
+ klass_name = 'Repeater' + symbol.to_s.capitalize
116
+ klass = Chronic.const_get(klass_name)
117
+ return klass.new(symbol, options)
118
+ end
86
119
  end
120
+ return nil
121
+ end
122
+
123
+ def <=>(other)
124
+ width <=> other.width
125
+ end
126
+
127
+ # returns the width (in seconds or months) of this repeatable.
128
+ def width
129
+ raise("Repeater#width must be overridden in subclasses")
130
+ end
131
+
132
+ # returns the next occurance of this repeatable.
133
+ def next(pointer)
134
+ raise("Start point must be set before calling #next") unless @now
135
+ end
136
+
137
+ def this(pointer)
138
+ raise("Start point must be set before calling #this") unless @now
139
+ end
140
+
141
+ def to_s
142
+ 'repeater'
87
143
  end
88
- return nil
89
- end
90
-
91
- def <=>(other)
92
- width <=> other.width
93
- end
94
-
95
- # returns the width (in seconds or months) of this repeatable.
96
- def width
97
- raise("Repeatable#width must be overridden in subclasses")
98
- end
99
-
100
- # returns the next occurance of this repeatable.
101
- def next(pointer)
102
- !@now.nil? || raise("Start point must be set before calling #next")
103
- [:future, :none, :past].include?(pointer) || raise("First argument 'pointer' must be one of :past or :future")
104
- #raise("Repeatable#next must be overridden in subclasses")
105
- end
106
-
107
- def this(pointer)
108
- !@now.nil? || raise("Start point must be set before calling #this")
109
- [:future, :past, :none].include?(pointer) || raise("First argument 'pointer' must be one of :past, :future, :none")
110
- end
111
-
112
- def to_s
113
- 'repeater'
114
144
  end
115
- end
145
+ end
@@ -1,47 +1,54 @@
1
- class Chronic::RepeaterDay < Chronic::Repeater #:nodoc:
2
- DAY_SECONDS = 86_400 # (24 * 60 * 60)
3
-
4
- def next(pointer)
5
- super
6
-
7
- if !@current_day_start
8
- @current_day_start = Time.local(@now.year, @now.month, @now.day)
1
+ module Chronic
2
+ class RepeaterDay < Repeater #:nodoc:
3
+ DAY_SECONDS = 86_400 # (24 * 60 * 60)
4
+
5
+ def initialize(type, options = {})
6
+ super
7
+ @current_day_start = nil
9
8
  end
10
-
11
- direction = pointer == :future ? 1 : -1
12
- @current_day_start += direction * DAY_SECONDS
13
-
14
- Chronic::Span.new(@current_day_start, @current_day_start + DAY_SECONDS)
15
- end
16
-
17
- def this(pointer = :future)
18
- super
19
-
20
- case pointer
21
- when :future
22
- day_begin = Time.construct(@now.year, @now.month, @now.day, @now.hour + 1)
23
- day_end = Time.construct(@now.year, @now.month, @now.day) + DAY_SECONDS
24
- when :past
25
- day_begin = Time.construct(@now.year, @now.month, @now.day)
26
- day_end = Time.construct(@now.year, @now.month, @now.day, @now.hour)
27
- when :none
28
- day_begin = Time.construct(@now.year, @now.month, @now.day)
29
- day_end = Time.construct(@now.year, @now.month, @now.day) + DAY_SECONDS
9
+
10
+ def next(pointer)
11
+ super
12
+
13
+ unless @current_day_start
14
+ @current_day_start = Chronic.time_class.local(@now.year, @now.month, @now.day)
15
+ end
16
+
17
+ direction = pointer == :future ? 1 : -1
18
+ @current_day_start += direction * DAY_SECONDS
19
+
20
+ Span.new(@current_day_start, @current_day_start + DAY_SECONDS)
21
+ end
22
+
23
+ def this(pointer = :future)
24
+ super
25
+
26
+ case pointer
27
+ when :future
28
+ day_begin = Chronic.construct(@now.year, @now.month, @now.day, @now.hour)
29
+ day_end = Chronic.construct(@now.year, @now.month, @now.day) + DAY_SECONDS
30
+ when :past
31
+ day_begin = Chronic.construct(@now.year, @now.month, @now.day)
32
+ day_end = Chronic.construct(@now.year, @now.month, @now.day, @now.hour)
33
+ when :none
34
+ day_begin = Chronic.construct(@now.year, @now.month, @now.day)
35
+ day_end = Chronic.construct(@now.year, @now.month, @now.day) + DAY_SECONDS
36
+ end
37
+
38
+ Span.new(day_begin, day_end)
39
+ end
40
+
41
+ def offset(span, amount, pointer)
42
+ direction = pointer == :future ? 1 : -1
43
+ span + direction * amount * DAY_SECONDS
44
+ end
45
+
46
+ def width
47
+ DAY_SECONDS
48
+ end
49
+
50
+ def to_s
51
+ super << '-day'
30
52
  end
31
-
32
- Chronic::Span.new(day_begin, day_end)
33
- end
34
-
35
- def offset(span, amount, pointer)
36
- direction = pointer == :future ? 1 : -1
37
- span + direction * amount * DAY_SECONDS
38
- end
39
-
40
- def width
41
- DAY_SECONDS
42
- end
43
-
44
- def to_s
45
- super << '-day'
46
53
  end
47
- end
54
+ end
@@ -1,46 +1,53 @@
1
- class Chronic::RepeaterDayName < Chronic::Repeater #:nodoc:
2
- DAY_SECONDS = 86400 # (24 * 60 * 60)
3
-
4
- def next(pointer)
5
- super
6
-
7
- direction = pointer == :future ? 1 : -1
8
-
9
- if !@current_day_start
10
- @current_day_start = Time.construct(@now.year, @now.month, @now.day)
11
- @current_day_start += direction * DAY_SECONDS
12
-
13
- day_num = symbol_to_number(@type)
14
-
15
- while @current_day_start.wday != day_num
16
- @current_day_start += direction * DAY_SECONDS
1
+ module Chronic
2
+ class RepeaterDayName < Repeater #:nodoc:
3
+ DAY_SECONDS = 86400 # (24 * 60 * 60)
4
+
5
+ def initialize(type, options = {})
6
+ super
7
+ @current_date = nil
8
+ end
9
+
10
+ def next(pointer)
11
+ super
12
+
13
+ direction = pointer == :future ? 1 : -1
14
+
15
+ unless @current_date
16
+ @current_date = ::Date.new(@now.year, @now.month, @now.day)
17
+ @current_date += direction
18
+
19
+ day_num = symbol_to_number(@type)
20
+
21
+ while @current_date.wday != day_num
22
+ @current_date += direction
23
+ end
24
+ else
25
+ @current_date += direction * 7
17
26
  end
18
- else
19
- @current_day_start += direction * 7 * DAY_SECONDS
27
+ next_date = @current_date.succ
28
+ Span.new(Chronic.construct(@current_date.year, @current_date.month, @current_date.day), Chronic.construct(next_date.year, next_date.month, next_date.day))
29
+ end
30
+
31
+ def this(pointer = :future)
32
+ super
33
+
34
+ pointer = :future if pointer == :none
35
+ self.next(pointer)
36
+ end
37
+
38
+ def width
39
+ DAY_SECONDS
40
+ end
41
+
42
+ def to_s
43
+ super << '-dayname-' << @type.to_s
44
+ end
45
+
46
+ private
47
+
48
+ def symbol_to_number(sym)
49
+ lookup = {:sunday => 0, :monday => 1, :tuesday => 2, :wednesday => 3, :thursday => 4, :friday => 5, :saturday => 6}
50
+ lookup[sym] || raise("Invalid symbol specified")
20
51
  end
21
-
22
- Chronic::Span.new(@current_day_start, @current_day_start + DAY_SECONDS)
23
- end
24
-
25
- def this(pointer = :future)
26
- super
27
-
28
- pointer = :future if pointer == :none
29
- self.next(pointer)
30
- end
31
-
32
- def width
33
- DAY_SECONDS
34
- end
35
-
36
- def to_s
37
- super << '-dayname-' << @type.to_s
38
- end
39
-
40
- private
41
-
42
- def symbol_to_number(sym)
43
- lookup = {:sunday => 0, :monday => 1, :tuesday => 2, :wednesday => 3, :thursday => 4, :friday => 5, :saturday => 6}
44
- lookup[sym] || raise("Invalid symbol specified")
45
52
  end
46
- end
53
+ end
@@ -1,93 +1,109 @@
1
- class Chronic::RepeaterDayPortion < Chronic::Repeater #:nodoc:
2
- @@morning = (6 * 60 * 60)..(12 * 60 * 60) # 6am-12am
3
- @@afternoon = (13 * 60 * 60)..(17 * 60 * 60) # 1pm-5pm
4
- @@evening = (17 * 60 * 60)..(20 * 60 * 60) # 5pm-8pm
5
- @@night = (20 * 60 * 60)..(24 * 60 * 60) # 8pm-12pm
6
-
7
- def initialize(type)
8
- super
9
-
10
- if type.kind_of? Integer
11
- @range = (@type * 60 * 60)..((@type + 12) * 60 * 60)
12
- else
13
- lookup = {:am => 1..(12 * 60 * 60),
14
- :pm => (12 * 60 * 60)..(24 * 60 * 60),
15
- :morning => @@morning,
16
- :afternoon => @@afternoon,
17
- :evening => @@evening,
18
- :night => @@night}
19
- @range = lookup[type]
20
- lookup[type] || raise("Invalid type '#{type}' for RepeaterDayPortion")
1
+ module Chronic
2
+ class RepeaterDayPortion < Repeater #:nodoc:
3
+ PORTIONS = {
4
+ :am => 0..(12 * 60 * 60 - 1),
5
+ :pm => (12 * 60 * 60)..(24 * 60 * 60 - 1),
6
+ :morning => (6 * 60 * 60)..(12 * 60 * 60), # 6am-12am,
7
+ :afternoon => (13 * 60 * 60)..(17 * 60 * 60), # 1pm-5pm,
8
+ :evening => (17 * 60 * 60)..(20 * 60 * 60), # 5pm-8pm,
9
+ :night => (20 * 60 * 60)..(24 * 60 * 60), # 8pm-12pm
10
+ }
11
+
12
+ def initialize(type, options = {})
13
+ super
14
+ @current_span = nil
15
+
16
+ if type.kind_of? Integer
17
+ @range = (@type * 60 * 60)..((@type + 12) * 60 * 60)
18
+ else
19
+ @range = PORTIONS[type]
20
+ @range || raise("Invalid type '#{type}' for RepeaterDayPortion")
21
+ end
22
+
23
+ @range || raise("Range should have been set by now")
21
24
  end
22
- @range || raise("Range should have been set by now")
23
- end
24
-
25
- def next(pointer)
26
- super
27
-
28
- full_day = 60 * 60 * 24
29
-
30
- if !@current_span
31
- now_seconds = @now - Time.construct(@now.year, @now.month, @now.day)
32
- if now_seconds < @range.begin
33
- case pointer
34
- when :future
35
- range_start = Time.construct(@now.year, @now.month, @now.day) + @range.begin
36
- when :past
37
- range_start = Time.construct(@now.year, @now.month, @now.day) - full_day + @range.begin
38
- end
39
- elsif now_seconds > @range.end
40
- case pointer
41
- when :future
42
- range_start = Time.construct(@now.year, @now.month, @now.day) + full_day + @range.begin
43
- when :past
44
- range_start = Time.construct(@now.year, @now.month, @now.day) + @range.begin
25
+
26
+ def next(pointer)
27
+ super
28
+
29
+ unless @current_span
30
+ now_seconds = @now - Chronic.construct(@now.year, @now.month, @now.day)
31
+ if now_seconds < @range.begin
32
+ case pointer
33
+ when :future
34
+ range_start = Chronic.construct(@now.year, @now.month, @now.day) + @range.begin
35
+ when :past
36
+ range_start = Chronic.construct(@now.year, @now.month, @now.day - 1) + @range.begin
37
+ end
38
+ elsif now_seconds > @range.end
39
+ case pointer
40
+ when :future
41
+ range_start = Chronic.construct(@now.year, @now.month, @now.day + 1) + @range.begin
42
+ when :past
43
+ range_start = Chronic.construct(@now.year, @now.month, @now.day) + @range.begin
44
+ end
45
+ else
46
+ case pointer
47
+ when :future
48
+ range_start = Chronic.construct(@now.year, @now.month, @now.day + 1) + @range.begin
49
+ when :past
50
+ range_start = Chronic.construct(@now.year, @now.month, @now.day - 1) + @range.begin
51
+ end
45
52
  end
53
+ offset = (@range.end - @range.begin)
54
+ range_end = construct_date_from_reference_and_offset(range_start, offset)
55
+ @current_span = Span.new(range_start, range_end)
46
56
  else
57
+ days_to_shift_window =
47
58
  case pointer
48
59
  when :future
49
- range_start = Time.construct(@now.year, @now.month, @now.day) + full_day + @range.begin
60
+ 1
50
61
  when :past
51
- range_start = Time.construct(@now.year, @now.month, @now.day) - full_day + @range.begin
62
+ -1
52
63
  end
64
+
65
+ new_begin = Chronic.construct(@current_span.begin.year, @current_span.begin.month, @current_span.begin.day + days_to_shift_window, @current_span.begin.hour, @current_span.begin.min, @current_span.begin.sec)
66
+ new_end = Chronic.construct(@current_span.end.year, @current_span.end.month, @current_span.end.day + days_to_shift_window, @current_span.end.hour, @current_span.end.min, @current_span.end.sec)
67
+ @current_span = Span.new(new_begin, new_end)
53
68
  end
54
-
55
- @current_span = Chronic::Span.new(range_start, range_start + (@range.end - @range.begin))
56
- else
57
- case pointer
58
- when :future
59
- @current_span += full_day
60
- when :past
61
- @current_span -= full_day
69
+ end
70
+
71
+ def this(context = :future)
72
+ super
73
+
74
+ range_start = Chronic.construct(@now.year, @now.month, @now.day) + @range.begin
75
+ range_end = construct_date_from_reference_and_offset(range_start)
76
+ @current_span = Span.new(range_start, range_end)
77
+ end
78
+
79
+ def offset(span, amount, pointer)
80
+ @now = span.begin
81
+ portion_span = self.next(pointer)
82
+ direction = pointer == :future ? 1 : -1
83
+ portion_span + (direction * (amount - 1) * RepeaterDay::DAY_SECONDS)
84
+ end
85
+
86
+ def width
87
+ @range || raise("Range has not been set")
88
+ return @current_span.width if @current_span
89
+ if @type.kind_of? Integer
90
+ return (12 * 60 * 60)
91
+ else
92
+ @range.end - @range.begin
62
93
  end
63
94
  end
64
- end
65
-
66
- def this(context = :future)
67
- super
68
-
69
- range_start = Time.construct(@now.year, @now.month, @now.day) + @range.begin
70
- @current_span = Chronic::Span.new(range_start, range_start + (@range.end - @range.begin))
71
- end
72
-
73
- def offset(span, amount, pointer)
74
- @now = span.begin
75
- portion_span = self.next(pointer)
76
- direction = pointer == :future ? 1 : -1
77
- portion_span + (direction * (amount - 1) * Chronic::RepeaterDay::DAY_SECONDS)
78
- end
79
-
80
- def width
81
- @range || raise("Range has not been set")
82
- return @current_span.width if @current_span
83
- if @type.kind_of? Integer
84
- return (12 * 60 * 60)
85
- else
86
- @range.end - @range.begin
95
+
96
+ def to_s
97
+ super << '-dayportion-' << @type.to_s
98
+ end
99
+
100
+ private
101
+ def construct_date_from_reference_and_offset(reference, offset = nil)
102
+ elapsed_seconds_for_range = offset || (@range.end - @range.begin)
103
+ second_hand = ((elapsed_seconds_for_range - (12 * 60))) % 60
104
+ minute_hand = (elapsed_seconds_for_range - second_hand) / (60) % 60
105
+ hour_hand = (elapsed_seconds_for_range - minute_hand - second_hand) / (60 * 60) + reference.hour % 24
106
+ Chronic.construct(reference.year, reference.month, reference.day, hour_hand, minute_hand, second_hand)
87
107
  end
88
108
  end
89
-
90
- def to_s
91
- super << '-dayportion-' << @type.to_s
92
- end
93
- end
109
+ end