chronic 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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