chronic 0.1.5 → 0.6.5

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 (68) hide show
  1. data/.gemtest +0 -0
  2. data/.gitignore +5 -0
  3. data/.yardopts +3 -0
  4. data/HISTORY.md +180 -0
  5. data/LICENSE +21 -0
  6. data/README.md +176 -0
  7. data/Rakefile +39 -32
  8. data/chronic.gemspec +17 -0
  9. data/lib/chronic/chronic.rb +294 -208
  10. data/lib/chronic/grabber.rb +22 -17
  11. data/lib/chronic/handler.rb +90 -0
  12. data/lib/chronic/handlers.rb +365 -278
  13. data/lib/chronic/mini_date.rb +38 -0
  14. data/lib/chronic/numerizer.rb +121 -0
  15. data/lib/chronic/ordinal.rb +23 -19
  16. data/lib/chronic/pointer.rb +20 -17
  17. data/lib/chronic/repeater.rb +126 -105
  18. data/lib/chronic/repeaters/repeater_day.rb +49 -43
  19. data/lib/chronic/repeaters/repeater_day_name.rb +48 -42
  20. data/lib/chronic/repeaters/repeater_day_portion.rb +81 -80
  21. data/lib/chronic/repeaters/repeater_fortnight.rb +59 -53
  22. data/lib/chronic/repeaters/repeater_hour.rb +49 -43
  23. data/lib/chronic/repeaters/repeater_minute.rb +55 -39
  24. data/lib/chronic/repeaters/repeater_month.rb +77 -55
  25. data/lib/chronic/repeaters/repeater_month_name.rb +83 -82
  26. data/lib/chronic/repeaters/repeater_season.rb +107 -21
  27. data/lib/chronic/repeaters/repeater_season_name.rb +41 -22
  28. data/lib/chronic/repeaters/repeater_second.rb +39 -33
  29. data/lib/chronic/repeaters/repeater_time.rb +117 -103
  30. data/lib/chronic/repeaters/repeater_week.rb +63 -57
  31. data/lib/chronic/repeaters/repeater_weekday.rb +85 -0
  32. data/lib/chronic/repeaters/repeater_weekend.rb +64 -9
  33. data/lib/chronic/repeaters/repeater_year.rb +70 -51
  34. data/lib/chronic/scalar.rb +64 -29
  35. data/lib/chronic/season.rb +37 -0
  36. data/lib/chronic/separator.rb +50 -38
  37. data/lib/chronic/span.rb +31 -0
  38. data/lib/chronic/tag.rb +42 -0
  39. data/lib/chronic/time_zone.rb +30 -0
  40. data/lib/chronic/token.rb +45 -0
  41. data/lib/chronic.rb +86 -22
  42. data/test/helper.rb +6 -0
  43. data/test/test_Chronic.rb +118 -20
  44. data/test/test_DaylightSavings.rb +118 -0
  45. data/test/test_Handler.rb +36 -42
  46. data/test/test_MiniDate.rb +32 -0
  47. data/test/test_Numerizer.rb +72 -0
  48. data/test/test_RepeaterDayName.rb +15 -16
  49. data/test/test_RepeaterFortnight.rb +16 -17
  50. data/test/test_RepeaterHour.rb +22 -19
  51. data/test/test_RepeaterMinute.rb +34 -0
  52. data/test/test_RepeaterMonth.rb +20 -17
  53. data/test/test_RepeaterMonthName.rb +17 -18
  54. data/test/test_RepeaterSeason.rb +40 -0
  55. data/test/test_RepeaterTime.rb +20 -22
  56. data/test/test_RepeaterWeek.rb +16 -17
  57. data/test/test_RepeaterWeekday.rb +55 -0
  58. data/test/test_RepeaterWeekend.rb +74 -0
  59. data/test/test_RepeaterYear.rb +24 -18
  60. data/test/test_Span.rb +5 -6
  61. data/test/test_Token.rb +5 -6
  62. data/test/test_parsing.rb +743 -338
  63. metadata +81 -54
  64. data/History.txt +0 -29
  65. data/Manifest.txt +0 -43
  66. data/README.txt +0 -149
  67. data/test/parse_numbers.rb +0 -50
  68. data/test/suite.rb +0 -9
