chronic 0.2.3 → 0.4.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 (63) hide show
  1. data/HISTORY.md +103 -0
  2. data/LICENSE +21 -0
  3. data/Manifest.txt +16 -5
  4. data/README.md +171 -0
  5. data/Rakefile +136 -15
  6. data/benchmark/benchmark.rb +13 -0
  7. data/chronic.gemspec +82 -0
  8. data/lib/chronic/chronic.rb +65 -147
  9. data/lib/chronic/grabber.rb +13 -17
  10. data/lib/chronic/handlers.rb +216 -138
  11. data/lib/chronic/mini_date.rb +27 -0
  12. data/lib/chronic/numerizer.rb +120 -0
  13. data/lib/chronic/ordinal.rb +11 -16
  14. data/lib/chronic/pointer.rb +11 -13
  15. data/lib/chronic/repeater.rb +117 -106
  16. data/lib/chronic/repeaters/repeater_day.rb +50 -43
  17. data/lib/chronic/repeaters/repeater_day_name.rb +49 -42
  18. data/lib/chronic/repeaters/repeater_day_portion.rb +82 -80
  19. data/lib/chronic/repeaters/repeater_fortnight.rb +60 -53
  20. data/lib/chronic/repeaters/repeater_hour.rb +50 -43
  21. data/lib/chronic/repeaters/repeater_minute.rb +50 -43
  22. data/lib/chronic/repeaters/repeater_month.rb +63 -56
  23. data/lib/chronic/repeaters/repeater_month_name.rb +91 -82
  24. data/lib/chronic/repeaters/repeater_season.rb +125 -20
  25. data/lib/chronic/repeaters/repeater_season_name.rb +43 -22
  26. data/lib/chronic/repeaters/repeater_second.rb +40 -33
  27. data/lib/chronic/repeaters/repeater_time.rb +118 -106
  28. data/lib/chronic/repeaters/repeater_week.rb +64 -57
  29. data/lib/chronic/repeaters/repeater_weekday.rb +86 -0
  30. data/lib/chronic/repeaters/repeater_weekend.rb +58 -51
  31. data/lib/chronic/repeaters/repeater_year.rb +58 -50
  32. data/lib/chronic/scalar.rb +37 -27
  33. data/lib/chronic/separator.rb +34 -37
  34. data/lib/chronic/span.rb +31 -0
  35. data/lib/chronic/tag.rb +26 -0
  36. data/lib/chronic/time_zone.rb +9 -10
  37. data/lib/chronic/token.rb +35 -0
  38. data/lib/chronic.rb +34 -25
  39. data/test/helper.rb +6 -0
  40. data/test/test_Chronic.rb +22 -18
  41. data/test/test_DaylightSavings.rb +118 -0
  42. data/test/test_Handler.rb +37 -38
  43. data/test/test_Numerizer.rb +66 -42
  44. data/test/test_RepeaterDayName.rb +15 -16
  45. data/test/test_RepeaterFortnight.rb +16 -17
  46. data/test/test_RepeaterHour.rb +22 -19
  47. data/test/test_RepeaterMinute.rb +34 -0
  48. data/test/test_RepeaterMonth.rb +16 -17
  49. data/test/test_RepeaterMonthName.rb +17 -18
  50. data/test/test_RepeaterTime.rb +20 -22
  51. data/test/test_RepeaterWeek.rb +16 -17
  52. data/test/test_RepeaterWeekday.rb +55 -0
  53. data/test/test_RepeaterWeekend.rb +21 -22
  54. data/test/test_RepeaterYear.rb +17 -18
  55. data/test/test_Span.rb +5 -6
  56. data/test/test_Time.rb +11 -12
  57. data/test/test_Token.rb +5 -6
  58. data/test/test_parsing.rb +395 -208
  59. metadata +68 -52
  60. data/History.txt +0 -53
  61. data/README.txt +0 -149
  62. data/lib/numerizer/numerizer.rb +0 -103
  63. data/test/suite.rb +0 -9
