chronic-mmlac 0.6.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/.gemtest +0 -0
  3. data/.gitignore +5 -0
  4. data/.yardopts +3 -0
  5. data/HISTORY.md +174 -0
  6. data/LICENSE +21 -0
  7. data/README.md +177 -0
  8. data/Rakefile +46 -0
  9. data/chronic.gemspec +17 -0
  10. data/lib/chronic/chronic.rb +325 -0
  11. data/lib/chronic/grabber.rb +31 -0
  12. data/lib/chronic/handler.rb +90 -0
  13. data/lib/chronic/handlers.rb +465 -0
  14. data/lib/chronic/mini_date.rb +38 -0
  15. data/lib/chronic/numerizer.rb +121 -0
  16. data/lib/chronic/ordinal.rb +44 -0
  17. data/lib/chronic/pointer.rb +30 -0
  18. data/lib/chronic/repeater.rb +135 -0
  19. data/lib/chronic/repeaters/repeater_day.rb +53 -0
  20. data/lib/chronic/repeaters/repeater_day_name.rb +52 -0
  21. data/lib/chronic/repeaters/repeater_day_portion.rb +94 -0
  22. data/lib/chronic/repeaters/repeater_fortnight.rb +71 -0
  23. data/lib/chronic/repeaters/repeater_hour.rb +58 -0
  24. data/lib/chronic/repeaters/repeater_minute.rb +58 -0
  25. data/lib/chronic/repeaters/repeater_month.rb +79 -0
  26. data/lib/chronic/repeaters/repeater_month_name.rb +94 -0
  27. data/lib/chronic/repeaters/repeater_season.rb +109 -0
  28. data/lib/chronic/repeaters/repeater_season_name.rb +43 -0
  29. data/lib/chronic/repeaters/repeater_second.rb +42 -0
  30. data/lib/chronic/repeaters/repeater_time.rb +128 -0
  31. data/lib/chronic/repeaters/repeater_week.rb +74 -0
  32. data/lib/chronic/repeaters/repeater_weekday.rb +85 -0
  33. data/lib/chronic/repeaters/repeater_weekend.rb +66 -0
  34. data/lib/chronic/repeaters/repeater_year.rb +77 -0
  35. data/lib/chronic/scalar.rb +109 -0
  36. data/lib/chronic/season.rb +37 -0
  37. data/lib/chronic/separator.rb +88 -0
  38. data/lib/chronic/span.rb +31 -0
  39. data/lib/chronic/tag.rb +42 -0
  40. data/lib/chronic/time_zone.rb +30 -0
  41. data/lib/chronic/token.rb +45 -0
  42. data/lib/chronic.rb +118 -0
  43. data/test/helper.rb +6 -0
  44. data/test/test_Chronic.rb +148 -0
  45. data/test/test_DaylightSavings.rb +118 -0
  46. data/test/test_Handler.rb +104 -0
  47. data/test/test_MiniDate.rb +32 -0
  48. data/test/test_Numerizer.rb +72 -0
  49. data/test/test_RepeaterDayName.rb +51 -0
  50. data/test/test_RepeaterFortnight.rb +62 -0
  51. data/test/test_RepeaterHour.rb +68 -0
  52. data/test/test_RepeaterMinute.rb +34 -0
  53. data/test/test_RepeaterMonth.rb +50 -0
  54. data/test/test_RepeaterMonthName.rb +56 -0
  55. data/test/test_RepeaterSeason.rb +40 -0
  56. data/test/test_RepeaterTime.rb +70 -0
  57. data/test/test_RepeaterWeek.rb +62 -0
  58. data/test/test_RepeaterWeekday.rb +55 -0
  59. data/test/test_RepeaterWeekend.rb +74 -0
  60. data/test/test_RepeaterYear.rb +69 -0
  61. data/test/test_Span.rb +23 -0
  62. data/test/test_Token.rb +25 -0
  63. data/test/test_parsing.rb +886 -0
  64. metadata +132 -0
