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,24 +1,45 @@
1
+ require 'chronic/repeaters/repeater_season.rb'
2
+
1
3
  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
-
4
+ SEASON_SECONDS = 7_862_400 # 91 * 24 * 60 * 60
5
+ DAY_SECONDS = 86_400 # (24 * 60 * 60)
6
+
7
7
  def next(pointer)
8
- super
9
- raise 'Not implemented'
8
+ direction = pointer == :future ? 1 : -1
9
+ find_next_season_span(direction, @type)
10
10
  end
11
-
11
+
12
12
  def this(pointer = :future)
13
- super
14
- raise 'Not implemented'
13
+ # super
14
+
15
+ direction = pointer == :future ? 1 : -1
16
+
17
+ today = Time.construct(@now.year, @now.month, @now.day)
18
+ goal_ssn_start = today + direction * num_seconds_til_start(@type, direction)
19
+ goal_ssn_end = today + direction * num_seconds_til_end(@type, direction)
20
+ curr_ssn = find_current_season(@now.to_minidate)
21
+ case pointer
22
+ when :past
23
+ this_ssn_start = goal_ssn_start
24
+ this_ssn_end = (curr_ssn == @type) ? today : goal_ssn_end
25
+ when :future
26
+ this_ssn_start = (curr_ssn == @type) ? today + Chronic::RepeaterDay::DAY_SECONDS : goal_ssn_start
27
+ this_ssn_end = goal_ssn_end
28
+ when :none
29
+ this_ssn_start = goal_ssn_start
30
+ this_ssn_end = goal_ssn_end
31
+ end
32
+
33
+ Chronic::Span.new(this_ssn_start, this_ssn_end)
15
34
  end
16
-
17
- def width
18
- (91 * 24 * 60 * 60)
35
+
36
+ def offset(span, amount, pointer)
37
+ Chronic::Span.new(offset_by(span.begin, amount, pointer), offset_by(span.end, amount, pointer))
19
38
  end
20
-
21
- def to_s
22
- super << '-season-' << @type.to_s
39
+
40
+ def offset_by(time, amount, pointer)
41
+ direction = pointer == :future ? 1 : -1
42
+ time + amount * direction * Chronic::RepeaterYear::YEAR_SECONDS
23
43
  end
24
- end
44
+
45
+ end
@@ -1,36 +1,41 @@
1
1
  class Chronic::RepeaterSecond < Chronic::Repeater #:nodoc:
2
2
  SECOND_SECONDS = 1 # haha, awesome
3
-
3
+
4
+ def initialize(type)
5
+ super
6
+ @second_start = nil
7
+ end
8
+
4
9
  def next(pointer = :future)
5
10
  super
6
-
11
+
7
12
  direction = pointer == :future ? 1 : -1
8
-
13
+
9
14
  if !@second_start
10
15
  @second_start = @now + (direction * SECOND_SECONDS)
11
16
  else
12
17
  @second_start += SECOND_SECONDS * direction
13
18
  end
14
-
19
+
15
20
  Chronic::Span.new(@second_start, @second_start + SECOND_SECONDS)
16
21
  end
17
-
22
+
18
23
  def this(pointer = :future)
19
24
  super
20
-
25
+
21
26
  Chronic::Span.new(@now, @now + 1)
22
27
  end
23
-
28
+
24
29
  def offset(span, amount, pointer)
25
30
  direction = pointer == :future ? 1 : -1
26
31
  span + direction * amount * SECOND_SECONDS
27
32
  end
28
-
33
+
29
34
  def width
30
35
  SECOND_SECONDS
31
36
  end
32
-
37
+
33
38
  def to_s
34
39
  super << '-second'
35
40
  end
36
- end
41
+ end
@@ -1,117 +1,124 @@
1
1
  class Chronic::RepeaterTime < Chronic::Repeater #:nodoc:
2
2
  class Tick #:nodoc:
3
3
  attr_accessor :time
4
-
4
+
5
5
  def initialize(time, ambiguous = false)
6
6
  @time = time
7
7
  @ambiguous = ambiguous
8
8
  end
9
-
9
+
10
10
  def ambiguous?
11
11
  @ambiguous
12
12
  end
13
-
13
+
14
14
  def *(other)
15
15
  Tick.new(@time * other, @ambiguous)
16
16
  end
17
-
17
+
18
18
  def to_f
19
19
  @time.to_f
20
20
  end