@@ -0,0 +1,120 @@
1
+ require 'strscan'
2
+
3
+ module Chronic
4
+ class Numerizer
5
+
6
+ DIRECT_NUMS = [
7
+ ['eleven', '11'],
8
+ ['twelve', '12'],
9
+ ['thirteen', '13'],
10
+ ['fourteen', '14'],
11
+ ['fifteen', '15'],
12
+ ['sixteen', '16'],
13
+ ['seventeen', '17'],
14
+ ['eighteen', '18'],
15
+ ['nineteen', '19'],
16
+ ['ninteen', '19'], # Common mis-spelling
17
+ ['zero', '0'],
18
+ ['one', '1'],
19
+ ['two', '2'],
20
+ ['three', '3'],
21
+ ['four(\W|$)', '4\1'], # The weird regex is so that it matches four but not fourty
22
+ ['five', '5'],
23
+ ['six(\W|$)', '6\1'],
24
+ ['seven(\W|$)', '7\1'],
25
+ ['eight(\W|$)', '8\1'],
26
+ ['nine(\W|$)', '9\1'],
27
+ ['ten', '10'],
28
+ ['\ba[\b^$]', '1'] # doesn't make sense for an 'a' at the end to be a 1
29
+ ]
30
+
31
+ ORDINALS = [
32
+ ['first', '1'],
33
+ ['third', '3'],
34
+ ['fourth', '4'],
35
+ ['fifth', '5'],
36
+ ['sixth', '6'],
37
+ ['seventh', '7'],
38
+ ['eighth', '8'],
39
+ ['ninth', '9'],
40
+ ['tenth', '10']
41
+ ]
42
+
43
+ TEN_PREFIXES = [
44
+ ['twenty', 20],
45
+ ['thirty', 30],
46
+ ['forty', 40],
47
+ ['fourty', 40], # Common mis-spelling
48
+ ['fifty', 50],
49
+ ['sixty', 60],
50
+ ['seventy', 70],
51
+ ['eighty', 80],
52
+ ['ninety', 90]
53
+ ]
54
+
55
+ BIG_PREFIXES = [
56
+ ['hundred', 100],
57
+ ['thousand', 1000],
58
+ ['million', 1_000_000],
59
+ ['billion', 1_000_000_000],
60
+ ['trillion', 1_000_000_000_000],
61
+ ]
62
+
63
+ def self.numerize(string)
64
+ string = string.dup
65
+
66
+ # preprocess
67
+ string.gsub!(/ +|([^\d])-([^\d])/, '\1 \2') # will mutilate hyphenated-words but shouldn't matter for date extraction
68
+ string.gsub!(/a half/, 'haAlf') # take the 'a' out so it doesn't turn into a 1, save the half for the end
69
+
70
+ # easy/direct replacements
71
+
72
+ DIRECT_NUMS.each do |dn|
73
+ string.gsub!(/#{dn[0]}/i, '<num>' + dn[1])
74
+ end
75
+
76
+ ORDINALS.each do |on|
77
+ string.gsub!(/#{on[0]}/i, '<num>' + on[1] + on[0][-2, 2])
78
+ end
79
+
80
+ # ten, twenty, etc.
81
+
82
+ TEN_PREFIXES.each do |tp|
83
+ string.gsub!(/(?:#{tp[0]}) *<num>(\d(?=[^\d]|$))*/i) { '<num>' + (tp[1] + $1.to_i).to_s }
84
+ end
85
+
86
+ TEN_PREFIXES.each do |tp|
87
+ string.gsub!(/#{tp[0]}/i) { '<num>' + tp[1].to_s }
88
+ end
89
+
90
+ # hundreds, thousands, millions, etc.
91
+
92
+ BIG_PREFIXES.each do |bp|
93
+ string.gsub!(/(?:<num>)?(\d*) *#{bp[0]}/i) { '<num>' + (bp[1] * $1.to_i).to_s}
94
+ andition(string)
95
+ end
96
+
97
+ # fractional addition
98
+ # I'm not combining this with the previous block as using float addition complicates the strings
99
+ # (with extraneous .0's and such )
100
+ string.gsub!(/(\d+)(?: | and |-)*haAlf/i) { ($1.to_f + 0.5).to_s }
101
+
102
+ string.gsub(/<num>/, '')
103
+ end
104
+
105
+ class << self
106
+ private
107
+
108
+ def andition(string)
109
+ sc = StringScanner.new(string)
110
+ while(sc.scan_until(/<num>(\d+)( | and )<num>(\d+)(?=[^\w]|$)/i))
111
+ if sc[2] =~ /and/ || sc[1].size > sc[3].size
112
+ string[(sc.pos - sc.matched_size)..(sc.pos-1)] = '<num>' + (sc[1].to_i + sc[3].to_i).to_s
113
+ sc.reset
114
+ end
115
+ end
116
+ end
117
+ end
118
+
119
+ end
120
+ end
@@ -1,40 +1,35 @@
1
1
  module Chronic
2
2
 
3
3
  class Ordinal < Tag #:nodoc:
4
- def self.scan(tokens)
4
+ def self.scan(tokens, options)
5
5
  # for each token
6
6
  tokens.each_index do |i|
7
- if t = self.scan_for_ordinals(tokens[i]) then tokens[i].tag(t) end
8
- if t = self.scan_for_days(tokens[i]) then tokens[i].tag(t) end
7
+ if t = scan_for_ordinals(tokens[i]) then tokens[i].tag(t) end
8
+ if t = scan_for_days(tokens[i]) then tokens[i].tag(t) end
9
9
  end
10
- tokens
11
10
  end
12
-
11
+
13
12
  def self.scan_for_ordinals(token)
14
- if token.word =~ /^(\d*)(st|nd|rd|th)$/
15
- return Ordinal.new($1.to_i)
16
- end
17
- return nil
13
+ Ordinal.new($1.to_i) if token.word =~ /^(\d*)(st|nd|rd|th)$/
18
14
  end
19
-
15
+
20
16
  def self.scan_for_days(token)
21
17
  if token.word =~ /^(\d*)(st|nd|rd|th)$/
22
- unless $1.to_i > 31
23
- return OrdinalDay.new(token.word.to_i)
18
+ unless $1.to_i > 31 || $1.to_i < 1
19
+ OrdinalDay.new(token.word.to_i)
24
20
  end
25
21
  end
26
- return nil
27
22
  end
28
-
23
+
29
24
  def to_s
30
25
  'ordinal'
31
26
  end
32
27
  end
33
-
28
+
34
29
  class OrdinalDay < Ordinal #:nodoc:
35
30
  def to_s
36
31
  super << '-day-' << @type.to_s
37
32
  end
38
33
  end
39
34
 
40
- end
35
+ end
@@ -1,27 +1,25 @@
1
1
  module Chronic
2
2
 
3
3
  class Pointer < Tag #:nodoc:
4
- def self.scan(tokens)
4
+ def self.scan(tokens, options)
5
5
  # for each token
6
6
  tokens.each_index do |i|
7
- if t = self.scan_for_all(tokens[i]) then tokens[i].tag(t) end
7
+ if t = scan_for_all(tokens[i]) then tokens[i].tag(t) end
8
8
  end
9
- tokens
10
9
  end
11
-
10
+
12
11
  def self.scan_for_all(token)
13
- scanner = {/\bpast\b/ => :past,
14
- /\bfuture\b/ => :future,
15
- /\bin\b/ => :future}
16
- scanner.keys.each do |scanner_item|
17
- return self.new(scanner[scanner_item]) if scanner_item =~ token.word
18
- end
19
- return nil
12
+ scan_for token, self,
13
+ {
14
+ /\bpast\b/ => :past,
15
+ /\bfuture\b/ => :future,
16
+ /\bin\b/ => :future
17
+ }
20
18
  end
21
-
19
+
22
20
  def to_s
23
21
  'pointer-' << @type.to_s
24
22
  end
25
23
  end
26
24
 
27
- end
25
+ end
@@ -1,115 +1,126 @@
1
- class Chronic::Repeater < Chronic::Tag #:nodoc:
2
- def self.scan(tokens, options)
3
- # for each token
4
- tokens.each_index do |i|
5
- if t = self.scan_for_month_names(tokens[i]) then tokens[i].tag(t); next end
6
- if t = self.scan_for_day_names(tokens[i]) then tokens[i].tag(t); next end
7
- if t = self.scan_for_day_portions(tokens[i]) then tokens[i].tag(t); next end
8
- if t = self.scan_for_times(tokens[i], options) then tokens[i].tag(t); next end
9
- if t = self.scan_for_units(tokens[i]) then tokens[i].tag(t); next end
1
+ module Chronic
2
+ class Repeater < Tag #:nodoc:
3
+ def self.scan(tokens, options)
4
+ # for each token
5
+ tokens.each_index do |i|
6
+ if t = scan_for_season_names(tokens[i]) then tokens[i].tag(t); next end
7
+ if t = scan_for_month_names(tokens[i]) then tokens[i].tag(t); next end
8
+ if t = scan_for_day_names(tokens[i]) then tokens[i].tag(t); next end
9
+ if t = scan_for_day_portions(tokens[i]) then tokens[i].tag(t); next end
10
+ if t = scan_for_times(tokens[i]) then tokens[i].tag(t); next end
11
+ if t = scan_for_units(tokens[i]) then tokens[i].tag(t); next end
12
+ end
10
13
  end
11
- tokens
12
- end
13
-
14
- def self.scan_for_month_names(token)
15
- scanner = {/^jan\.?(uary)?$/ => :january,
16
- /^feb\.?(ruary)?$/ => :february,
17
- /^mar\.?(ch)?$/ => :march,
18
- /^apr\.?(il)?$/ => :april,
19
- /^may$/ => :may,
20
- /^jun\.?e?$/ => :june,
21
- /^jul\.?y?$/ => :july,
22
- /^aug\.?(ust)?$/ => :august,
23
- /^sep\.?(t\.?|tember)?$/ => :september,
24
- /^oct\.?(ober)?$/ => :october,
25
- /^nov\.?(ember)?$/ => :november,
26
- /^dec\.?(ember)?$/ => :december}
27
- scanner.keys.each do |scanner_item|
28
- return Chronic::RepeaterMonthName.new(scanner[scanner_item]) if scanner_item =~ token.word
14
+
15
+ def self.scan_for_season_names(token)
16
+ scan_for token, RepeaterSeasonName,
17
+ {
18
+ /^springs?$/ => :spring,
19
+ /^summers?$/ => :summer,
20
+ /^(autumn)|(fall)s?$/ => :autumn,
21
+ /^winters?$/ => :winter
22
+ }
29
23
  end
30
- return nil
31
- end
32
-
33
- def self.scan_for_day_names(token)
34
- scanner = {/^m[ou]n(day)?$/ => :monday,
35
- /^t(ue|eu|oo|u|)s(day)?$/ => :tuesday,
36
- /^tue$/ => :tuesday,
37
- /^we(dnes|nds|nns)day$/ => :wednesday,
38
- /^wed$/ => :wednesday,
39
- /^th(urs|ers)day$/ => :thursday,
40
- /^thu$/ => :thursday,
41
- /^fr[iy](day)?$/ => :friday,
42
- /^sat(t?[ue]rday)?$/ => :saturday,
43
- /^su[nm](day)?$/ => :sunday}
44
- scanner.keys.each do |scanner_item|
45
- return Chronic::RepeaterDayName.new(scanner[scanner_item]) if scanner_item =~ token.word
24
+
25
+ def self.scan_for_month_names(token)
26
+ scan_for token, RepeaterMonthName,
27
+ {
28
+ /^jan\.?(uary)?$/ => :january,
29
+ /^feb\.?(ruary)?$/ => :february,
30
+ /^mar\.?(ch)?$/ => :march,
31
+ /^apr\.?(il)?$/ => :april,
32
+ /^may$/ => :may,
33
+ /^jun\.?e?$/ => :june,
34
+ /^jul\.?y?$/ => :july,
35
+ /^aug\.?(ust)?$/ => :august,
36
+ /^sep\.?(t\.?|tember)?$/ => :september,
37
+ /^oct\.?(ober)?$/ => :october,
38
+ /^nov\.?(ember)?$/ => :november,
39
+ /^dec\.?(ember)?$/ => :december
40
+ }
46
41
  end
47
- return nil
48
- end
49
-
50
- def self.scan_for_day_portions(token)
51
- scanner = {/^ams?$/ => :am,
52
- /^pms?$/ => :pm,
53
- /^mornings?$/ => :morning,
54
- /^afternoons?$/ => :afternoon,
55
- /^evenings?$/ => :evening,
56
- /^(night|nite)s?$/ => :night}
57
- scanner.keys.each do |scanner_item|
58
- return Chronic::RepeaterDayPortion.new(scanner[scanner_item]) if scanner_item =~ token.word
42
+
43
+ def self.scan_for_day_names(token)
44
+ scan_for token, RepeaterDayName,
45
+ {
46
+ /^m[ou]n(day)?$/ => :monday,
47
+ /^t(ue|eu|oo|u|)s(day)?$/ => :tuesday,
48
+ /^tue$/ => :tuesday,
49
+ /^we(dnes|nds|nns)day$/ => :wednesday,
50
+ /^wed$/ => :wednesday,
51
+ /^th(urs|ers)day$/ => :thursday,
52
+ /^thu(rs)?$/ => :thursday,
53
+ /^fr[iy](day)?$/ => :friday,
54
+ /^sat(t?[ue]rday)?$/ => :saturday,
55
+ /^su[nm](day)?$/ => :sunday
56
+ }
59
57
  end
60
- return nil
61
- end
62
-
63
- def self.scan_for_times(token, options)
64
- if token.word =~ /^\d{1,2}(:?\d{2})?([\.:]?\d{2})?$/
65
- return Chronic::RepeaterTime.new(token.word, options)
58
+
59
+ def self.scan_for_day_portions(token)
60
+ scan_for token, RepeaterDayPortion,
61
+ {
62
+ /^ams?$/ => :am,
63
+ /^pms?$/ => :pm,
64
+ /^mornings?$/ => :morning,
65
+ /^afternoons?$/ => :afternoon,
66
+ /^evenings?$/ => :evening,
67
+ /^(night|nite)s?$/ => :night
68
+ }
66
69
  end
67
- return nil
68
- end
69
-
70
- def self.scan_for_units(token)
71
- scanner = {/^years?$/ => :year,
72
- /^seasons?$/ => :season,
73
- /^months?$/ => :month,
74
- /^fortnights?$/ => :fortnight,
75
- /^weeks?$/ => :week,
76
- /^weekends?$/ => :weekend,
77
- /^days?$/ => :day,
78
- /^hours?$/ => :hour,
79
- /^minutes?$/ => :minute,
80
- /^seconds?$/ => :second}
81
- scanner.keys.each do |scanner_item|
82
- if scanner_item =~ token.word
83
- klass_name = 'Chronic::Repeater' + scanner[scanner_item].to_s.capitalize
84
- klass = eval(klass_name)
85
- return klass.new(scanner[scanner_item])
70
+
71
+ def self.scan_for_times(token)
72
+ if token.word =~ /^\d{1,2}(:?\d{2})?([\.:]?\d{2})?$/
73
+ return Chronic::RepeaterTime.new(token.word)
86
74
  end
75
+ return nil
76
+ end
77
+
78
+ def self.scan_for_units(token)
79
+ {
80
+ /^years?$/ => :year,
81
+ /^seasons?$/ => :season,
82
+ /^months?$/ => :month,
83
+ /^fortnights?$/ => :fortnight,
84
+ /^weeks?$/ => :week,
85
+ /^weekends?$/ => :weekend,
86
+ /^(week|business)days?$/ => :weekday,
87
+ /^days?$/ => :day,
88
+ /^hours?$/ => :hour,
89
+ /^minutes?$/ => :minute,
90
+ /^seconds?$/ => :second
91
+ }.each do |item, symbol|
92
+ if item =~ token.word
93
+ klass_name = 'Repeater' + symbol.to_s.capitalize
94
+ klass = Chronic.const_get(klass_name)
95
+ return klass.new(symbol)
96
+ end
97
+ end
98
+ return nil
99
+ end
100
+
101
+ def <=>(other)
102
+ width <=> other.width
103
+ end
104
+
105
+ # returns the width (in seconds or months) of this repeatable.
106
+ def width
107
+ raise("Repeatable#width must be overridden in subclasses")
108
+ end
109
+
110
+ # returns the next occurance of this repeatable.
111
+ def next(pointer)
112
+ !@now.nil? || raise("Start point must be set before calling #next")
113
+ [:future, :none, :past].include?(pointer) || raise("First argument 'pointer' must be one of :past or :future")
114
+ #raise("Repeatable#next must be overridden in subclasses")
115
+ end
116
+
117
+ def this(pointer)
118
+ !@now.nil? || raise("Start point must be set before calling #this")
119
+ [:future, :past, :none].include?(pointer) || raise("First argument 'pointer' must be one of :past, :future, :none")
120
+ end
121
+
122
+ def to_s
123
+ 'repeater'
87
124
  end
88
- return nil
89
- end
90
-
91
- def <=>(other)
92
- width <=> other.width
93
- end
94
-
95
- # returns the width (in seconds or months) of this repeatable.
96
- def width
97
- raise("Repeatable#width must be overridden in subclasses")
98
- end
99
-
100
- # returns the next occurance of this repeatable.
101
- def next(pointer)
102
- !@now.nil? || raise("Start point must be set before calling #next")
103
- [:future, :none, :past].include?(pointer) || raise("First argument 'pointer' must be one of :past or :future")
104
- #raise("Repeatable#next must be overridden in subclasses")
105
- end
106
-
107
- def this(pointer)
108
- !@now.nil? || raise("Start point must be set before calling #this")
109
- [:future, :past, :none].include?(pointer) || raise("First argument 'pointer' must be one of :past, :future, :none")
110
- end
111
-
112
- def to_s
113
- 'repeater'
114
125
  end
115
126
  end
@@ -1,47 +1,54 @@
1
- class Chronic::RepeaterDay < Chronic::Repeater #:nodoc:
2
- DAY_SECONDS = 86_400 # (24 * 60 * 60)
3
-
4
- def next(pointer)
5
- super
6
-
7
- if !@current_day_start
8
- @current_day_start = Time.local(@now.year, @now.month, @now.day)
1
+ module Chronic
2
+ class RepeaterDay < Repeater #:nodoc:
3
+ DAY_SECONDS = 86_400 # (24 * 60 * 60)
4
+
5
+ def initialize(type)
6
+ super
7
+ @current_day_start = nil
9
8
  end
10
-
11
- direction = pointer == :future ? 1 : -1
12
- @current_day_start += direction * DAY_SECONDS
13
-
14
- Chronic::Span.new(@current_day_start, @current_day_start + DAY_SECONDS)
15
- end
16
-
17
- def this(pointer = :future)
18
- super
19
-
20
- case pointer
21
- when :future
22
- day_begin = Time.construct(@now.year, @now.month, @now.day, @now.hour + 1)
23
- day_end = Time.construct(@now.year, @now.month, @now.day) + DAY_SECONDS
24
- when :past
25
- day_begin = Time.construct(@now.year, @now.month, @now.day)
26
- day_end = Time.construct(@now.year, @now.month, @now.day, @now.hour)
27
- when :none
28
- day_begin = Time.construct(@now.year, @now.month, @now.day)
29
- day_end = Time.construct(@now.year, @now.month, @now.day) + DAY_SECONDS
9
+
10
+ def next(pointer)
11
+ super
12
+
13
+ if !@current_day_start
14
+ @current_day_start = Chronic.time_class.local(@now.year, @now.month, @now.day)
15
+ end
16
+
17
+ direction = pointer == :future ? 1 : -1
18
+ @current_day_start += direction * DAY_SECONDS
19
+
20
+ Span.new(@current_day_start, @current_day_start + DAY_SECONDS)
21
+ end
22
+
23
+ def this(pointer = :future)
24
+ super
25
+
26
+ case pointer
27
+ when :future
28
+ day_begin = Time.construct(@now.year, @now.month, @now.day, @now.hour)
29
+ day_end = Time.construct(@now.year, @now.month, @now.day) + DAY_SECONDS
30
+ when :past
31
+ day_begin = Time.construct(@now.year, @now.month, @now.day)
32
+ day_end = Time.construct(@now.year, @now.month, @now.day, @now.hour)
33
+ when :none
34
+ day_begin = Time.construct(@now.year, @now.month, @now.day)
35
+ day_end = Time.construct(@now.year, @now.month, @now.day) + DAY_SECONDS
36
+ end
37
+
38
+ Span.new(day_begin, day_end)
39
+ end
40
+
41
+ def offset(span, amount, pointer)
42
+ direction = pointer == :future ? 1 : -1
43
+ span + direction * amount * DAY_SECONDS
44
+ end
45
+
46
+ def width
47
+ DAY_SECONDS
48
+ end
49
+
50
+ def to_s
51
+ super << '-day'
30
52
  end
31
-
32
- Chronic::Span.new(day_begin, day_end)
33
- end
34
-
35
- def offset(span, amount, pointer)
36
- direction = pointer == :future ? 1 : -1
37
- span + direction * amount * DAY_SECONDS
38
- end
39
-
40
- def width
41
- DAY_SECONDS
42
- end
43
-
44
- def to_s
45
- super << '-day'
46
53
  end
47
54
  end
@@ -1,46 +1,53 @@
1
- class Chronic::RepeaterDayName < Chronic::Repeater #:nodoc:
2
- DAY_SECONDS = 86400 # (24 * 60 * 60)
3
-
4
- def next(pointer)
5
- super
6
-
7
- direction = pointer == :future ? 1 : -1
8
-
9
- if !@current_day_start
10
- @current_day_start = Time.construct(@now.year, @now.month, @now.day)
11
- @current_day_start += direction * DAY_SECONDS
12
-
13
- day_num = symbol_to_number(@type)
14
-
15
- while @current_day_start.wday != day_num
16
- @current_day_start += direction * DAY_SECONDS
1
+ module Chronic
2
+ class RepeaterDayName < Repeater #:nodoc:
3
+ DAY_SECONDS = 86400 # (24 * 60 * 60)
4
+
5
+ def initialize(type)
6
+ super
7
+ @current_day_start = nil
8
+ end
9
+
10
+ def next(pointer)
11
+ super
12
+
13
+ direction = pointer == :future ? 1 : -1
14
+
15
+ if !@current_date
16
+ @current_date = Date.new(@now.year, @now.month, @now.day)
17
+ @current_date += direction
18
+
19
+ day_num = symbol_to_number(@type)
20
+
21
+ while @current_date.wday != day_num
22
+ @current_date += direction
23
+ end
24
+ else
25
+ @current_date += direction * 7
17
26
  end
18
- else
19
- @current_day_start += direction * 7 * DAY_SECONDS
27
+ next_date = @current_date.succ
28
+ Span.new(Time.construct(@current_date.year, @current_date.month, @current_date.day), Time.construct(next_date.year, next_date.month, next_date.day))
29
+ end
30
+
31
+ def this(pointer = :future)
32
+ super
33
+
34
+ pointer = :future if pointer == :none
35
+ self.next(pointer)
36
+ end
37
+
38
+ def width
39
+ DAY_SECONDS
40
+ end
41
+
42
+ def to_s
43
+ super << '-dayname-' << @type.to_s
44
+ end
45
+
46
+ private
47
+
48
+ def symbol_to_number(sym)
49
+ lookup = {:sunday => 0, :monday => 1, :tuesday => 2, :wednesday => 3, :thursday => 4, :friday => 5, :saturday => 6}
50
+ lookup[sym] || raise("Invalid symbol specified")
20
51
  end
21
-
22
- Chronic::Span.new(@current_day_start, @current_day_start + DAY_SECONDS)
23
- end
24
-
25
- def this(pointer = :future)
26
- super
27
-
28
- pointer = :future if pointer == :none
29
- self.next(pointer)
30
- end
31
-
32
- def width
33
- DAY_SECONDS
34
- end
35
-
36
- def to_s
37
- super << '-dayname-' << @type.to_s
38
- end
39
-
40
- private
41
-
42
- def symbol_to_number(sym)
43
- lookup = {:sunday => 0, :monday => 1, :tuesday => 2, :wednesday => 3, :thursday => 4, :friday => 5, :saturday => 6}
44
- lookup[sym] || raise("Invalid symbol specified")
45
52
  end
46
53
  end