chronic_2011 0.1.0

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 (62) hide show
  1. data/.gitignore +6 -0
  2. data/HISTORY.md +4 -0
  3. data/LICENSE +21 -0
  4. data/README.md +180 -0
  5. data/Rakefile +46 -0
  6. data/chronic.gemspec +18 -0
  7. data/lib/chronic.rb +117 -0
  8. data/lib/chronic/chronic.rb +346 -0
  9. data/lib/chronic/grabber.rb +33 -0
  10. data/lib/chronic/handler.rb +88 -0
  11. data/lib/chronic/handlers.rb +553 -0
  12. data/lib/chronic/mini_date.rb +38 -0
  13. data/lib/chronic/numerizer.rb +121 -0
  14. data/lib/chronic/ordinal.rb +47 -0
  15. data/lib/chronic/pointer.rb +32 -0
  16. data/lib/chronic/repeater.rb +142 -0
  17. data/lib/chronic/repeaters/repeater_day.rb +53 -0
  18. data/lib/chronic/repeaters/repeater_day_name.rb +52 -0
  19. data/lib/chronic/repeaters/repeater_day_portion.rb +108 -0
  20. data/lib/chronic/repeaters/repeater_fortnight.rb +71 -0
  21. data/lib/chronic/repeaters/repeater_hour.rb +58 -0
  22. data/lib/chronic/repeaters/repeater_minute.rb +58 -0
  23. data/lib/chronic/repeaters/repeater_month.rb +79 -0
  24. data/lib/chronic/repeaters/repeater_month_name.rb +94 -0
  25. data/lib/chronic/repeaters/repeater_season.rb +109 -0
  26. data/lib/chronic/repeaters/repeater_season_name.rb +43 -0
  27. data/lib/chronic/repeaters/repeater_second.rb +42 -0
  28. data/lib/chronic/repeaters/repeater_time.rb +128 -0
  29. data/lib/chronic/repeaters/repeater_week.rb +74 -0
  30. data/lib/chronic/repeaters/repeater_weekday.rb +85 -0
  31. data/lib/chronic/repeaters/repeater_weekend.rb +66 -0
  32. data/lib/chronic/repeaters/repeater_year.rb +77 -0
  33. data/lib/chronic/scalar.rb +116 -0
  34. data/lib/chronic/season.rb +26 -0
  35. data/lib/chronic/separator.rb +94 -0
  36. data/lib/chronic/span.rb +31 -0
  37. data/lib/chronic/tag.rb +36 -0
  38. data/lib/chronic/time_zone.rb +32 -0
  39. data/lib/chronic/token.rb +47 -0
  40. data/test/helper.rb +12 -0
  41. data/test/test_chronic.rb +148 -0
  42. data/test/test_daylight_savings.rb +118 -0
  43. data/test/test_handler.rb +104 -0
  44. data/test/test_mini_date.rb +32 -0
  45. data/test/test_numerizer.rb +72 -0
  46. data/test/test_parsing.rb +977 -0
  47. data/test/test_repeater_day_name.rb +51 -0
  48. data/test/test_repeater_day_portion.rb +254 -0
  49. data/test/test_repeater_fortnight.rb +62 -0
  50. data/test/test_repeater_hour.rb +68 -0
  51. data/test/test_repeater_minute.rb +34 -0
  52. data/test/test_repeater_month.rb +50 -0
  53. data/test/test_repeater_month_name.rb +56 -0
  54. data/test/test_repeater_season.rb +40 -0
  55. data/test/test_repeater_time.rb +70 -0
  56. data/test/test_repeater_week.rb +62 -0
  57. data/test/test_repeater_weekday.rb +55 -0
  58. data/test/test_repeater_weekend.rb +74 -0
  59. data/test/test_repeater_year.rb +69 -0
  60. data/test/test_span.rb +23 -0
  61. data/test/test_token.rb +25 -0
  62. metadata +156 -0
