chronic 0.2.3 → 0.3.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 (57) hide show
  1. data/HISTORY.md +76 -0
  2. data/LICENSE +21 -0
  3. data/README.md +165 -0
  4. data/Rakefile +145 -18
  5. data/benchmark/benchmark.rb +13 -0
  6. data/chronic.gemspec +85 -0
  7. data/lib/chronic.rb +21 -19
  8. data/lib/chronic/chronic.rb +59 -49
  9. data/lib/chronic/grabber.rb +2 -2
  10. data/lib/chronic/handlers.rb +167 -112
  11. data/lib/{numerizer → chronic/numerizer}/numerizer.rb +17 -23
  12. data/lib/chronic/ordinal.rb +6 -6
  13. data/lib/chronic/pointer.rb +3 -3
  14. data/lib/chronic/repeater.rb +26 -12
  15. data/lib/chronic/repeaters/repeater_day.rb +17 -12
  16. data/lib/chronic/repeaters/repeater_day_name.rb +17 -12
  17. data/lib/chronic/repeaters/repeater_day_portion.rb +13 -12
  18. data/lib/chronic/repeaters/repeater_fortnight.rb +14 -9
  19. data/lib/chronic/repeaters/repeater_hour.rb +15 -10
  20. data/lib/chronic/repeaters/repeater_minute.rb +15 -10
  21. data/lib/chronic/repeaters/repeater_month.rb +20 -15
  22. data/lib/chronic/repeaters/repeater_month_name.rb +21 -16
  23. data/lib/chronic/repeaters/repeater_season.rb +136 -9
  24. data/lib/chronic/repeaters/repeater_season_name.rb +38 -17
  25. data/lib/chronic/repeaters/repeater_second.rb +15 -10
  26. data/lib/chronic/repeaters/repeater_time.rb +49 -42
  27. data/lib/chronic/repeaters/repeater_week.rb +16 -11
  28. data/lib/chronic/repeaters/repeater_weekday.rb +77 -0
  29. data/lib/chronic/repeaters/repeater_weekend.rb +14 -9
  30. data/lib/chronic/repeaters/repeater_year.rb +19 -13
  31. data/lib/chronic/scalar.rb +16 -14
  32. data/lib/chronic/separator.rb +25 -10
  33. data/lib/chronic/time_zone.rb +4 -3
  34. data/test/helper.rb +7 -0
  35. data/test/test_Chronic.rb +17 -18
  36. data/test/test_DaylightSavings.rb +118 -0
  37. data/test/test_Handler.rb +37 -38
  38. data/test/test_Numerizer.rb +8 -5
  39. data/test/test_RepeaterDayName.rb +15 -16
  40. data/test/test_RepeaterFortnight.rb +16 -17
  41. data/test/test_RepeaterHour.rb +18 -19
  42. data/test/test_RepeaterMinute.rb +34 -0
  43. data/test/test_RepeaterMonth.rb +16 -17
  44. data/test/test_RepeaterMonthName.rb +17 -18
  45. data/test/test_RepeaterTime.rb +20 -22
  46. data/test/test_RepeaterWeek.rb +16 -17
  47. data/test/test_RepeaterWeekday.rb +55 -0
  48. data/test/test_RepeaterWeekend.rb +21 -22
  49. data/test/test_RepeaterYear.rb +17 -18
  50. data/test/test_Span.rb +5 -6
  51. data/test/test_Time.rb +11 -12
  52. data/test/test_Token.rb +5 -6
  53. data/test/test_parsing.rb +300 -204
  54. metadata +74 -52
  55. data/History.txt +0 -53
  56. data/README.txt +0 -149
  57. data/test/suite.rb +0 -9
@@ -1,9 +1,14 @@
1
1
  class Chronic::RepeaterFortnight < Chronic::Repeater #:nodoc:
2
2
  FORTNIGHT_SECONDS = 1_209_600 # (14 * 24 * 60 * 60)
3
-
3
+
4
+ def initialize(type)
5
+ super
6
+ @current_fortnight_start = nil
7
+ end
8
+
4
9
  def next(pointer)
5
10
  super
6
-
11
+
7
12
  if !@current_fortnight_start
8
13
  case pointer
9
14
  when :future
@@ -22,15 +27,15 @@ class Chronic::RepeaterFortnight < Chronic::Repeater #:nodoc:
22
27
  direction = pointer == :future ? 1 : -1