21
-
21
+
22
22
  def to_s
23
23
  @time.to_s + (@ambiguous ? '?' : '')
24
24
  end
25
25
  end
26
-
26
+
27
27
  def initialize(time, options = {})
28
+ @current_time = nil
28
29
  t = time.gsub(/\:/, '')
29
- @type =
30
- if (1..2) === t.size
31
- hours = t.to_i
32
- hours == 12 ? Tick.new(0 * 60 * 60, true) : Tick.new(hours * 60 * 60, true)
33
- elsif t.size == 3
34
- Tick.new((t[0..0].to_i * 60 * 60) + (t[1..2].to_i * 60), true)
35
- elsif t.size == 4
36
- ambiguous = time =~ /:/ && t[0..0].to_i != 0 && t[0..1].to_i <= 12
37
- hours = t[0..1].to_i
38
- 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)
39
- elsif t.size == 5
40
- Tick.new(t[0..0].to_i * 60 * 60 + t[1..2].to_i * 60 + t[3..4].to_i, true)
41
- elsif t.size == 6
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 + t[4..5].to_i, ambiguous) : Tick.new(hours * 60 * 60 + t[2..3].to_i * 60 + t[4..5].to_i, ambiguous)
45
- else
46
- raise("Time cannot exceed six digits")
30
+
31
+ @type =
32
+ case t.size
33
+ when 1..2
34
+ hours = t.to_i
35
+ hours == 12 ? Tick.new(0 * 60 * 60, true) : Tick.new(hours * 60 * 60, true)
36
+ when 3
37
+ Tick.new((t[0..0].to_i * 60 * 60) + (t[1..2].to_i * 60), true)
38
+ when 4
39
+ ambiguous = time =~ /:/ && t[0..0].to_i != 0 && t[0..1].to_i <= 12
40
+ hours = t[0..1].to_i
41
+ 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)
42
+ when 5
43
+ Tick.new(t[0..0].to_i * 60 * 60 + t[1..2].to_i * 60 + t[3..4].to_i, true)
44
+ when 6
45
+ ambiguous = time =~ /:/ && t[0..0].to_i != 0 && t[0..1].to_i <= 12
46
+ hours = t[0..1].to_i
47
+ 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)
48
+ else
49
+ raise("Time cannot exceed six digits")
47
50
  end
48
51
  end
49
-
52
+
50
53
  # Return the next past or future Span for the time that this Repeater represents
51
54
  # pointer - Symbol representing which temporal direction to fetch the next day
52
55
  # must be either :past or :future
53
56
  def next(pointer)
54
57
  super
55
-
58
+
56
59
  half_day = 60 * 60 * 12
57
60
  full_day = 60 * 60 * 24
58
-
61
+
59
62
  first = false
60
-
63
+
61
64
  unless @current_time
62
65
  first = true
63
- midnight = Time.local(@now.year, @now.month, @now.day)
66
+ midnight = Chronic.time_class.local(@now.year, @now.month, @now.day)
67
+
64
68
  yesterday_midnight = midnight - full_day
65
69
  tomorrow_midnight = midnight + full_day
66
70
 
71
+ offset_fix = midnight.gmt_offset - tomorrow_midnight.gmt_offset
72
+ tomorrow_midnight += offset_fix
73
+
67
74
  catch :done do
68
75
  if pointer == :future
69
76
  if @type.ambiguous?
70
- [midnight + @type, midnight + half_day + @type, tomorrow_midnight + @type].each do |t|
77
+ [midnight + @type + offset_fix, midnight + half_day + @type + offset_fix, tomorrow_midnight + @type].each do |t|
71
78
  (@current_time = t; throw :done) if t >= @now
72
79
  end
73
80
  else
74
- [midnight + @type, tomorrow_midnight + @type].each do |t|
81
+ [midnight + @type + offset_fix, tomorrow_midnight + @type].each do |t|
75
82
  (@current_time = t; throw :done) if t >= @now
76
83
  end
77
84
  end
78
85
  else # pointer == :past
79
86
  if @type.ambiguous?
80
- [midnight + half_day + @type, midnight + @type, yesterday_midnight + @type * 2].each do |t|
87
+ [midnight + half_day + @type + offset_fix, midnight + @type + offset_fix, yesterday_midnight + @type + half_day].each do |t|
81
88
  (@current_time = t; throw :done) if t <= @now
82
89
  end
83
90
  else
