chronic 0.1.5 → 0.6.5
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.
- data/.gemtest +0 -0
- data/.gitignore +5 -0
- data/.yardopts +3 -0
- data/HISTORY.md +180 -0
- data/LICENSE +21 -0
- data/README.md +176 -0
- data/Rakefile +39 -32
- data/chronic.gemspec +17 -0
- data/lib/chronic/chronic.rb +294 -208
- data/lib/chronic/grabber.rb +22 -17
- data/lib/chronic/handler.rb +90 -0
- data/lib/chronic/handlers.rb +365 -278
- data/lib/chronic/mini_date.rb +38 -0
- data/lib/chronic/numerizer.rb +121 -0
- data/lib/chronic/ordinal.rb +23 -19
- data/lib/chronic/pointer.rb +20 -17
- data/lib/chronic/repeater.rb +126 -105
- data/lib/chronic/repeaters/repeater_day.rb +49 -43
- data/lib/chronic/repeaters/repeater_day_name.rb +48 -42
- data/lib/chronic/repeaters/repeater_day_portion.rb +81 -80
- data/lib/chronic/repeaters/repeater_fortnight.rb +59 -53
- data/lib/chronic/repeaters/repeater_hour.rb +49 -43
- data/lib/chronic/repeaters/repeater_minute.rb +55 -39
- data/lib/chronic/repeaters/repeater_month.rb +77 -55
- data/lib/chronic/repeaters/repeater_month_name.rb +83 -82
- data/lib/chronic/repeaters/repeater_season.rb +107 -21
- data/lib/chronic/repeaters/repeater_season_name.rb +41 -22
- data/lib/chronic/repeaters/repeater_second.rb +39 -33
- data/lib/chronic/repeaters/repeater_time.rb +117 -103
- data/lib/chronic/repeaters/repeater_week.rb +63 -57
- data/lib/chronic/repeaters/repeater_weekday.rb +85 -0
- data/lib/chronic/repeaters/repeater_weekend.rb +64 -9
- data/lib/chronic/repeaters/repeater_year.rb +70 -51
- data/lib/chronic/scalar.rb +64 -29
- data/lib/chronic/season.rb +37 -0
- data/lib/chronic/separator.rb +50 -38
- data/lib/chronic/span.rb +31 -0
- data/lib/chronic/tag.rb +42 -0
- data/lib/chronic/time_zone.rb +30 -0
- data/lib/chronic/token.rb +45 -0
- data/lib/chronic.rb +86 -22
- data/test/helper.rb +6 -0
- data/test/test_Chronic.rb +118 -20
- data/test/test_DaylightSavings.rb +118 -0
- data/test/test_Handler.rb +36 -42
- data/test/test_MiniDate.rb +32 -0
- data/test/test_Numerizer.rb +72 -0
- data/test/test_RepeaterDayName.rb +15 -16
- data/test/test_RepeaterFortnight.rb +16 -17
- data/test/test_RepeaterHour.rb +22 -19
- data/test/test_RepeaterMinute.rb +34 -0
- data/test/test_RepeaterMonth.rb +20 -17
- data/test/test_RepeaterMonthName.rb +17 -18
- data/test/test_RepeaterSeason.rb +40 -0
- data/test/test_RepeaterTime.rb +20 -22
- data/test/test_RepeaterWeek.rb +16 -17
- data/test/test_RepeaterWeekday.rb +55 -0
- data/test/test_RepeaterWeekend.rb +74 -0
- data/test/test_RepeaterYear.rb +24 -18
- data/test/test_Span.rb +5 -6
- data/test/test_Token.rb +5 -6
- data/test/test_parsing.rb +743 -338
- metadata +81 -54
- data/History.txt +0 -29
- data/Manifest.txt +0 -43
- data/README.txt +0 -149
- data/test/parse_numbers.rb +0 -50
- data/test/suite.rb +0 -9
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Chronic
|
|
2
|
+
class MiniDate
|
|
3
|
+
attr_accessor :month, :day
|
|
4
|
+
|
|
5
|
+
def self.from_time(time)
|
|
6
|
+
new(time.month, time.day)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def initialize(month, day)
|
|
10
|
+
unless (1..12).include?(month)
|
|
11
|
+
raise ArgumentError, "1..12 are valid months"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
@month = month
|
|
15
|
+
@day = day
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def is_between?(md_start, md_end)
|
|
19
|
+
return false if (@month == md_start.month && @month == md_end.month) &&
|
|
20
|
+
(@day < md_start.day || @day > md_end.day)
|
|
21
|
+
return true if (@month == md_start.month && @day >= md_start.day) ||
|
|
22
|
+
(@month == md_end.month && @day <= md_end.day)
|
|
23
|
+
|
|
24
|
+
i = (md_start.month % 12) + 1
|
|
25
|
+
|
|
26
|
+
until i == md_end.month
|
|
27
|
+
return true if @month == i
|
|
28
|
+
i = (i % 12) + 1
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
return false
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def equals?(other)
|
|
35
|
+
@month == other.month and @day == other.day
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,121 @@
|
|
|
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
|
+
|
|
111
|
+
while sc.scan_until(/<num>(\d+)( | and )<num>(\d+)(?=[^\w]|$)/i)
|
|
112
|
+
if sc[2] =~ /and/ || sc[1].size > sc[3].size
|
|
113
|
+
string[(sc.pos - sc.matched_size)..(sc.pos-1)] = '<num>' + (sc[1].to_i + sc[3].to_i).to_s
|
|
114
|
+
sc.reset
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
data/lib/chronic/ordinal.rb
CHANGED
|
@@ -1,40 +1,44 @@
|
|
|
1
1
|
module Chronic
|
|
2
|
+
class Ordinal < Tag
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
# Scan an Array of {Token}s and apply any necessary Ordinal tags to
|
|
5
|
+
# each token
|
|
6
|
+
#
|
|
7
|
+
# @param [Array<Token>] tokens Array of tokens to scan
|
|
8
|
+
# @param [Hash] options Options specified in {Chronic.parse}
|
|
9
|
+
# @return [Array] list of tokens
|
|
10
|
+
def self.scan(tokens, options)
|
|
11
|
+
tokens.each do |token|
|
|
12
|
+
if t = scan_for_ordinals(token) then token.tag(t) end
|
|
13
|
+
if t = scan_for_days(token) then token.tag(t) end
|
|
9
14
|
end
|
|
10
|
-
tokens
|
|
11
15
|
end
|
|
12
|
-
|
|
16
|
+
|
|
17
|
+
# @param [Token] token
|
|
18
|
+
# @return [Ordinal, nil]
|
|
13
19
|
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
|
|
20
|
+
Ordinal.new($1.to_i) if token.word =~ /^(\d*)(st|nd|rd|th)$/
|
|
18
21
|
end
|
|
19
|
-
|
|
22
|
+
|
|
23
|
+
# @param [Token] token
|
|
24
|
+
# @return [OrdinalDay, nil]
|
|
20
25
|
def self.scan_for_days(token)
|
|
21
26
|
if token.word =~ /^(\d*)(st|nd|rd|th)$/
|
|
22
|
-
unless $1.to_i > 31
|
|
23
|
-
|
|
27
|
+
unless $1.to_i > 31 || $1.to_i < 1
|
|
28
|
+
OrdinalDay.new(token.word.to_i)
|
|
24
29
|
end
|
|
25
30
|
end
|
|
26
|
-
return nil
|
|
27
31
|
end
|
|
28
|
-
|
|
32
|
+
|
|
29
33
|
def to_s
|
|
30
34
|
'ordinal'
|
|
31
35
|
end
|
|
32
36
|
end
|
|
33
|
-
|
|
37
|
+
|
|
34
38
|
class OrdinalDay < Ordinal #:nodoc:
|
|
35
39
|
def to_s
|
|
36
40
|
super << '-day-' << @type.to_s
|
|
37
41
|
end
|
|
38
42
|
end
|
|
39
43
|
|
|
40
|
-
end
|
|
44
|
+
end
|
data/lib/chronic/pointer.rb
CHANGED
|
@@ -1,27 +1,30 @@
|
|
|
1
1
|
module Chronic
|
|
2
|
+
class Pointer < Tag
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
# Scan an Array of {Token}s and apply any necessary Pointer tags to
|
|
5
|
+
# each token
|
|
6
|
+
#
|
|
7
|
+
# @param [Array<Token>] tokens Array of tokens to scan
|
|
8
|
+
# @param [Hash] options Options specified in {Chronic.parse}
|
|
9
|
+
# @return [Array] list of tokens
|
|
10
|
+
def self.scan(tokens, options)
|
|
11
|
+
tokens.each do |token|
|
|
12
|
+
if t = scan_for_all(token) then token.tag(t) end
|
|
8
13
|
end
|
|
9
|
-
tokens
|
|
10
14
|
end
|
|
11
|
-
|
|
15
|
+
|
|
16
|
+
# @param [Token] token
|
|
17
|
+
# @return [Pointer, nil]
|
|
12
18
|
def self.scan_for_all(token)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
end
|
|
19
|
-
return nil
|
|
19
|
+
scan_for token, self,
|
|
20
|
+
{
|
|
21
|
+
/\bpast\b/ => :past,
|
|
22
|
+
/\b(?:future|in)\b/ => :future,
|
|
23
|
+
}
|
|
20
24
|
end
|
|
21
|
-
|
|
25
|
+
|
|
22
26
|
def to_s
|
|
23
27
|
'pointer-' << @type.to_s
|
|
24
28
|
end
|
|
25
29
|
end
|
|
26
|
-
|
|
27
|
-
end
|
|
30
|
+
end
|
data/lib/chronic/repeater.rb
CHANGED
|
@@ -1,114 +1,135 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
module Chronic
|
|
2
|
+
class Repeater < Tag
|
|
3
|
+
|
|
4
|
+
# Scan an Array of {Token}s and apply any necessary Repeater tags to
|
|
5
|
+
# each token
|
|
6
|
+
#
|
|
7
|
+
# @param [Array<Token>] tokens Array of tokens to scan
|
|
8
|
+
# @param [Hash] options Options specified in {Chronic.parse}
|
|
9
|
+
# @return [Array] list of tokens
|
|
10
|
+
def self.scan(tokens, options)
|
|
11
|
+
tokens.each do |token|
|
|
12
|
+
if t = scan_for_season_names(token) then token.tag(t); next end
|
|
13
|
+
if t = scan_for_month_names(token) then token.tag(t); next end
|
|
14
|
+
if t = scan_for_day_names(token) then token.tag(t); next end
|
|
15
|
+
if t = scan_for_day_portions(token) then token.tag(t); next end
|
|
16
|
+
if t = scan_for_times(token) then token.tag(t); next end
|
|
17
|
+
if t = scan_for_units(token) then token.tag(t); next end
|
|
18
|
+
end
|
|
10
19
|
end
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
|
20
|
+
|
|
21
|
+
# @param [Token] token
|
|
22
|
+
# @return [RepeaterSeasonName, nil]
|
|
23
|
+
def self.scan_for_season_names(token)
|
|
24
|
+
scan_for token, RepeaterSeasonName,
|
|
25
|
+
{
|
|
26
|
+
/^springs?$/ => :spring,
|
|
27
|
+
/^summers?$/ => :summer,
|
|
28
|
+
/^(autumn)|(fall)s?$/ => :autumn,
|
|
29
|
+
/^winters?$/ => :winter
|
|
30
|
+
}
|
|
29
31
|
end
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
32
|
+
|
|
33
|
+
# @param [Token] token
|
|
34
|
+
# @return [RepeaterMonthName, nil]
|
|
35
|
+
def self.scan_for_month_names(token)
|
|
36
|
+
scan_for token, RepeaterMonthName,
|
|
37
|
+
{
|
|
38
|
+
/^jan\.?(uary)?$/ => :january,
|
|
39
|
+
/^feb\.?(ruary)?$/ => :february,
|
|
40
|
+
/^mar\.?(ch)?$/ => :march,
|
|
41
|
+
/^apr\.?(il)?$/ => :april,
|
|
42
|
+
/^may$/ => :may,
|
|
43
|
+
/^jun\.?e?$/ => :june,
|
|
44
|
+
/^jul\.?y?$/ => :july,
|
|
45
|
+
/^aug\.?(ust)?$/ => :august,
|
|
46
|
+
/^sep\.?(t\.?|tember)?$/ => :september,
|
|
47
|
+
/^oct\.?(ober)?$/ => :october,
|
|
48
|
+
/^nov\.?(ember)?$/ => :november,
|
|
49
|
+
/^dec\.?(ember)?$/ => :december
|
|
50
|
+
}
|
|
45
51
|
end
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
52
|
+
|
|
53
|
+
# @param [Token] token
|
|
54
|
+
# @return [RepeaterDayName, nil]
|
|
55
|
+
def self.scan_for_day_names(token)
|
|
56
|
+
scan_for token, RepeaterDayName,
|
|
57
|
+
{
|
|
58
|
+
/^m[ou]n(day)?$/ => :monday,
|
|
59
|
+
/^t(ue|eu|oo|u|)s?(day)?$/ => :tuesday,
|
|
60
|
+
/^we(d|dnes|nds|nns)(day)?$/ => :wednesday,
|
|
61
|
+
/^th(u(?:rs)?|ers)(day)?$/ => :thursday,
|
|
62
|
+
/^fr[iy](day)?$/ => :friday,
|
|
63
|
+
/^sat(t?[ue]rday)?$/ => :saturday,
|
|
64
|
+
/^su[nm](day)?$/ => :sunday
|
|
65
|
+
}
|
|
58
66
|
end
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
67
|
+
|
|
68
|
+
# @param [Token] token
|
|
69
|
+
# @return [RepeaterDayPortion, nil]
|
|
70
|
+
def self.scan_for_day_portions(token)
|
|
71
|
+
scan_for token, RepeaterDayPortion,
|
|
72
|
+
{
|
|
73
|
+
/^ams?$/ => :am,
|
|
74
|
+
/^pms?$/ => :pm,
|
|
75
|
+
/^mornings?$/ => :morning,
|
|
76
|
+
/^afternoons?$/ => :afternoon,
|
|
77
|
+
/^evenings?$/ => :evening,
|
|
78
|
+
/^(night|nite)s?$/ => :night
|
|
79
|
+
}
|
|
65
80
|
end
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
|
|
82
|
+
# @param [Token] token
|
|
83
|
+
# @return [RepeaterTime, nil]
|
|
84
|
+
def self.scan_for_times(token)
|
|
85
|
+
scan_for token, RepeaterTime, /^\d{1,2}(:?\d{2})?([\.:]?\d{2})?$/
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# @param [Token] token
|
|
89
|
+
# @return [Repeater] A new instance of a subclass of Repeater
|
|
90
|
+
def self.scan_for_units(token)
|
|
91
|
+
{
|
|
92
|
+
/^years?$/ => :year,
|
|
93
|
+
/^seasons?$/ => :season,
|
|
94
|
+
/^months?$/ => :month,
|
|
95
|
+
/^fortnights?$/ => :fortnight,
|
|
96
|
+
/^weeks?$/ => :week,
|
|
97
|
+
/^weekends?$/ => :weekend,
|
|
98
|
+
/^(week|business)days?$/ => :weekday,
|
|
99
|
+
/^days?$/ => :day,
|
|
100
|
+
/^hours?$/ => :hour,
|
|
101
|
+
/^minutes?$/ => :minute,
|
|
102
|
+
/^seconds?$/ => :second
|
|
103
|
+
}.each do |item, symbol|
|
|
104
|
+
if item =~ token.word
|
|
105
|
+
klass_name = 'Repeater' + symbol.to_s.capitalize
|
|
106
|
+
klass = Chronic.const_get(klass_name)
|
|
107
|
+
return klass.new(symbol)
|
|
108
|
+
end
|
|
85
109
|
end
|
|
110
|
+
return nil
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def <=>(other)
|
|
114
|
+
width <=> other.width
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# returns the width (in seconds or months) of this repeatable.
|
|
118
|
+
def width
|
|
119
|
+
raise("Repeater#width must be overridden in subclasses")
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# returns the next occurance of this repeatable.
|
|
123
|
+
def next(pointer)
|
|
124
|
+
raise("Start point must be set before calling #next") unless @now
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def this(pointer)
|
|
128
|
+
raise("Start point must be set before calling #this") unless @now
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def to_s
|
|
132
|
+
'repeater'
|
|
86
133
|
end
|
|
87
|
-
return nil
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
def <=>(other)
|
|
91
|
-
width <=> other.width
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
# returns the width (in seconds or months) of this repeatable.
|
|
95
|
-
def width
|
|
96
|
-
raise("Repeatable#width must be overridden in subclasses")
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
# returns the next occurance of this repeatable.
|
|
100
|
-
def next(pointer)
|
|
101
|
-
!@now.nil? || raise("Start point must be set before calling #next")
|
|
102
|
-
[:future, :none, :past].include?(pointer) || raise("First argument 'pointer' must be one of :past or :future")
|
|
103
|
-
#raise("Repeatable#next must be overridden in subclasses")
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def this(pointer)
|
|
107
|
-
!@now.nil? || raise("Start point must be set before calling #next")
|
|
108
|
-
[:future, :past, :none].include?(pointer) || raise("First argument 'pointer' must be one of :past, :future, :none")
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
def to_s
|
|
112
|
-
'repeater'
|
|
113
134
|
end
|
|
114
135
|
end
|
|
@@ -1,47 +1,53 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
|
9
7
|
end
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
8
|
+
|
|
9
|
+
def next(pointer)
|
|
10
|
+
super
|
|
11
|
+
|
|
12
|
+
if !@current_day_start
|
|
13
|
+
@current_day_start = Chronic.time_class.local(@now.year, @now.month, @now.day)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
direction = pointer == :future ? 1 : -1
|
|
17
|
+
@current_day_start += direction * DAY_SECONDS
|
|
18
|
+
|
|
19
|
+
Span.new(@current_day_start, @current_day_start + DAY_SECONDS)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def this(pointer = :future)
|
|
23
|
+
super
|
|
24
|
+
|
|
25
|
+
case pointer
|
|
26
|
+
when :future
|
|
27
|
+
day_begin = Chronic.construct(@now.year, @now.month, @now.day, @now.hour)
|
|
28
|
+
day_end = Chronic.construct(@now.year, @now.month, @now.day) + DAY_SECONDS
|
|
29
|
+
when :past
|
|
30
|
+
day_begin = Chronic.construct(@now.year, @now.month, @now.day)
|
|
31
|
+
day_end = Chronic.construct(@now.year, @now.month, @now.day, @now.hour)
|
|
32
|
+
when :none
|
|
33
|
+
day_begin = Chronic.construct(@now.year, @now.month, @now.day)
|
|
34
|
+
day_end = Chronic.construct(@now.year, @now.month, @now.day) + DAY_SECONDS
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
Span.new(day_begin, day_end)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def offset(span, amount, pointer)
|
|
41
|
+
direction = pointer == :future ? 1 : -1
|
|
42
|
+
span + direction * amount * DAY_SECONDS
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def width
|
|
46
|
+
DAY_SECONDS
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def to_s
|
|
50
|
+
super << '-day'
|
|
30
51
|
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
52
|
end
|
|
47
53
|
end
|