23
28
  @current_fortnight_start += direction * FORTNIGHT_SECONDS
24
29
  end
25
-
30
+
26
31
  Chronic::Span.new(@current_fortnight_start, @current_fortnight_start + FORTNIGHT_SECONDS)
27
32
  end
28
-
33
+
29
34
  def this(pointer = :future)
30
35
  super
31
-
36
+
32
37
  pointer = :future if pointer == :none
33
-
38
+
34
39
  case pointer
35
40
  when :future
36
41
  this_fortnight_start = Time.construct(@now.year, @now.month, @now.day, @now.hour) + Chronic::RepeaterHour::HOUR_SECONDS
@@ -49,7 +54,7 @@ class Chronic::RepeaterFortnight < Chronic::Repeater #:nodoc:
49
54
  Chronic::Span.new(this_fortnight_start, this_fortnight_end)
50
55
  end
51
56
  end
52
-
57
+
53
58
  def offset(span, amount, pointer)
54
59
  direction = pointer == :future ? 1 : -1
55
60
  span + direction * amount * FORTNIGHT_SECONDS
@@ -58,8 +63,8 @@ class Chronic::RepeaterFortnight < Chronic::Repeater #:nodoc:
58
63
  def width
59
64
  FORTNIGHT_SECONDS
60
65
  end
61
-
66
+
62
67
  def to_s
63
68
  super << '-fortnight'
64
69
  end
65
- end
70
+ end
@@ -1,9 +1,14 @@
1
1
  class Chronic::RepeaterHour < Chronic::Repeater #:nodoc:
2
2
  HOUR_SECONDS = 3600 # 60 * 60
3
-
3
+
4
+ def initialize(type)
5
+ super
6
+ @current_hour_start = nil
7
+ end
8
+
4
9
  def next(pointer)
5
10
  super
6
-
11
+
7
12
  if !@current_hour_start
8
13
  case pointer
9
14
  when :future
@@ -15,13 +20,13 @@ class Chronic::RepeaterHour < Chronic::Repeater #:nodoc:
15
20
  direction = pointer == :future ? 1 : -1
16
21
  @current_hour_start += direction * HOUR_SECONDS
17
22
  end
18
-
23
+
19
24
  Chronic::Span.new(@current_hour_start, @current_hour_start + HOUR_SECONDS)
20
25
  end
21
-
26
+
22
27
  def this(pointer = :future)
23
28
  super
24
-
29
+
25
30
  case pointer
26
31
  when :future
27
32
  hour_start = Time.construct(@now.year, @now.month, @now.day, @now.hour, @now.min + 1)
@@ -33,20 +38,20 @@ class Chronic::RepeaterHour < Chronic::Repeater #:nodoc:
33
38
  hour_start = Time.construct(@now.year, @now.month, @now.day, @now.hour)
34
39
  hour_end = hour_begin + HOUR_SECONDS
35
40
  end
36
-
41
+
37
42
  Chronic::Span.new(hour_start, hour_end)
38
43
  end
39
-
44
+
40
45
  def offset(span, amount, pointer)
41
46
  direction = pointer == :future ? 1 : -1
42
47
  span + direction * amount * HOUR_SECONDS
43
48
  end
44
-
49
+
45
50
  def width
46
51
  HOUR_SECONDS
47
52
  end
48
-
53
+
49
54
  def to_s
50
55
  super << '-hour'
51
56
  end
52
- end
57
+ end
@@ -1,9 +1,14 @@
1
1
  class Chronic::RepeaterMinute < Chronic::Repeater #:nodoc:
2
2
  MINUTE_SECONDS = 60
3
-
3
+
4
+ def initialize(type)
5
+ super
6
+ @current_minute_start = nil
7
+ end
8
+
4
9
  def next(pointer = :future)
5
10
  super
6
-
11
+
7
12
  if !@current_minute_start
8
13
  case pointer
9
14
  when :future
@@ -15,13 +20,13 @@ class Chronic::RepeaterMinute < Chronic::Repeater #:nodoc:
15
20
  direction = pointer == :future ? 1 : -1
16
21
  @current_minute_start += direction * MINUTE_SECONDS
17
22
  end
18
-
23
+
19
24
  Chronic::Span.new(@current_minute_start, @current_minute_start + MINUTE_SECONDS)
20
25
  end
21
-
26
+
22
27
  def this(pointer = :future)
23
28
  super