84
- [midnight + @type, yesterday_midnight + @type].each do |t|
91
+ [midnight + @type + offset_fix, yesterday_midnight + @type].each do |t|
85
92
  (@current_time = t; throw :done) if t <= @now
86
93
  end
87
94
  end
88
95
  end
89
96
  end
90
-
97
+
91
98
  @current_time || raise("Current time cannot be nil at this point")
92
99
  end
93
-
100
+
94
101
  unless first
95
102
  increment = @type.ambiguous? ? half_day : full_day
96
103
  @current_time += pointer == :future ? increment : -increment
97
104
  end
98
-
105
+
99
106
  Chronic::Span.new(@current_time, @current_time + width)
100
107
  end
101
-
108
+
102
109
  def this(context = :future)
103
110
  super
104
-
111
+
105
112
  context = :future if context == :none
106
-
113
+
107
114
  self.next(context)
108
115
  end
109
-
116
+
110
117
  def width
111
118
  1
112
119
  end
113
-
120
+
114
121
  def to_s
115
122
  super << '-time-' << @type.to_s
116
123
  end
117
- end
124
+ end
@@ -1,9 +1,14 @@
1
1
  class Chronic::RepeaterWeek < Chronic::Repeater #:nodoc:
2
2
  WEEK_SECONDS = 604800 # (7 * 24 * 60 * 60)
3
-
3
+
4
+ def initialize(type)
5
+ super
6
+ @current_week_start = nil
7
+ end
8
+
4
9
  def next(pointer)
5
10
  super
6
-
11
+
7
12
  if !@current_week_start
8
13
  case pointer
9
14
  when :future
@@ -22,23 +27,23 @@ class Chronic::RepeaterWeek < Chronic::Repeater #:nodoc:
22
27
  direction = pointer == :future ? 1 : -1
23
28
  @current_week_start += direction * WEEK_SECONDS
24
29
  end
25
-
30
+
26
31
  Chronic::Span.new(@current_week_start, @current_week_start + WEEK_SECONDS)
27
32
  end
28
-
33
+
29
34
  def this(pointer = :future)
30
35
  super
31
-
36
+
32
37
  case pointer
33
38
  when :future
34
- this_week_start = Time.local(@now.year, @now.month, @now.day, @now.hour) + Chronic::RepeaterHour::HOUR_SECONDS
39
+ this_week_start = Chronic.time_class.local(@now.year, @now.month, @now.day, @now.hour) + Chronic::RepeaterHour::HOUR_SECONDS
35
40
  sunday_repeater = Chronic::RepeaterDayName.new(:sunday)
36
41
  sunday_repeater.start = @now
37
42
  this_sunday_span = sunday_repeater.this(:future)
38
43
  this_week_end = this_sunday_span.begin
39
44
  Chronic::Span.new(this_week_start, this_week_end)
40
45
  when :past
41
- this_week_end = Time.local(@now.year, @now.month, @now.day, @now.hour)
46
+ this_week_end = Chronic.time_class.local(@now.year, @now.month, @now.day, @now.hour)
42
47
  sunday_repeater = Chronic::RepeaterDayName.new(:sunday)
43
48
  sunday_repeater.start = @now
44
49
  last_sunday_span = sunday_repeater.next(:past)
@@ -52,17 +57,17 @@ class Chronic::RepeaterWeek < Chronic::Repeater #:nodoc:
52
57
  Chronic::Span.new(this_week_start, this_week_start + WEEK_SECONDS)
53
58
  end
54
59
  end
55
-
60
+
56
61
  def offset(span, amount, pointer)
57
62
  direction = pointer == :future ? 1 : -1
58
63
  span + direction * amount * WEEK_SECONDS
59
64
  end
60
-
65
+
61
66
  def width
62
67
  WEEK_SECONDS
63
68
  end
64
-
69
+
65
70
  def to_s
66
71
  super << '-week'
67
72
  end