@@ -1,57 +1,79 @@
1
- class Chronic::RepeaterMonth < Chronic::Repeater #:nodoc:
2
- MONTH_SECONDS = 2_592_000 # 30 * 24 * 60 * 60
3
- YEAR_MONTHS = 12
4
-
5
- def next(pointer)
6
- super
7
-
8
- if !@current_month_start
9
- @current_month_start = offset_by(Time.local(@now.year, @now.month), 1, pointer)
10
- else
11
- @current_month_start = offset_by(Time.local(@current_month_start.year, @current_month_start.month), 1, pointer)
12
- end
13
-
14
- Chronic::Span.new(@current_month_start, Time.local(@current_month_start.year, @current_month_start.month + 1))
15
- end
16
-
17
- def this(pointer = :future)
18
- super
19
-
20
- case pointer
21
- when :future
22
- month_start = Time.local(@now.year, @now.month, @now.day + 1)
23
- month_end = self.offset_by(Time.local(@now.year, @now.month), 1, :future)
24
- when :past
25
- month_start = Time.local(@now.year, @now.month)
26
- month_end = Time.local(@now.year, @now.month, @now.day)
27
- when :none
28
- month_start = Time.local(@now.year, @now.month)
29
- month_end = self.offset_by(Time.local(@now.year, @now.month), 1, :future)
30
- end
31
-
32
- Chronic::Span.new(month_start, month_end)
33
- end
34
-
35
- def offset(span, amount, pointer)
36
- Chronic::Span.new(offset_by(span.begin, amount, pointer), offset_by(span.end, amount, pointer))
37
- end
38
-
39
- def offset_by(time, amount, pointer)
40
- direction = pointer == :future ? 1 : -1
41
-
42
- amount_years = direction * amount / YEAR_MONTHS
43
- amount_months = direction * amount % YEAR_MONTHS
44
-
45
- new_year = time.year + amount_years
46
- new_month = time.month + amount_months
47
- if new_month > YEAR_MONTHS
48
- new_year += 1
49
- new_month -= YEAR_MONTHS
50
- end
51
- Time.local(new_year, new_month, time.day, time.hour, time.min, time.sec)
52
- end
53
-
54
- def width
55
- MONTH_SECONDS
1
+ module Chronic
2
+ class RepeaterMonth < Repeater #:nodoc:
3
+ MONTH_SECONDS = 2_592_000 # 30 * 24 * 60 * 60
4
+ YEAR_MONTHS = 12
5
+ MONTH_DAYS = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
6
+ MONTH_DAYS_LEAP = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
7
+
8
+ def initialize(type)
9
+ super
10
+ end
11
+
12
+ def next(pointer)
13
+ super
14
+
15
+ if !@current_month_start
16
+ @current_month_start = offset_by(Chronic.construct(@now.year, @now.month), 1, pointer)
17
+ else
18
+ @current_month_start = offset_by(Chronic.construct(@current_month_start.year, @current_month_start.month), 1, pointer)
19
+ end
20
+
21
+ Span.new(@current_month_start, Chronic.construct(@current_month_start.year, @current_month_start.month + 1))
22
+ end
23
+
24
+ def this(pointer = :future)
25
+ super
26
+
27
+ case pointer
28
+ when :future
29
+ month_start = Chronic.construct(@now.year, @now.month, @now.day + 1)
30
+ month_end = self.offset_by(Chronic.construct(@now.year, @now.month), 1, :future)
31
+ when :past
32
+ month_start = Chronic.construct(@now.year, @now.month)
33
+ month_end = Chronic.construct(@now.year, @now.month, @now.day)
34
+ when :none
35
+ month_start = Chronic.construct(@now.year, @now.month)
36
+ month_end = self.offset_by(Chronic.construct(@now.year, @now.month), 1, :future)
37
+ end
38
+
39
+ Span.new(month_start, month_end)
40
+ end
41
+
42
+ def offset(span, amount, pointer)
43
+ Span.new(offset_by(span.begin, amount, pointer), offset_by(span.end, amount, pointer))
44
+ end
45
+
46
+ def offset_by(time, amount, pointer)
47
+ direction = pointer == :future ? 1 : -1
48
+
49
+ amount_years = direction * amount / YEAR_MONTHS
50
+ amount_months = direction * amount % YEAR_MONTHS
51
+
52
+ new_year = time.year + amount_years
53
+ new_month = time.month + amount_months
54
+ if new_month > YEAR_MONTHS
55
+ new_year += 1
56
+ new_month -= YEAR_MONTHS
57
+ end
58
+
59
+ days = month_days(new_year, new_month)
60
+ new_day = time.day > days ? days : time.day
61
+
62
+ Chronic.construct(new_year, new_month, new_day, time.hour, time.min, time.sec)
63
+ end
64
+
65
+ def width
66
+ MONTH_SECONDS
67
+ end
68
+
69
+ def to_s
70
+ super << '-month'
71
+ end
72
+
73
+ private
74
+
75
+ def month_days(year, month)
76
+ Date.leap?(year) ? MONTH_DAYS_LEAP[month - 1] : MONTH_DAYS[month - 1]
77
+ end
56
78
  end