24
-
29
+
25
30
  case pointer
26
31
  when :future
27
32
  minute_begin = @now
@@ -33,20 +38,20 @@ class Chronic::RepeaterMinute < Chronic::Repeater #:nodoc:
33
38
  minute_begin = Time.construct(@now.year, @now.month, @now.day, @now.hour, @now.min)
34
39
  minute_end = Time.construct(@now.year, @now.month, @now.day, @now.hour, @now.min) + MINUTE_SECONDS
35
40
  end
36
-
41
+
37
42
  Chronic::Span.new(minute_begin, minute_end)
38
43
  end
39
-
44
+
40
45
  def offset(span, amount, pointer)
41
46
  direction = pointer == :future ? 1 : -1
42
47
  span + direction * amount * MINUTE_SECONDS
43
48
  end
44
-
49
+
45
50
  def width
46
51
  MINUTE_SECONDS
47
52
  end
48
-
53
+
49
54
  def to_s
50
55
  super << '-minute'
51
56
  end
52
- end
57
+ end
@@ -1,22 +1,27 @@
1
1
  class Chronic::RepeaterMonth < Chronic::Repeater #:nodoc:
2
2
  MONTH_SECONDS = 2_592_000 # 30 * 24 * 60 * 60
3
3
  YEAR_MONTHS = 12
4
-
4
+
5
+ def initialize(type)
6
+ super
7
+ @current_month_start = nil
8
+ end
9
+
5
10
  def next(pointer)
6
11
  super
7
-
12
+
8
13
  if !@current_month_start
9
14
  @current_month_start = offset_by(Time.construct(@now.year, @now.month), 1, pointer)
10
15
  else
11
16
  @current_month_start = offset_by(Time.construct(@current_month_start.year, @current_month_start.month), 1, pointer)
12
17
  end
13
-
18
+
14
19
  Chronic::Span.new(@current_month_start, Time.construct(@current_month_start.year, @current_month_start.month + 1))
15
20
  end
16
-
21
+
17
22
  def this(pointer = :future)
18
23
  super
19
-
24
+
20
25
  case pointer
21
26
  when :future
22
27
  month_start = Time.construct(@now.year, @now.month, @now.day + 1)
@@ -28,20 +33,20 @@ class Chronic::RepeaterMonth < Chronic::Repeater #:nodoc:
28
33
  month_start = Time.construct(@now.year, @now.month)
29
34
  month_end = self.offset_by(Time.construct(@now.year, @now.month), 1, :future)
30
35
  end
31
-
36
+
32
37
  Chronic::Span.new(month_start, month_end)
33
38
  end
34
-
35
- def offset(span, amount, pointer)
39
+
40
+ def offset(span, amount, pointer)
36
41
  Chronic::Span.new(offset_by(span.begin, amount, pointer), offset_by(span.end, amount, pointer))
37
42
  end
38
-
39
- def offset_by(time, amount, pointer)
43
+
44
+ def offset_by(time, amount, pointer)
40
45
  direction = pointer == :future ? 1 : -1
41
-
46
+
42
47
  amount_years = direction * amount / YEAR_MONTHS
43
48
  amount_months = direction * amount % YEAR_MONTHS
44
-
49
+
45
50
  new_year = time.year + amount_years
46
51
  new_month = time.month + amount_months
47
52
  if new_month > YEAR_MONTHS
@@ -50,12 +55,12 @@ class Chronic::RepeaterMonth < Chronic::Repeater #:nodoc:
50
55
  end
51
56
  Time.construct(new_year, new_month, time.day, time.hour, time.min, time.sec)
52
57
  end
53
-
58
+
54
59
  def width
55
60
  MONTH_SECONDS
56
61
  end
57
-
62
+
58
63
  def to_s
59
64
  super << '-month'
60
65
  end
61
- end
66
+ end
@@ -1,28 +1,33 @@
1
1
  class Chronic::RepeaterMonthName < Chronic::Repeater #:nodoc:
2
2
  MONTH_SECONDS = 2_592_000 # 30 * 24 * 60 * 60
3
-
3
+
4
+ def initialize(type)
5
+ super
6
+ @current_month_begin = nil
7
+ end
8
+
4
9
  def next(pointer)
5
10
  super
6
-
11
+
7
12
  if !@current_month_begin
8
13
  target_month = symbol_to_number(@type)
9
14
  case pointer
10
15
  when :future