68
- end
73
+ end
@@ -0,0 +1,77 @@
1
+ class Chronic::RepeaterWeekday < Chronic::Repeater #:nodoc:
2
+ WEEK_WEEKDAYS = 5
3
+ DAY_SECONDS = 86400 # (24 * 60 * 60)
4
+
5
+ def initialize(type)
6
+ super
7
+ @current_weekday_start = nil
8
+ end
9
+
10
+ def next(pointer)
11
+ super
12
+
13
+ direction = pointer == :future ? 1 : -1
14
+
15
+ if !@current_weekday_start
16
+ @current_weekday_start = Time.construct(@now.year, @now.month, @now.day)
17
+ @current_weekday_start += direction * DAY_SECONDS
18
+
19
+ until is_weekday?(@current_weekday_start.wday)
20
+ @current_weekday_start += direction * DAY_SECONDS
21
+ end
22
+ else
23
+ loop do
24
+ @current_weekday_start += direction * DAY_SECONDS
25
+ break if is_weekday?(@current_weekday_start.wday)
26
+ end
27
+ end
28
+
29
+ Chronic::Span.new(@current_weekday_start, @current_weekday_start + DAY_SECONDS)
30
+ end
31
+
32
+ def this(pointer = :future)
33
+ super
34
+
35
+ case pointer
36
+ when :past
37
+ self.next(:past)
38
+ when :future, :none
39
+ self.next(:future)
40
+ end
41
+ end
42
+
43
+ def offset(span, amount, pointer)
44
+ direction = pointer == :future ? 1 : -1
45
+
46
+ num_weekdays_passed = 0; offset = 0
47
+ until num_weekdays_passed == amount
48
+ offset += direction * DAY_SECONDS
49
+ num_weekdays_passed += 1 if is_weekday?((span.begin+offset).wday)
50
+ end
51
+
52
+ span + offset
53
+ end
54
+
55
+ def width
56
+ DAY_SECONDS
57
+ end
58
+
59
+ def to_s
60
+ super << '-weekday'
61
+ end
62
+
63
+ private
64
+
65
+ def is_weekend?(day)
66
+ day == symbol_to_number(:saturday) || day == symbol_to_number(:sunday)
67
+ end
68
+
69
+ def is_weekday?(day)
70
+ !is_weekend?(day)
71
+ end
72
+
73
+ def symbol_to_number(sym)
74
+ lookup = {:sunday => 0, :monday => 1, :tuesday => 2, :wednesday => 3, :thursday => 4, :friday => 5, :saturday => 6}
75
+ lookup[sym] || raise("Invalid symbol specified")
76
+ end
77
+ end
@@ -1,9 +1,14 @@
1
1
  class Chronic::RepeaterWeekend < Chronic::Repeater #:nodoc:
2
2
  WEEKEND_SECONDS = 172_800 # (2 * 24 * 60 * 60)
3
-
3
+
4
+ def initialize(type)
5
+ super
6
+ @current_week_start = nil
7
+ end
8
+
4
9
  def next(pointer)
5
10
  super
6
-
11
+
7
12
  if !@current_week_start
8
13
  case pointer
9
14
  when :future
@@ -21,13 +26,13 @@ class Chronic::RepeaterWeekend < Chronic::Repeater #:nodoc:
21
26
  direction = pointer == :future ? 1 : -1
22
27
  @current_week_start += direction * Chronic::RepeaterWeek::WEEK_SECONDS
23
28
  end
24
-
29
+
25
30
  Chronic::Span.new(@current_week_start, @current_week_start + WEEKEND_SECONDS)
26
31
  end
27
-
32
+
28
33
  def this(pointer = :future)
29
34
  super
30
-
35
+
31
36
  case pointer
32
37
  when :future, :none
33
38
  saturday_repeater = Chronic::RepeaterDayName.new(:saturday)
@@ -41,7 +46,7 @@ class Chronic::RepeaterWeekend < Chronic::Repeater #:nodoc:
41
46
  Chronic::Span.new(last_saturday_span.begin, last_saturday_span.begin + WEEKEND_SECONDS)
42
47
  end
43
48
  end
44
-
49
+
45
50
  def offset(span, amount, pointer)
46
51
  direction = pointer == :future ? 1 : -1
47
52
  weekend = Chronic::RepeaterWeekend.new(:weekend)
@@ -49,12 +54,12 @@ class Chronic::RepeaterWeekend < Chronic::Repeater #:nodoc:
49
54
  start = weekend.next(pointer).begin + (amount - 1) * direction * Chronic::RepeaterWeek::WEEK_SECONDS
50
55
  Chronic::Span.new(start, start + (span.end - span.begin))
51
56
  end
52
-
57
+
53
58
  def width
54
59
  WEEKEND_SECONDS
55
60
  end
56
-
61
+
57
62
  def to_s
58
63
  super << '-weekend'
59
64
  end
60
- end
65
+ end