57
79
  end
@@ -1,93 +1,94 @@
1
- class Chronic::RepeaterMonthName < Chronic::Repeater #:nodoc:
2
- MONTH_SECONDS = 2_592_000 # 30 * 24 * 60 * 60
3
-
4
- def next(pointer)
5
- super
6
-
7
- if !@current_month_begin
8
- target_month = symbol_to_number(@type)
9
- case pointer
10
- when :future
11
- if @now.month < target_month
12
- @current_month_begin = Time.local(@now.year, target_month)
13
- else @now.month > target_month
14
- @current_month_begin = Time.local(@now.year + 1, target_month)
15
- end
16
- when :none
17
- if @now.month <= target_month
18
- @current_month_begin = Time.local(@now.year, target_month)
19
- else @now.month > target_month
20
- @current_month_begin = Time.local(@now.year + 1, target_month)
1
+ module Chronic
2
+ class RepeaterMonthName < Repeater #:nodoc:
3
+ MONTH_SECONDS = 2_592_000 # 30 * 24 * 60 * 60
4
+ MONTHS = {
5
+ :january => 1,
6
+ :february => 2,
7
+ :march => 3,
8
+ :april => 4,
9
+ :may => 5,
10
+ :june => 6,
11
+ :july => 7,
12
+ :august => 8,
13
+ :september => 9,
14
+ :october => 10,
15
+ :november => 11,
16
+ :december => 12
17
+ }
18
+
19
+ def initialize(type)
20
+ super
21
+ end
22
+
23
+ def next(pointer)
24
+ super
25
+
26
+ if !@current_month_begin
27
+ case pointer
28
+ when :future
29
+ if @now.month < index
30
+ @current_month_begin = Chronic.construct(@now.year, index)
31
+ elsif @now.month > index
32
+ @current_month_begin = Chronic.construct(@now.year + 1, index)
33
+ end
34
+ when :none
35
+ if @now.month <= index
36
+ @current_month_begin = Chronic.construct(@now.year, index)
37
+ elsif @now.month > index
38
+ @current_month_begin = Chronic.construct(@now.year + 1, index)
39
+ end
40
+ when :past
41
+ if @now.month >= index
42
+ @current_month_begin = Chronic.construct(@now.year, index)
43
+ elsif @now.month < index
44
+ @current_month_begin = Chronic.construct(@now.year - 1, index)
45
+ end
21
46
  end
22
- when :past
23
- if @now.month > target_month
24
- @current_month_begin = Time.local(@now.year, target_month)
25
- else @now.month < target_month
26
- @current_month_begin = Time.local(@now.year - 1, target_month)
47
+ @current_month_begin || raise("Current month should be set by now")
48
+ else
49
+ case pointer
50
+ when :future
51
+ @current_month_begin = Chronic.construct(@current_month_begin.year + 1, @current_month_begin.month)
52
+ when :past
53
+ @current_month_begin = Chronic.construct(@current_month_begin.year - 1, @current_month_begin.month)
27
54
  end
28
55
  end
29
- @current_month_begin || raise("Current month should be set by now")
30
- else
56
+
57
+ cur_month_year = @current_month_begin.year
58
+ cur_month_month = @current_month_begin.month
59
+
60
+ if cur_month_month == 12
61
+ next_month_year = cur_month_year + 1
62
+ next_month_month = 1
63
+ else
64
+ next_month_year = cur_month_year
65
+ next_month_month = cur_month_month + 1
66
+ end
67
+
68
+ Span.new(@current_month_begin, Chronic.construct(next_month_year, next_month_month))
69
+ end
70
+
71
+ def this(pointer = :future)
72
+ super
73
+
31
74
  case pointer
32
- when :future
33
- @current_month_begin = Time.local(@current_month_begin.year + 1, @current_month_begin.month)
34
75
  when :past