@@ -0,0 +1,109 @@
1
+ module Chronic
2
+ class RepeaterSeason < Repeater #:nodoc:
3
+ SEASON_SECONDS = 7_862_400 # 91 * 24 * 60 * 60
4
+ SEASONS = {
5
+ :spring => Season.new(MiniDate.new(3,20), MiniDate.new(6,20)),
6
+ :summer => Season.new(MiniDate.new(6,21), MiniDate.new(9,22)),
7
+ :autumn => Season.new(MiniDate.new(9,23), MiniDate.new(12,21)),
8
+ :winter => Season.new(MiniDate.new(12,22), MiniDate.new(3,19))
9
+ }
10
+
11
+ def initialize(type)
12
+ super
13
+ end
14
+
15
+ def next(pointer)
16
+ super
17
+
18
+ direction = pointer == :future ? 1 : -1
19
+ next_season = Season.find_next_season(find_current_season(MiniDate.from_time(@now)), direction)
20
+
21
+ find_next_season_span(direction, next_season)
22
+ end
23
+
24
+ def this(pointer = :future)
25
+ super
26
+
27
+ direction = pointer == :future ? 1 : -1
28
+
29
+ today = Chronic.construct(@now.year, @now.month, @now.day)
30
+ this_ssn = find_current_season(MiniDate.from_time(@now))
31
+ case pointer
32
+ when :past
33
+ this_ssn_start = today + direction * num_seconds_til_start(this_ssn, direction)
34
+ this_ssn_end = today
35
+ when :future
36
+ this_ssn_start = today + RepeaterDay::DAY_SECONDS
37
+ this_ssn_end = today + direction * num_seconds_til_end(this_ssn, direction)
38
+ when :none
39
+ this_ssn_start = today + direction * num_seconds_til_start(this_ssn, direction)
40
+ this_ssn_end = today + direction * num_seconds_til_end(this_ssn, direction)
41
+ end
42
+
43
+ construct_season(this_ssn_start, this_ssn_end)
44
+ end
45
+
46
+ def offset(span, amount, pointer)
47
+ Span.new(offset_by(span.begin, amount, pointer), offset_by(span.end, amount, pointer))
48
+ end
49
+
50
+ def offset_by(time, amount, pointer)
51
+ direction = pointer == :future ? 1 : -1
52
+ time + amount * direction * SEASON_SECONDS
53
+ end
54
+
55
+ def width
56
+ SEASON_SECONDS
57
+ end
58
+
59
+ def to_s
60
+ super << '-season'
61
+ end
62
+
63
+ private
64
+
65
+ def find_next_season_span(direction, next_season)
66
+ unless @next_season_start or @next_season_end
67
+ @next_season_start = Chronic.construct(@now.year, @now.month, @now.day)
68
+ @next_season_end = Chronic.construct(@now.year, @now.month, @now.day)
69
+ end
70
+
71
+ @next_season_start += direction * num_seconds_til_start(next_season, direction)
72
+ @next_season_end += direction * num_seconds_til_end(next_season, direction)
73
+
74
+ construct_season(@next_season_start, @next_season_end)
75
+ end
76
+
77
+ def find_current_season(md)
78
+ [:spring, :summer, :autumn, :winter].find do |season|
79
+ md.is_between?(SEASONS[season].start, SEASONS[season].end)
80
+ end
81
+ end
82
+
83
+ def num_seconds_til(goal, direction)
84
+ start = Chronic.construct(@now.year, @now.month, @now.day)
85
+ seconds = 0
86
+
87
+ until MiniDate.from_time(start + direction * seconds).equals?(goal)
88
+ seconds += RepeaterDay::DAY_SECONDS
89
+ end
90
+
91
+ seconds
92
+ end
93
+
94
+ def num_seconds_til_start(season_symbol, direction)
95
+ num_seconds_til(SEASONS[season_symbol].start, direction)
96
+ end
97
+
98
+ def num_seconds_til_end(season_symbol, direction)
99
+ num_seconds_til(SEASONS[season_symbol].end, direction)
100
+ end
101
+
102
+ def construct_season(start, finish)
103
+ Span.new(
104
+ Chronic.construct(start.year, start.month, start.day),
105
+ Chronic.construct(finish.year, finish.month, finish.day)
106
+ )
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,43 @@
1
+ module Chronic
2
+ class RepeaterSeasonName < RepeaterSeason #:nodoc:
3
+ SEASON_SECONDS = 7_862_400 # 91 * 24 * 60 * 60
4
+ DAY_SECONDS = 86_400 # (24 * 60 * 60)
5
+
6
+ def next(pointer)
7
+ direction = pointer == :future ? 1 : -1
8
+ find_next_season_span(direction, @type)
9
+ end
10
+
11
+ def this(pointer = :future)
12
+ direction = pointer == :future ? 1 : -1
13
+
14
+ today = Chronic.construct(@now.year, @now.month, @now.day)
15
+ goal_ssn_start = today + direction * num_seconds_til_start(@type, direction)
16
+ goal_ssn_end = today + direction * num_seconds_til_end(@type, direction)
17
+ curr_ssn = find_current_season(MiniDate.from_time(@now))
18
+ case pointer
19
+ when :past
20
+ this_ssn_start = goal_ssn_start
21
+ this_ssn_end = (curr_ssn == @type) ? today : goal_ssn_end
22
+ when :future
23
+ this_ssn_start = (curr_ssn == @type) ? today + RepeaterDay::DAY_SECONDS : goal_ssn_start
24
+ this_ssn_end = goal_ssn_end
25
+ when :none
26
+ this_ssn_start = goal_ssn_start
27
+ this_ssn_end = goal_ssn_end
28
+ end
29
+
30
+ construct_season(this_ssn_start, this_ssn_end)
31
+ end
32
+
33
+ def offset(span, amount, pointer)
34
+ Span.new(offset_by(span.begin, amount, pointer), offset_by(span.end, amount, pointer))
35
+ end
36
+
37
+ def offset_by(time, amount, pointer)
38
+ direction = pointer == :future ? 1 : -1
39
+ time + amount * direction * RepeaterYear::YEAR_SECONDS
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,42 @@
1
+ module Chronic
2
+ class RepeaterSecond < Repeater #:nodoc:
3
+ SECOND_SECONDS = 1 # haha, awesome
4
+
5
+ def initialize(type)
6
+ super
7
+ end
8
+
9
+ def next(pointer = :future)
10
+ super
11
+
12
+ direction = pointer == :future ? 1 : -1
13
+
14
+ if !@second_start
15
+ @second_start = @now + (direction * SECOND_SECONDS)
16
+ else
17
+ @second_start += SECOND_SECONDS * direction
18
+ end
19
+
20
+ Span.new(@second_start, @second_start + SECOND_SECONDS)
21
+ end
22
+
23
+ def this(pointer = :future)
24
+ super
25
+
26
+ Span.new(@now, @now + 1)
27
+ end
28
+
29
+ def offset(span, amount, pointer)
30
+ direction = pointer == :future ? 1 : -1
31
+ span + direction * amount * SECOND_SECONDS
32
+ end
33
+
34
+ def width
35
+ SECOND_SECONDS
36
+ end
37
+
38
+ def to_s
39
+ super << '-second'
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,128 @@
1
+ module Chronic
2
+ class RepeaterTime < Repeater #:nodoc:
3
+ class Tick #:nodoc:
4
+ attr_accessor :time
5
+
6
+ def initialize(time, ambiguous = false)
7
+ @time = time
8
+ @ambiguous = ambiguous
9
+ end
10
+
11
+ def ambiguous?
12
+ @ambiguous
13
+ end
14
+
15
+ def *(other)
16
+ Tick.new(@time * other, @ambiguous)
17
+ end
18
+
19
+ def to_f
20
+ @time.to_f
21
+ end
22
+
23
+ def to_s
24
+ @time.to_s + (@ambiguous ? '?' : '')
25
+ end
26
+
27
+ end
28
+
29
+ def initialize(time)
30
+ t = time.gsub(/\:/, '')
31
+
32
+ @type =
33
+ case t.size
34
+ when 1..2
35
+ hours = t.to_i
36
+ Tick.new((hours == 12 ? 0 : hours) * 60 * 60, true)
37
+ when 3
38
+ hours = t[0..0].to_i
39
+ ambiguous = hours > 0
40
+ Tick.new((hours * 60 * 60) + (t[1..2].to_i * 60), ambiguous)
41
+ when 4
42
+ ambiguous = time =~ /:/ && t[0..0].to_i != 0 && t[0..1].to_i <= 12
43
+ hours = t[0..1].to_i
44
+ hours == 12 ? Tick.new(0 * 60 * 60 + t[2..3].to_i * 60, ambiguous) : Tick.new(hours * 60 * 60 + t[2..3].to_i * 60, ambiguous)
45
+ when 5
46
+ Tick.new(t[0..0].to_i * 60 * 60 + t[1..2].to_i * 60 + t[3..4].to_i, true)
47
+ when 6
48
+ ambiguous = time =~ /:/ && t[0..0].to_i != 0 && t[0..1].to_i <= 12
49
+ hours = t[0..1].to_i
50
+ hours == 12 ? Tick.new(0 * 60 * 60 + t[2..3].to_i * 60 + t[4..5].to_i, ambiguous) : Tick.new(hours * 60 * 60 + t[2..3].to_i * 60 + t[4..5].to_i, ambiguous)
51
+ else
52
+ raise("Time cannot exceed six digits")
53
+ end
54
+ end
55
+
56
+ # Return the next past or future Span for the time that this Repeater represents
57
+ # pointer - Symbol representing which temporal direction to fetch the next day
58
+ # must be either :past or :future
59
+ def next(pointer)
60
+ super
61
+
62
+ half_day = 60 * 60 * 12
63
+ full_day = 60 * 60 * 24
64
+
65
+ first = false
66
+
67
+ unless @current_time
68
+ first = true
69
+ midnight = Chronic.time_class.local(@now.year, @now.month, @now.day)
70
+
71
+ yesterday_midnight = midnight - full_day
72
+ tomorrow_midnight = midnight + full_day
73
+
74
+ offset_fix = midnight.gmt_offset - tomorrow_midnight.gmt_offset
75
+ tomorrow_midnight += offset_fix
76
+
77
+ catch :done do
78
+ if pointer == :future
79
+ if @type.ambiguous?
80
+ [midnight + @type.time + offset_fix, midnight + half_day + @type.time + offset_fix, tomorrow_midnight + @type.time].each do |t|
81
+ (@current_time = t; throw :done) if t >= @now
82
+ end
83
+ else
84
+ [midnight + @type.time + offset_fix, tomorrow_midnight + @type.time].each do |t|
85
+ (@current_time = t; throw :done) if t >= @now
86
+ end
87
+ end
88
+ else # pointer == :past
89
+ if @type.ambiguous?
90
+ [midnight + half_day + @type.time + offset_fix, midnight + @type.time + offset_fix, yesterday_midnight + @type.time + half_day].each do |t|
91
+ (@current_time = t; throw :done) if t <= @now
92
+ end
93
+ else
94
+ [midnight + @type.time + offset_fix, yesterday_midnight + @type.time].each do |t|
95
+ (@current_time = t; throw :done) if t <= @now
96
+ end
97
+ end
98
+ end
99
+ end
100
+
101
+ @current_time || raise("Current time cannot be nil at this point")
102
+ end
103
+
104
+ unless first
105
+ increment = @type.ambiguous? ? half_day : full_day
106
+ @current_time += pointer == :future ? increment : -increment
107
+ end
108
+
109
+ Span.new(@current_time, @current_time + width)
110
+ end
111
+
112
+ def this(context = :future)
113
+ super
114
+
115
+ context = :future if context == :none
116
+
117
+ self.next(context)
118
+ end
119
+
120
+ def width
121
+ 1
122
+ end
123
+
124
+ def to_s
125
+ super << '-time-' << @type.to_s
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,74 @@
1
+ module Chronic
2
+ class RepeaterWeek < Repeater #:nodoc:
3
+ WEEK_SECONDS = 604800 # (7 * 24 * 60 * 60)
4
+
5
+ def initialize(type)
6
+ super
7
+ end
8
+
9
+ def next(pointer)
10
+ super
11
+
12
+ if !@current_week_start
13
+ case pointer
14
+ when :future
15
+ sunday_repeater = RepeaterDayName.new(:sunday)
16
+ sunday_repeater.start = @now
17
+ next_sunday_span = sunday_repeater.next(:future)
18
+ @current_week_start = next_sunday_span.begin
19
+ when :past
20
+ sunday_repeater = RepeaterDayName.new(:sunday)
21
+ sunday_repeater.start = (@now + RepeaterDay::DAY_SECONDS)
22
+ sunday_repeater.next(:past)
23
+ last_sunday_span = sunday_repeater.next(:past)
24
+ @current_week_start = last_sunday_span.begin
25
+ end
26
+ else
27
+ direction = pointer == :future ? 1 : -1
28
+ @current_week_start += direction * WEEK_SECONDS
29
+ end
30
+
31
+ Span.new(@current_week_start, @current_week_start + WEEK_SECONDS)
32
+ end
33
+
34
+ def this(pointer = :future)
35
+ super
36
+
37
+ case pointer
38
+ when :future
39
+ this_week_start = Chronic.time_class.local(@now.year, @now.month, @now.day, @now.hour) + RepeaterHour::HOUR_SECONDS
40
+ sunday_repeater = RepeaterDayName.new(:sunday)
41
+ sunday_repeater.start = @now
42
+ this_sunday_span = sunday_repeater.this(:future)
43
+ this_week_end = this_sunday_span.begin
44
+ Span.new(this_week_start, this_week_end)
45
+ when :past
46
+ this_week_end = Chronic.time_class.local(@now.year, @now.month, @now.day, @now.hour)
47
+ sunday_repeater = RepeaterDayName.new(:sunday)
48
+ sunday_repeater.start = @now
49
+ last_sunday_span = sunday_repeater.next(:past)
50
+ this_week_start = last_sunday_span.begin
51
+ Span.new(this_week_start, this_week_end)
52
+ when :none
53
+ sunday_repeater = RepeaterDayName.new(:sunday)
54
+ sunday_repeater.start = @now
55
+ last_sunday_span = sunday_repeater.next(:past)
56
+ this_week_start = last_sunday_span.begin
57
+ Span.new(this_week_start, this_week_start + WEEK_SECONDS)
58
+ end
59
+ end
60
+
61
+ def offset(span, amount, pointer)
62
+ direction = pointer == :future ? 1 : -1
63
+ span + direction * amount * WEEK_SECONDS
64
+ end
65
+
66
+ def width
67
+ WEEK_SECONDS
68
+ end
69
+
70
+ def to_s
71
+ super << '-week'
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,85 @@
1
+ module Chronic
2
+ class RepeaterWeekday < Repeater #:nodoc:
3
+ DAY_SECONDS = 86400 # (24 * 60 * 60)
4
+ DAYS = {
5
+ :sunday => 0,
6
+ :monday => 1,
7
+ :tuesday => 2,
8
+ :wednesday => 3,
9
+ :thursday => 4,
10
+ :friday => 5,
11
+ :saturday => 6
12
+ }
13
+
14
+ def initialize(type)
15
+ super
16
+ end
17
+
18
+ def next(pointer)
19
+ super
20
+
21
+ direction = pointer == :future ? 1 : -1
22
+
23
+ if !@current_weekday_start
24
+ @current_weekday_start = Chronic.construct(@now.year, @now.month, @now.day)
25
+ @current_weekday_start += direction * DAY_SECONDS
26
+
27
+ until is_weekday?(@current_weekday_start.wday)
28
+ @current_weekday_start += direction * DAY_SECONDS
29
+ end
30
+ else
31
+ loop do
32
+ @current_weekday_start += direction * DAY_SECONDS
33
+ break if is_weekday?(@current_weekday_start.wday)
34
+ end
35
+ end
36
+
37
+ Span.new(@current_weekday_start, @current_weekday_start + DAY_SECONDS)
38
+ end
39
+
40
+ def this(pointer = :future)
41
+ super
42
+
43
+ case pointer
44
+ when :past
45
+ self.next(:past)
46
+ when :future, :none
47
+ self.next(:future)
48
+ end
49
+ end
50
+
51
+ def offset(span, amount, pointer)
52
+ direction = pointer == :future ? 1 : -1
53
+
54
+ num_weekdays_passed = 0; offset = 0
55
+ until num_weekdays_passed == amount
56
+ offset += direction * DAY_SECONDS
57
+ num_weekdays_passed += 1 if is_weekday?((span.begin+offset).wday)
58
+ end
59
+
60
+ span + offset
61
+ end
62
+
63
+ def width
64
+ DAY_SECONDS
65
+ end
66
+
67
+ def to_s
68
+ super << '-weekday'
69
+ end
70
+
71
+ private
72
+
73
+ def is_weekend?(day)
74
+ day == symbol_to_number(:saturday) || day == symbol_to_number(:sunday)
75
+ end
76
+
77
+ def is_weekday?(day)
78
+ !is_weekend?(day)
79
+ end
80
+
81
+ def symbol_to_number(sym)
82
+ DAYS[sym] || raise("Invalid symbol specified")
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,66 @@
1
+ module Chronic
2
+ class RepeaterWeekend < Repeater #:nodoc:
3
+ WEEKEND_SECONDS = 172_800 # (2 * 24 * 60 * 60)
4
+
5
+ def initialize(type)
6
+ super
7
+ end
8
+
9
+ def next(pointer)
10
+ super
11
+
12
+ if !@current_week_start
13
+ case pointer
14
+ when :future
15
+ saturday_repeater = RepeaterDayName.new(:saturday)
16
+ saturday_repeater.start = @now
17
+ next_saturday_span = saturday_repeater.next(:future)
18
+ @current_week_start = next_saturday_span.begin
19
+ when :past
20
+ saturday_repeater = RepeaterDayName.new(:saturday)
21
+ saturday_repeater.start = (@now + RepeaterDay::DAY_SECONDS)
22
+ last_saturday_span = saturday_repeater.next(:past)
23
+ @current_week_start = last_saturday_span.begin
24
+ end
25
+ else
26
+ direction = pointer == :future ? 1 : -1
27
+ @current_week_start += direction * RepeaterWeek::WEEK_SECONDS
28
+ end
29
+
30
+ Span.new(@current_week_start, @current_week_start + WEEKEND_SECONDS)
31
+ end
32
+
33
+ def this(pointer = :future)
34
+ super
35
+
36
+ case pointer
37
+ when :future, :none
38
+ saturday_repeater = RepeaterDayName.new(:saturday)
39
+ saturday_repeater.start = @now
40
+ this_saturday_span = saturday_repeater.this(:future)
41
+ Span.new(this_saturday_span.begin, this_saturday_span.begin + WEEKEND_SECONDS)
42
+ when :past
43
+ saturday_repeater = RepeaterDayName.new(:saturday)
44
+ saturday_repeater.start = @now
45
+ last_saturday_span = saturday_repeater.this(:past)
46
+ Span.new(last_saturday_span.begin, last_saturday_span.begin + WEEKEND_SECONDS)
47
+ end
48
+ end
49
+
50
+ def offset(span, amount, pointer)
51
+ direction = pointer == :future ? 1 : -1
52
+ weekend = RepeaterWeekend.new(:weekend)
53
+ weekend.start = span.begin
54
+ start = weekend.next(pointer).begin + (amount - 1) * direction * RepeaterWeek::WEEK_SECONDS
55
+ Span.new(start, start + (span.end - span.begin))
56
+ end
57
+
58
+ def width
59
+ WEEKEND_SECONDS
60
+ end
61
+
62
+ def to_s
63
+ super << '-weekend'
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,77 @@
1
+ module Chronic
2
+ class RepeaterYear < Repeater #:nodoc:
3
+ YEAR_SECONDS = 31536000 # 365 * 24 * 60 * 60
4
+
5
+ def initialize(type)
6
+ super
7
+ end
8
+
9
+ def next(pointer)
10
+ super
11
+
12
+ if !@current_year_start
13
+ case pointer
14
+ when :future
15
+ @current_year_start = Chronic.construct(@now.year + 1)
16
+ when :past
17
+ @current_year_start = Chronic.construct(@now.year - 1)
18
+ end
19
+ else
20
+ diff = pointer == :future ? 1 : -1
21
+ @current_year_start = Chronic.construct(@current_year_start.year + diff)
22
+ end
23
+
24
+ Span.new(@current_year_start, Chronic.construct(@current_year_start.year + 1))
25
+ end
26
+
27
+ def this(pointer = :future)
28
+ super
29
+
30
+ case pointer
31
+ when :future
32
+ this_year_start = Chronic.construct(@now.year, @now.month, @now.day + 1)
33
+ this_year_end = Chronic.construct(@now.year + 1, 1, 1)
34
+ when :past
35
+ this_year_start = Chronic.construct(@now.year, 1, 1)
36
+ this_year_end = Chronic.construct(@now.year, @now.month, @now.day)
37
+ when :none
38
+ this_year_start = Chronic.construct(@now.year, 1, 1)
39
+ this_year_end = Chronic.construct(@now.year + 1, 1, 1)
40
+ end
41
+
42
+ Span.new(this_year_start, this_year_end)
43
+ end
44
+
45
+ def offset(span, amount, pointer)
46
+ direction = pointer == :future ? 1 : -1
47
+ new_begin = build_offset_time(span.begin, amount, direction)
48
+ new_end = build_offset_time(span.end, amount, direction)
49
+ Span.new(new_begin, new_end)
50
+ end
51
+
52
+ def width
53
+ YEAR_SECONDS
54
+ end
55
+
56
+ def to_s
57
+ super << '-year'
58
+ end
59
+
60
+ private
61
+
62
+ def build_offset_time(time, amount, direction)
63
+ year = time.year + (amount * direction)
64
+ days = month_days(year, time.month)
65
+ day = time.day > days ? days : time.day
66
+ Chronic.construct(year, time.month, day, time.hour, time.min, time.sec)
67
+ end
68
+
69
+ def month_days(year, month)
70
+ if Date.leap?(year)
71
+ RepeaterMonth::MONTH_DAYS_LEAP[month - 1]
72
+ else
73
+ RepeaterMonth::MONTH_DAYS[month - 1]
74
+ end
75
+ end
76
+ end
77
+ end