11
16
  if @now.month < target_month
12
17
  @current_month_begin = Time.construct(@now.year, target_month)
13
- else @now.month > target_month
18
+ elsif @now.month > target_month
14
19
  @current_month_begin = Time.construct(@now.year + 1, target_month)
15
20
  end
16
21
  when :none
17
22
  if @now.month <= target_month
18
23
  @current_month_begin = Time.construct(@now.year, target_month)
19
- else @now.month > target_month
24
+ elsif @now.month > target_month
20
25
  @current_month_begin = Time.construct(@now.year + 1, target_month)
21
26
  end
22
27
  when :past
23
28
  if @now.month > target_month
24
29
  @current_month_begin = Time.construct(@now.year, target_month)
25
- else @now.month < target_month
30
+ elsif @now.month < target_month
26
31
  @current_month_begin = Time.construct(@now.year - 1, target_month)
27
32
  end
28
33
  end
@@ -35,10 +40,10 @@ class Chronic::RepeaterMonthName < Chronic::Repeater #:nodoc:
35
40
  @current_month_begin = Time.construct(@current_month_begin.year - 1, @current_month_begin.month)
36
41
  end
37
42
  end
38
-
43
+
39
44
  cur_month_year = @current_month_begin.year
40
45
  cur_month_month = @current_month_begin.month
41
-
46
+
42
47
  if cur_month_month == 12
43
48
  next_month_year = cur_month_year + 1
44
49
  next_month_month = 1
@@ -46,13 +51,13 @@ class Chronic::RepeaterMonthName < Chronic::Repeater #:nodoc:
46
51
  next_month_year = cur_month_year
47
52
  next_month_month = cur_month_month + 1
48
53
  end
49
-
54
+
50
55
  Chronic::Span.new(@current_month_begin, Time.construct(next_month_year, next_month_month))
51
56
  end
52
-
57
+
53
58
  def this(pointer = :future)
54
59
  super
55
-
60
+
56
61
  case pointer
57
62
  when :past
58
63
  self.next(pointer)
@@ -60,21 +65,21 @@ class Chronic::RepeaterMonthName < Chronic::Repeater #:nodoc:
60
65
  self.next(:none)
61
66
  end
62
67
  end
63
-
68
+
64
69
  def width
65
70
  MONTH_SECONDS
66
71
  end
67
-
72
+
68
73
  def index
69
74
  symbol_to_number(@type)
70
75
  end
71
-
76
+
72
77
  def to_s
73
78
  super << '-monthname-' << @type.to_s
74
79
  end
75
-
80
+
76
81
  private
77
-
82
+
78
83
  def symbol_to_number(sym)
79
84
  lookup = {:january => 1,
80
85
  :february => 2,
@@ -90,4 +95,4 @@ class Chronic::RepeaterMonthName < Chronic::Repeater #:nodoc:
90
95
  :december => 12}
91
96
  lookup[sym] || raise("Invalid symbol specified")
92
97
  end
93
- end
98
+ end
@@ -1,23 +1,150 @@
1
+ class Time
2
+ def to_minidate
3
+ MiniDate.new(self.month, self.day)
4
+ end
5
+ end
6
+
7
+ class Season
8
+ attr_reader :start, :end
9
+
10
+ def initialize(myStart, myEnd)
11
+ @start = myStart
12
+ @end = myEnd
13
+ end
14
+
15
+ def self.find_next_season(season, pointer)
16
+ lookup = {:spring => 0, :summer => 1, :autumn => 2, :winter => 3}
17
+ next_season_num = (lookup[season]+1*pointer) % 4
18
+ lookup.invert[next_season_num]
19
+ end
20
+
21
+ def self.season_after(season); find_next_season(season, +1); end
22
+ def self.season_before(season); find_next_season(season, -1); end
23
+ end
24
+
25
+ class MiniDate
26
+ attr_accessor :month, :day
27
+
28
+ def initialize(month, day)
29
+ @month = month
30
+ @day = day
31
+ end
32
+
33
+ def is_between?(md_start, md_end)
34
+ return true if (@month == md_start.month and @day >= md_start.day) ||
35
+ (@month == md_end.month and @day <= md_end.day)
36
+
37
+ i = md_start.month + 1
38
+ until i == md_end.month
39
+ return true if @month == i
40
+ i = (i+1) % 12
41
+ end
42
+
43
+ return false
44
+ end
45
+
46
+ def equals?(other)
47
+ @month == other.month and day == other.day
48
+ end
49
+ end
50
+
1
51
  class Chronic::RepeaterSeason < Chronic::Repeater #:nodoc:
52
+ YEAR_SEASONS = 4
2
53
  SEASON_SECONDS = 7_862_400 # 91 * 24 * 60 * 60
3
-
54
+ SEASONS = { :spring => Season.new( MiniDate.new(3,20),MiniDate.new(6,20) ),
55
+ :summer => Season.new( MiniDate.new(6,21),MiniDate.new(9,22) ),
56
+ :autumn => Season.new( MiniDate.new(9,23),MiniDate.new(12,21) ),
57
+ :winter => Season.new( MiniDate.new(12,22),MiniDate.new(3,19) ) }
58
+
59
+ def initialize(type)
60
+ super
61
+ @next_season_start = nil
62
+ end
63
+
4
64
  def next(pointer)
5
65
  super
6
-
7
- raise 'Not implemented'
66
+
67
+ direction = pointer == :future ? 1 : -1
68
+ next_season = Season.find_next_season(find_current_season(@now.to_minidate), direction)
69
+
70
+ find_next_season_span(direction, next_season)
8
71
  end
9
-
72
+
10
73
  def this(pointer = :future)
11
74
  super
12
-
13
- raise 'Not implemented'
75
+
76
+ direction = pointer == :future ? 1 : -1
77
+
78
+ today = Time.construct(@now.year, @now.month, @now.day)
79
+ this_ssn = find_current_season(@now.to_minidate)
80
+ case pointer
81
+ when :past
82
+ this_ssn_start = today + direction * num_seconds_til_start(this_ssn, direction)
83
+ this_ssn_end = today
84
+ when :future
85
+ this_ssn_start = today + Chronic::RepeaterDay::DAY_SECONDS
86
+ this_ssn_end = today + direction * num_seconds_til_end(this_ssn, direction)
87
+ when :none
88
+ this_ssn_start = today + direction * num_seconds_til_start(this_ssn, direction)
89
+ this_ssn_end = today + direction * num_seconds_til_end(this_ssn, direction)
90
+ end
91
+
92
+ Chronic::Span.new(this_ssn_start, this_ssn_end)
14
93
  end
15
-
94
+
95
+ def offset(span, amount, pointer)
96
+ Chronic::Span.new(offset_by(span.begin, amount, pointer), offset_by(span.end, amount, pointer))
97
+ end
98
+
99
+ def offset_by(time, amount, pointer)
100
+ direction = pointer == :future ? 1 : -1
101
+ time + amount * direction * SEASON_SECONDS
102
+ end
103
+
16
104
  def width
17
105
  SEASON_SECONDS
18
106
  end
19
-
107
+
20
108
  def to_s
21
109
  super << '-season'
22
110
  end
23
- end
111
+
112
+ private
113
+
114
+ def find_next_season_span(direction, next_season)
115
+ if !@next_season_start or !@next_season_end
116
+ @next_season_start = Time.construct(@now.year, @now.month, @now.day)
117
+ @next_season_end = Time.construct(@now.year, @now.month, @now.day)
118
+ end
119
+
120
+ @next_season_start += direction * num_seconds_til_start(next_season, direction)
121
+ @next_season_end += direction * num_seconds_til_end(next_season, direction)
122
+
123
+ Chronic::Span.new(@next_season_start, @next_season_end)
124
+ end
125
+
126
+ def find_current_season(md)
127
+ [:spring, :summer, :autumn, :winter].each do |season|
128
+ return season if md.is_between?(SEASONS[season].start, SEASONS[season].end)
129
+ end
130
+ end
131
+
132
+ def num_seconds_til(goal, direction)
133
+ start = Time.construct(@now.year, @now.month, @now.day)
134
+ seconds = 0
135
+
136
+ until (start + direction * seconds).to_minidate.equals?(goal)
137
+ seconds += Chronic::RepeaterDay::DAY_SECONDS
138
+ end
139
+
140
+ seconds
141
+ end
142
+
143
+ def num_seconds_til_start(season_symbol, direction)
144
+ num_seconds_til(SEASONS[season_symbol].start, direction)
145
+ end
146
+
147
+ def num_seconds_til_end(season_symbol, direction)
148
+ num_seconds_til(SEASONS[season_symbol].end, direction)
149
+ end
150
+ end