35
- @current_month_begin = Time.local(@current_month_begin.year - 1, @current_month_begin.month)
76
+ self.next(pointer)
77
+ when :future, :none
78
+ self.next(:none)
36
79
  end
37
80
  end
38
-
39
- cur_month_year = @current_month_begin.year
40
- cur_month_month = @current_month_begin.month
41
-
42
- if cur_month_month == 12
43
- next_month_year = cur_month_year + 1
44
- next_month_month = 1
45
- else
46
- next_month_year = cur_month_year
47
- next_month_month = cur_month_month + 1
81
+
82
+ def width
83
+ MONTH_SECONDS
48
84
  end
49
-
50
- Chronic::Span.new(@current_month_begin, Time.local(next_month_year, next_month_month))
51
- end
52
-
53
- def this(pointer = :future)
54
- super
55
-
56
- case pointer
57
- when :past
58
- self.next(pointer)
59
- when :future, :none
60
- self.next(:none)
85
+
86
+ def index
87
+ @index ||= MONTHS[@type]
88
+ end
89
+
90
+ def to_s
91
+ super << '-monthname-' << @type.to_s
61
92
  end
62
- end
63
-
64
- def width
65
- MONTH_SECONDS
66
- end
67
-
68
- def index
69
- symbol_to_number(@type)
70
- end
71
-
72
- def to_s
73
- super << '-month-' << @type.to_s
74
- end
75
-
76
- private
77
-
78
- def symbol_to_number(sym)
79
- lookup = {:january => 1,
80
- :february => 2,
81
- :march => 3,
82
- :april => 4,
83
- :may => 5,
84
- :june => 6,
85
- :july => 7,
86
- :august => 8,
87
- :september => 9,
88
- :october => 10,
89
- :november => 11,
90
- :december => 12}
91
- lookup[sym] || raise("Invalid symbol specified")
92
93
  end
93
94
  end
@@ -1,23 +1,109 @@
1
- class Chronic::RepeaterSeason < Chronic::Repeater #:nodoc:
2
- SEASON_SECONDS = 7_862_400 # 91 * 24 * 60 * 60
3
-
4
- def next(pointer)
5
- super
6
-
7
- raise 'Not implemented'
8
- end
9
-
10
- def this(pointer = :future)
11
- super
12
-
13
- raise 'Not implemented'
14
- end
15
-
16
- def width
17
- SEASON_SECONDS
18
- end
19
-
20
- def to_s
21
- super << '-season'
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
22
108
  end
23
109
  end
@@ -1,24 +1,43 @@
1
- class Chronic::RepeaterSeasonName < Chronic::RepeaterSeason #:nodoc:
2
- @summer = ['jul 21', 'sep 22']
3
- @autumn = ['sep 23', 'dec 21']
4
- @winter = ['dec 22', 'mar 19']
5
- @spring = ['mar 20', 'jul 20']
6
-
7
- def next(pointer)
8
- super
9
- raise 'Not implemented'
10
- end
11
-
12
- def this(pointer = :future)
13
- super
14
- raise 'Not implemented'
15
- end
16
-
17
- def width
18
- (91 * 24 * 60 * 60)
19
- end
20
-
21
- def to_s
22
- super << '-season-' << @type.to_s
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
+
23
42
  end
24
43
  end
@@ -1,36 +1,42 @@
1
- class Chronic::RepeaterSecond < Chronic::Repeater #:nodoc:
2
- SECOND_SECONDS = 1 # haha, awesome
3
-
4
- def next(pointer = :future)
5
- super
6
-
7
- direction = pointer == :future ? 1 : -1
8
-
9
- if !@second_start
10
- @second_start = @now + (direction * SECOND_SECONDS)
11
- else
12
- @second_start += SECOND_SECONDS * direction
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'
13
40
  end
14
-
15
- Chronic::Span.new(@second_start, @second_start + SECOND_SECONDS)
16
- end
17
-
18
- def this(pointer = :future)
19
- super
20
-
21
- Chronic::Span.new(@now, @now + 1)
22
- end
23
-
24
- def offset(span, amount, pointer)
25
- direction = pointer == :future ? 1 : -1
26
- span + direction * amount * SECOND_SECONDS
27
- end
28
-
29
- def width
30
- SECOND_SECONDS
31
- end
32
-
33
- def to_s
34
- super << '-second'
35
41
  end
36
42
  end