@@ -0,0 +1,108 @@
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)
13
+ super
14
+
15
+ if type.kind_of? Integer
16
+ @range = (@type * 60 * 60)..((@type + 12) * 60 * 60)
17
+ else
18
+ @range = PORTIONS[type]
19
+ @range || raise("Invalid type '#{type}' for RepeaterDayPortion")
20
+ end
21
+
22
+ @range || raise("Range should have been set by now")
23
+ end
24
+
25
+ def next(pointer)
26
+ super
27
+
28
+ if !@current_span
29
+ now_seconds = @now - Chronic.construct(@now.year, @now.month, @now.day)
30
+ if now_seconds < @range.begin
31
+ case pointer
32
+ when :future
33
+ range_start = Chronic.construct(@now.year, @now.month, @now.day) + @range.begin
34
+ when :past
35
+ range_start = Chronic.construct(@now.year, @now.month, @now.day - 1) + @range.begin
36
+ end
37
+ elsif now_seconds > @range.end
38
+ case pointer
39
+ when :future
40
+ range_start = Chronic.construct(@now.year, @now.month, @now.day + 1) + @range.begin
41
+ when :past
42
+ range_start = Chronic.construct(@now.year, @now.month, @now.day) + @range.begin
43
+ end
44
+ else
45
+ case pointer
46
+ when :future
47
+ range_start = Chronic.construct(@now.year, @now.month, @now.day + 1) + @range.begin
48
+ when :past
49
+ range_start = Chronic.construct(@now.year, @now.month, @now.day - 1) + @range.begin
50
+ end
51
+ end
52
+ offset = (@range.end - @range.begin)
53
+ range_end = construct_date_from_reference_and_offset(range_start, offset)
54
+ @current_span = Span.new(range_start, range_end)
55
+ else
56
+ days_to_shift_window =
57
+ case pointer
58
+ when :future
59
+ 1
60
+ when :past
61
+ -1
62
+ end
63
+
64
+ 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)
65
+ 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)
66
+ @current_span = Span.new(new_begin, new_end)
67
+ end
68
+ end
69
+
70
+ def this(context = :future)
71
+ super
72
+
73
+ range_start = Chronic.construct(@now.year, @now.month, @now.day) + @range.begin
74
+ range_end = construct_date_from_reference_and_offset(range_start)
75
+ @current_span = Span.new(range_start, range_end)
76
+ end
77
+
78
+ def offset(span, amount, pointer)
79
+ @now = span.begin
80
+ portion_span = self.next(pointer)
81
+ direction = pointer == :future ? 1 : -1
82
+ portion_span + (direction * (amount - 1) * RepeaterDay::DAY_SECONDS)
83
+ end
84
+
85
+ def width
86
+ @range || raise("Range has not been set")
87
+ return @current_span.width if @current_span
88
+ if @type.kind_of? Integer
89
+ return (12 * 60 * 60)
90
+ else
91
+ @range.end - @range.begin
92
+ end
93
+ end
94
+
95
+ def to_s
96
+ super << '-dayportion-' << @type.to_s
97
+ end
98
+
99
+ private
100
+ def construct_date_from_reference_and_offset(reference, offset = nil)
101
+ elapsed_seconds_for_range = offset || (@range.end - @range.begin)
102
+ second_hand = ((elapsed_seconds_for_range - (12 * 60))) % 60
103
+ minute_hand = (elapsed_seconds_for_range - second_hand) / (60) % 60
104
+ hour_hand = (elapsed_seconds_for_range - minute_hand - second_hand) / (60 * 60) + reference.hour % 24
105
+ Chronic.construct(reference.year, reference.month, reference.day, hour_hand, minute_hand, second_hand)
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,71 @@
1
+ module Chronic
2
+ class RepeaterFortnight < Repeater #:nodoc:
3
+ FORTNIGHT_SECONDS = 1_209_600 # (14 * 24 * 60 * 60)
4
+
5
+ def initialize(type)
6
+ super
7
+ end
8
+
9
+ def next(pointer)
10
+ super
11
+
12
+ if !@current_fortnight_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_fortnight_start = next_sunday_span.begin
19
+ when :past
20
+ sunday_repeater = RepeaterDayName.new(:sunday)
21
+ sunday_repeater.start = (@now + RepeaterDay::DAY_SECONDS)
22
+ 2.times { sunday_repeater.next(:past) }
23
+ last_sunday_span = sunday_repeater.next(:past)
24
+ @current_fortnight_start = last_sunday_span.begin
25
+ end
26
+ else
27
+ direction = pointer == :future ? 1 : -1
28
+ @current_fortnight_start += direction * FORTNIGHT_SECONDS
29
+ end
30
+
31
+ Span.new(@current_fortnight_start, @current_fortnight_start + FORTNIGHT_SECONDS)
32
+ end
33
+
34
+ def this(pointer = :future)
35
+ super
36
+
37
+ pointer = :future if pointer == :none
38
+
39
+ case pointer
40
+ when :future
41
+ this_fortnight_start = Chronic.construct(@now.year, @now.month, @now.day, @now.hour) + RepeaterHour::HOUR_SECONDS
42
+ sunday_repeater = RepeaterDayName.new(:sunday)
43
+ sunday_repeater.start = @now
44
+ sunday_repeater.this(:future)
45
+ this_sunday_span = sunday_repeater.this(:future)
46
+ this_fortnight_end = this_sunday_span.begin
47
+ Span.new(this_fortnight_start, this_fortnight_end)
48
+ when :past
49
+ this_fortnight_end = Chronic.construct(@now.year, @now.month, @now.day, @now.hour)
50
+ sunday_repeater = RepeaterDayName.new(:sunday)
51
+ sunday_repeater.start = @now
52
+ last_sunday_span = sunday_repeater.next(:past)
53
+ this_fortnight_start = last_sunday_span.begin
54
+ Span.new(this_fortnight_start, this_fortnight_end)
55
+ end
56
+ end
57
+
58
+ def offset(span, amount, pointer)
59
+ direction = pointer == :future ? 1 : -1
60
+ span + direction * amount * FORTNIGHT_SECONDS
61
+ end
62
+
63
+ def width
64
+ FORTNIGHT_SECONDS
65
+ end
66
+
67
+ def to_s
68
+ super << '-fortnight'
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,58 @@
1
+ module Chronic
2
+ class RepeaterHour < Repeater #:nodoc:
3
+ HOUR_SECONDS = 3600 # 60 * 60
4
+
5
+ def initialize(type)
6
+ super
7
+ end
8
+
9
+ def next(pointer)
10
+ super
11
+
12
+ if !@current_hour_start
13
+ case pointer
14
+ when :future
15
+ @current_hour_start = Chronic.construct(@now.year, @now.month, @now.day, @now.hour + 1)
16
+ when :past
17
+ @current_hour_start = Chronic.construct(@now.year, @now.month, @now.day, @now.hour - 1)
18
+ end
19
+ else
20
+ direction = pointer == :future ? 1 : -1
21
+ @current_hour_start += direction * HOUR_SECONDS
22
+ end
23
+
24
+ Span.new(@current_hour_start, @current_hour_start + HOUR_SECONDS)
25
+ end
26
+
27
+ def this(pointer = :future)
28
+ super
29
+
30
+ case pointer
31
+ when :future
32
+ hour_start = Chronic.construct(@now.year, @now.month, @now.day, @now.hour, @now.min + 1)
33
+ hour_end = Chronic.construct(@now.year, @now.month, @now.day, @now.hour + 1)
34
+ when :past
35
+ hour_start = Chronic.construct(@now.year, @now.month, @now.day, @now.hour)
36
+ hour_end = Chronic.construct(@now.year, @now.month, @now.day, @now.hour, @now.min)
37
+ when :none
38
+ hour_start = Chronic.construct(@now.year, @now.month, @now.day, @now.hour)
39
+ hour_end = hour_start + HOUR_SECONDS
40
+ end
41
+
42
+ Span.new(hour_start, hour_end)
43
+ end
44
+
45
+ def offset(span, amount, pointer)
46
+ direction = pointer == :future ? 1 : -1
47
+ span + direction * amount * HOUR_SECONDS
48
+ end
49
+
50
+ def width
51
+ HOUR_SECONDS
52
+ end
53
+
54
+ def to_s
55
+ super << '-hour'
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,58 @@
1
+ module Chronic
2
+ class RepeaterMinute < Repeater #:nodoc:
3
+ MINUTE_SECONDS = 60
4
+
5
+ def initialize(type)
6
+ super
7
+ end
8
+
9
+ def next(pointer = :future)
10
+ super
11
+
12
+ if !@current_minute_start
13
+ case pointer
14
+ when :future
15
+ @current_minute_start = Chronic.construct(@now.year, @now.month, @now.day, @now.hour, @now.min + 1)
16
+ when :past
17
+ @current_minute_start = Chronic.construct(@now.year, @now.month, @now.day, @now.hour, @now.min - 1)
18
+ end
19
+ else
20
+ direction = pointer == :future ? 1 : -1
21
+ @current_minute_start += direction * MINUTE_SECONDS
22
+ end
23
+
24
+ Span.new(@current_minute_start, @current_minute_start + MINUTE_SECONDS)
25
+ end
26
+
27
+ def this(pointer = :future)
28
+ super
29
+
30
+ case pointer
31
+ when :future
32
+ minute_begin = @now
33
+ minute_end = Chronic.construct(@now.year, @now.month, @now.day, @now.hour, @now.min)
34
+ when :past
35
+ minute_begin = Chronic.construct(@now.year, @now.month, @now.day, @now.hour, @now.min)
36
+ minute_end = @now
37
+ when :none
38
+ minute_begin = Chronic.construct(@now.year, @now.month, @now.day, @now.hour, @now.min)
39
+ minute_end = Chronic.construct(@now.year, @now.month, @now.day, @now.hour, @now.min) + MINUTE_SECONDS
40
+ end
41
+
42
+ Span.new(minute_begin, minute_end)
43
+ end
44
+
45
+ def offset(span, amount, pointer)
46
+ direction = pointer == :future ? 1 : -1
47
+ span + direction * amount * MINUTE_SECONDS
48
+ end
49
+
50
+ def width
51
+ MINUTE_SECONDS
52
+ end
53
+
54
+ def to_s
55
+ super << '-minute'
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,79 @@
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
78
+ end
79
+ end
@@ -0,0 +1,94 @@
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
46
+ end
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)
54
+ end
55
+ end
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
+
74
+ case pointer
75
+ when :past
76
+ self.next(pointer)
77
+ when :future, :none
78
+ self.next(:none)
79
+ end
80
+ end
81
+
82
+ def width
83
+ MONTH_SECONDS
84
+ end
85
+
86
+ def index
87
+ @index ||= MONTHS[@type]
88
+ end
89
+
90
+ def to_s
91
+ super << '-monthname-' << @type.to_s
92
+ end
93
+ end
94
+ end