gitlab-chronic 0.10.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.gitlab-ci.yml +14 -0
- data/.travis.yml +10 -0
- data/Gemfile +3 -0
- data/HISTORY.md +252 -0
- data/LICENSE +21 -0
- data/README.md +188 -0
- data/Rakefile +68 -0
- data/chronic.gemspec +25 -0
- data/lib/chronic.rb +155 -0
- data/lib/chronic/date.rb +81 -0
- data/lib/chronic/definition.rb +128 -0
- data/lib/chronic/dictionary.rb +36 -0
- data/lib/chronic/handler.rb +97 -0
- data/lib/chronic/handlers.rb +672 -0
- data/lib/chronic/mini_date.rb +38 -0
- data/lib/chronic/parser.rb +222 -0
- data/lib/chronic/repeaters/repeater_day.rb +54 -0
- data/lib/chronic/repeaters/repeater_day_name.rb +53 -0
- data/lib/chronic/repeaters/repeater_day_portion.rb +109 -0
- data/lib/chronic/repeaters/repeater_fortnight.rb +72 -0
- data/lib/chronic/repeaters/repeater_hour.rb +59 -0
- data/lib/chronic/repeaters/repeater_minute.rb +59 -0
- data/lib/chronic/repeaters/repeater_month.rb +80 -0
- data/lib/chronic/repeaters/repeater_month_name.rb +95 -0
- data/lib/chronic/repeaters/repeater_quarter.rb +59 -0
- data/lib/chronic/repeaters/repeater_quarter_name.rb +40 -0
- data/lib/chronic/repeaters/repeater_season.rb +111 -0
- data/lib/chronic/repeaters/repeater_season_name.rb +43 -0
- data/lib/chronic/repeaters/repeater_second.rb +43 -0
- data/lib/chronic/repeaters/repeater_time.rb +159 -0
- data/lib/chronic/repeaters/repeater_week.rb +76 -0
- data/lib/chronic/repeaters/repeater_weekday.rb +86 -0
- data/lib/chronic/repeaters/repeater_weekend.rb +67 -0
- data/lib/chronic/repeaters/repeater_year.rb +78 -0
- data/lib/chronic/season.rb +26 -0
- data/lib/chronic/span.rb +31 -0
- data/lib/chronic/tag.rb +89 -0
- data/lib/chronic/tags/grabber.rb +29 -0
- data/lib/chronic/tags/ordinal.rb +52 -0
- data/lib/chronic/tags/pointer.rb +28 -0
- data/lib/chronic/tags/repeater.rb +160 -0
- data/lib/chronic/tags/scalar.rb +89 -0
- data/lib/chronic/tags/separator.rb +123 -0
- data/lib/chronic/tags/sign.rb +35 -0
- data/lib/chronic/tags/time_zone.rb +32 -0
- data/lib/chronic/time.rb +40 -0
- data/lib/chronic/token.rb +61 -0
- data/lib/chronic/tokenizer.rb +38 -0
- data/lib/chronic/version.rb +3 -0
- data/test/helper.rb +12 -0
- data/test/test_chronic.rb +203 -0
- data/test/test_daylight_savings.rb +122 -0
- data/test/test_handler.rb +128 -0
- data/test/test_mini_date.rb +32 -0
- data/test/test_parsing.rb +1537 -0
- data/test/test_repeater_day_name.rb +51 -0
- data/test/test_repeater_day_portion.rb +254 -0
- data/test/test_repeater_fortnight.rb +62 -0
- data/test/test_repeater_hour.rb +68 -0
- data/test/test_repeater_minute.rb +34 -0
- data/test/test_repeater_month.rb +50 -0
- data/test/test_repeater_month_name.rb +56 -0
- data/test/test_repeater_quarter.rb +70 -0
- data/test/test_repeater_quarter_name.rb +198 -0
- data/test/test_repeater_season.rb +40 -0
- data/test/test_repeater_time.rb +88 -0
- data/test/test_repeater_week.rb +115 -0
- data/test/test_repeater_weekday.rb +55 -0
- data/test/test_repeater_weekend.rb +74 -0
- data/test/test_repeater_year.rb +69 -0
- data/test/test_span.rb +23 -0
- data/test/test_token.rb +31 -0
- metadata +215 -0
@@ -0,0 +1,67 @@
|
|
1
|
+
module Chronic
|
2
|
+
class RepeaterWeekend < Repeater #:nodoc:
|
3
|
+
WEEKEND_SECONDS = 172_800 # (2 * 24 * 60 * 60)
|
4
|
+
|
5
|
+
def initialize(type, width = nil, options = {})
|
6
|
+
super
|
7
|
+
@current_week_start = nil
|
8
|
+
end
|
9
|
+
|
10
|
+
def next(pointer)
|
11
|
+
super
|
12
|
+
|
13
|
+
unless @current_week_start
|
14
|
+
case pointer
|
15
|
+
when :future
|
16
|
+
saturday_repeater = RepeaterDayName.new(:saturday)
|
17
|
+
saturday_repeater.start = @now
|
18
|
+
next_saturday_span = saturday_repeater.next(:future)
|
19
|
+
@current_week_start = next_saturday_span.begin
|
20
|
+
when :past
|
21
|
+
saturday_repeater = RepeaterDayName.new(:saturday)
|
22
|
+
saturday_repeater.start = (@now + RepeaterDay::DAY_SECONDS)
|
23
|
+
last_saturday_span = saturday_repeater.next(:past)
|
24
|
+
@current_week_start = last_saturday_span.begin
|
25
|
+
end
|
26
|
+
else
|
27
|
+
direction = pointer == :future ? 1 : -1
|
28
|
+
@current_week_start += direction * RepeaterWeek::WEEK_SECONDS
|
29
|
+
end
|
30
|
+
|
31
|
+
Span.new(@current_week_start, @current_week_start + WEEKEND_SECONDS)
|
32
|
+
end
|
33
|
+
|
34
|
+
def this(pointer = :future)
|
35
|
+
super
|
36
|
+
|
37
|
+
case pointer
|
38
|
+
when :future, :none
|
39
|
+
saturday_repeater = RepeaterDayName.new(:saturday)
|
40
|
+
saturday_repeater.start = @now
|
41
|
+
this_saturday_span = saturday_repeater.this(:future)
|
42
|
+
Span.new(this_saturday_span.begin, this_saturday_span.begin + WEEKEND_SECONDS)
|
43
|
+
when :past
|
44
|
+
saturday_repeater = RepeaterDayName.new(:saturday)
|
45
|
+
saturday_repeater.start = @now
|
46
|
+
last_saturday_span = saturday_repeater.this(:past)
|
47
|
+
Span.new(last_saturday_span.begin, last_saturday_span.begin + WEEKEND_SECONDS)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def offset(span, amount, pointer)
|
52
|
+
direction = pointer == :future ? 1 : -1
|
53
|
+
weekend = RepeaterWeekend.new(:weekend)
|
54
|
+
weekend.start = span.begin
|
55
|
+
start = weekend.next(pointer).begin + (amount - 1) * direction * RepeaterWeek::WEEK_SECONDS
|
56
|
+
Span.new(start, start + (span.end - span.begin))
|
57
|
+
end
|
58
|
+
|
59
|
+
def width
|
60
|
+
WEEKEND_SECONDS
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_s
|
64
|
+
super << '-weekend'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Chronic
|
2
|
+
class RepeaterYear < Repeater #:nodoc:
|
3
|
+
YEAR_SECONDS = 31536000 # 365 * 24 * 60 * 60
|
4
|
+
|
5
|
+
def initialize(type, width = nil, options = {})
|
6
|
+
super
|
7
|
+
@current_year_start = nil
|
8
|
+
end
|
9
|
+
|
10
|
+
def next(pointer)
|
11
|
+
super
|
12
|
+
|
13
|
+
unless @current_year_start
|
14
|
+
case pointer
|
15
|
+
when :future
|
16
|
+
@current_year_start = Chronic.construct(@now.year + 1)
|
17
|
+
when :past
|
18
|
+
@current_year_start = Chronic.construct(@now.year - 1)
|
19
|
+
end
|
20
|
+
else
|
21
|
+
diff = pointer == :future ? 1 : -1
|
22
|
+
@current_year_start = Chronic.construct(@current_year_start.year + diff)
|
23
|
+
end
|
24
|
+
|
25
|
+
Span.new(@current_year_start, Chronic.construct(@current_year_start.year + 1))
|
26
|
+
end
|
27
|
+
|
28
|
+
def this(pointer = :future)
|
29
|
+
super
|
30
|
+
|
31
|
+
case pointer
|
32
|
+
when :future
|
33
|
+
this_year_start = Chronic.construct(@now.year, @now.month, @now.day + 1)
|
34
|
+
this_year_end = Chronic.construct(@now.year + 1, 1, 1)
|
35
|
+
when :past
|
36
|
+
this_year_start = Chronic.construct(@now.year, 1, 1)
|
37
|
+
this_year_end = Chronic.construct(@now.year, @now.month, @now.day)
|
38
|
+
when :none
|
39
|
+
this_year_start = Chronic.construct(@now.year, 1, 1)
|
40
|
+
this_year_end = Chronic.construct(@now.year + 1, 1, 1)
|
41
|
+
end
|
42
|
+
|
43
|
+
Span.new(this_year_start, this_year_end)
|
44
|
+
end
|
45
|
+
|
46
|
+
def offset(span, amount, pointer)
|
47
|
+
direction = pointer == :future ? 1 : -1
|
48
|
+
new_begin = build_offset_time(span.begin, amount, direction)
|
49
|
+
new_end = build_offset_time(span.end, amount, direction)
|
50
|
+
Span.new(new_begin, new_end)
|
51
|
+
end
|
52
|
+
|
53
|
+
def width
|
54
|
+
YEAR_SECONDS
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_s
|
58
|
+
super << '-year'
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def build_offset_time(time, amount, direction)
|
64
|
+
year = time.year + (amount * direction)
|
65
|
+
days = month_days(year, time.month)
|
66
|
+
day = time.day > days ? days : time.day
|
67
|
+
Chronic.construct(year, time.month, day, time.hour, time.min, time.sec)
|
68
|
+
end
|
69
|
+
|
70
|
+
def month_days(year, month)
|
71
|
+
if ::Date.leap?(year)
|
72
|
+
RepeaterMonth::MONTH_DAYS_LEAP[month - 1]
|
73
|
+
else
|
74
|
+
RepeaterMonth::MONTH_DAYS[month - 1]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Chronic
|
2
|
+
class Season
|
3
|
+
|
4
|
+
attr_reader :start
|
5
|
+
attr_reader :end
|
6
|
+
|
7
|
+
def initialize(start_date, end_date)
|
8
|
+
@start = start_date
|
9
|
+
@end = end_date
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.find_next_season(season, pointer)
|
13
|
+
lookup = [:spring, :summer, :autumn, :winter]
|
14
|
+
next_season_num = (lookup.index(season) + 1 * pointer) % 4
|
15
|
+
lookup[next_season_num]
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.season_after(season)
|
19
|
+
find_next_season(season, +1)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.season_before(season)
|
23
|
+
find_next_season(season, -1)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/chronic/span.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
module Chronic
|
2
|
+
# A Span represents a range of time. Since this class extends
|
3
|
+
# Range, you can use #begin and #end to get the beginning and
|
4
|
+
# ending times of the span (they will be of class Time)
|
5
|
+
class Span < Range
|
6
|
+
# Returns the width of this span in seconds
|
7
|
+
def width
|
8
|
+
(self.end - self.begin).to_i
|
9
|
+
end
|
10
|
+
|
11
|
+
# Add a number of seconds to this span, returning the
|
12
|
+
# resulting Span
|
13
|
+
def +(seconds)
|
14
|
+
Span.new(self.begin + seconds, self.end + seconds)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Subtract a number of seconds to this span, returning the
|
18
|
+
# resulting Span
|
19
|
+
def -(seconds)
|
20
|
+
self + -seconds
|
21
|
+
end
|
22
|
+
|
23
|
+
# Prints this span in a nice fashion
|
24
|
+
def to_s
|
25
|
+
'(' << self.begin.to_s << '..' << self.end.to_s << ')'
|
26
|
+
end
|
27
|
+
|
28
|
+
alias :cover? :include? if RUBY_VERSION =~ /^1.8/
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
data/lib/chronic/tag.rb
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
module Chronic
|
2
|
+
# Tokens are tagged with subclassed instances of this class when
|
3
|
+
# they match specific criteria.
|
4
|
+
class Tag
|
5
|
+
|
6
|
+
attr_accessor :type
|
7
|
+
attr_accessor :width
|
8
|
+
|
9
|
+
# type - The Symbol type of this tag.
|
10
|
+
def initialize(type, width = nil, options = {})
|
11
|
+
@type = type
|
12
|
+
@width = width
|
13
|
+
@options = options
|
14
|
+
end
|
15
|
+
|
16
|
+
# time - Set the start Time for this Tag.
|
17
|
+
def start=(time)
|
18
|
+
@now = time
|
19
|
+
end
|
20
|
+
|
21
|
+
class << self
|
22
|
+
# Public: Scan an Array of Token objects.
|
23
|
+
#
|
24
|
+
# tokens - An Array of tokens to scan.
|
25
|
+
# options - The Hash of options specified in Chronic::parse.
|
26
|
+
#
|
27
|
+
# Returns an Array of tokens.
|
28
|
+
def scan(tokens, options)
|
29
|
+
raise NotImplementedError, 'Subclasses must override scan!'
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
# Internal: Match item and create respective Tag class.
|
35
|
+
# When item is a Symbol it will match only when it's identical to Token.
|
36
|
+
# When it's a String it will case-insesitively match partial token,
|
37
|
+
# but only if item's last char have different type than token text's next char.
|
38
|
+
# When item is a Regexp it will match by it.
|
39
|
+
#
|
40
|
+
# item - Item to match. It can be String, Symbol or Regexp.
|
41
|
+
# klass - Tag class to create.
|
42
|
+
# symbol - Tag type as symbol or string to pass to Tag class.
|
43
|
+
# token - Token to match against.
|
44
|
+
# options - Options as hash to pass to Tag class.
|
45
|
+
#
|
46
|
+
# Returns an instance of specified Tag klass or nil if item didn't match.
|
47
|
+
def match_item(item, klass, symbol, token, options)
|
48
|
+
match = false
|
49
|
+
case item
|
50
|
+
when String
|
51
|
+
item_type = Tokenizer.char_type(item.to_s[-1])
|
52
|
+
text_type = token.text[token.position+item.length]
|
53
|
+
text_type = Tokenizer.char_type(text_type) if text_type
|
54
|
+
compatible = true
|
55
|
+
compatible = item_type != text_type if text_type && (item_type == :letter || item_type == :digit)
|
56
|
+
match = compatible && token.text[token.position, item.length].casecmp(item).zero?
|
57
|
+
when Symbol
|
58
|
+
match = token.word == item.to_s
|
59
|
+
when Regexp
|
60
|
+
match = token.word =~ item
|
61
|
+
end
|
62
|
+
return klass.new(symbol, nil, options) if match
|
63
|
+
nil
|
64
|
+
end
|
65
|
+
|
66
|
+
# Internal: Scan for specified items and create respective Tag class.
|
67
|
+
#
|
68
|
+
# token - Token to match against.
|
69
|
+
# klass - Tag class to create.
|
70
|
+
# items - Item(s) to match. It can be Hash, String, Symbol or Regexp.
|
71
|
+
# Hash keys can be String, Symbol or Regexp, but values much be Symbol.
|
72
|
+
# options - Options as hash to pass to Tag class.
|
73
|
+
#
|
74
|
+
# Returns an instance of specified Tag klass or nil if item(s) didn't match.
|
75
|
+
def scan_for(token, klass, items, options = {})
|
76
|
+
if items.kind_of?(Hash)
|
77
|
+
items.each do |item, symbol|
|
78
|
+
scanned = match_item(item, klass, symbol, token, options)
|
79
|
+
return scanned if scanned
|
80
|
+
end
|
81
|
+
else
|
82
|
+
return match_item(items, klass, token.word, token, options)
|
83
|
+
end
|
84
|
+
nil
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Chronic
|
2
|
+
class Grabber < Tag
|
3
|
+
|
4
|
+
# Scan an Array of Tokens and apply any necessary Grabber tags to
|
5
|
+
# each token.
|
6
|
+
#
|
7
|
+
# tokens - An Array of Token objects to scan.
|
8
|
+
# options - The Hash of options specified in Chronic::parse.
|
9
|
+
#
|
10
|
+
# Returns an Array of Token objects.
|
11
|
+
def self.scan(tokens, options)
|
12
|
+
tokens.each do |token|
|
13
|
+
token.tag scan_for(token, self, patterns, options)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.patterns
|
18
|
+
@@patterns ||= {
|
19
|
+
'last' => :last,
|
20
|
+
'this' => :this,
|
21
|
+
'next' => :next
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_s
|
26
|
+
'grabber-' << @type.to_s
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Chronic
|
2
|
+
class Ordinal < Tag
|
3
|
+
|
4
|
+
# Scan an Array of Token objects and apply any necessary Ordinal
|
5
|
+
# tags to each token.
|
6
|
+
#
|
7
|
+
# tokens - An Array of tokens to scan.
|
8
|
+
# options - The Hash of options specified in Chronic::parse.
|
9
|
+
#
|
10
|
+
# Returns an Array of tokens.
|
11
|
+
def self.scan(tokens, options)
|
12
|
+
tokens.each_index do |i|
|
13
|
+
if tokens[i].word =~ /^(\d+)(st|nd|rd|th|\.)$/
|
14
|
+
width = $1.length
|
15
|
+
ordinal = $1.to_i
|
16
|
+
tokens[i].tag(Ordinal.new(ordinal, width))
|
17
|
+
tokens[i].tag(OrdinalDay.new(ordinal, width)) if Chronic::Date::could_be_day?(ordinal, width)
|
18
|
+
tokens[i].tag(OrdinalMonth.new(ordinal, width)) if Chronic::Date::could_be_month?(ordinal, width)
|
19
|
+
if Chronic::Date::could_be_year?(ordinal, width)
|
20
|
+
year = Chronic::Date::make_year(ordinal, options[:ambiguous_year_future_bias])
|
21
|
+
tokens[i].tag(OrdinalYear.new(year.to_i, width))
|
22
|
+
end
|
23
|
+
elsif tokens[i].word =~ /^second$/
|
24
|
+
tokens[i].tag(Ordinal.new(2, 1))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
'ordinal'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class OrdinalDay < Ordinal #:nodoc:
|
35
|
+
def to_s
|
36
|
+
super << '-day-' << @type.to_s
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class OrdinalMonth < Ordinal #:nodoc:
|
41
|
+
def to_s
|
42
|
+
super << '-month-' << @type.to_s
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class OrdinalYear < Ordinal #:nodoc:
|
47
|
+
def to_s
|
48
|
+
super << '-year-' << @type.to_s
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Chronic
|
2
|
+
class Pointer < Tag
|
3
|
+
|
4
|
+
# Scan an Array of Token objects and apply any necessary Pointer
|
5
|
+
# tags to each token.
|
6
|
+
#
|
7
|
+
# tokens - An Array of tokens to scan.
|
8
|
+
# options - The Hash of options specified in Chronic::parse.
|
9
|
+
#
|
10
|
+
# Returns an Array of tokens.
|
11
|
+
def self.scan(tokens, options)
|
12
|
+
tokens.each do |token|
|
13
|
+
token.tag scan_for(token, self, patterns, options)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.patterns
|
18
|
+
@@patterns ||= {
|
19
|
+
'past' => :past,
|
20
|
+
/^future|in$/i => :future,
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
'pointer-' << @type.to_s
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
module Chronic
|
2
|
+
class Repeater < Tag
|
3
|
+
|
4
|
+
# Scan an Array of Token objects and apply any necessary Repeater
|
5
|
+
# tags to each token.
|
6
|
+
#
|
7
|
+
# tokens - An Array of tokens to scan.
|
8
|
+
# options - The Hash of options specified in Chronic::parse.
|
9
|
+
#
|
10
|
+
# Returns an Array of tokens.
|
11
|
+
def self.scan(tokens, options)
|
12
|
+
tokens.each do |token|
|
13
|
+
token.tag scan_for_quarter_names(token, options)
|
14
|
+
token.tag scan_for_season_names(token, options)
|
15
|
+
token.tag scan_for_month_names(token, options)
|
16
|
+
token.tag scan_for_day_names(token, options)
|
17
|
+
token.tag scan_for_day_portions(token, options)
|
18
|
+
token.tag scan_for_times(token, options)
|
19
|
+
token.tag scan_for_units(token, options)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# token - The Token object we want to scan.
|
24
|
+
#
|
25
|
+
# Returns a new Repeater object.
|
26
|
+
def self.scan_for_quarter_names(token, options = {})
|
27
|
+
scan_for token, RepeaterQuarterName,
|
28
|
+
{
|
29
|
+
/^q1$/ => :q1,
|
30
|
+
/^q2$/ => :q2,
|
31
|
+
/^q3$/ => :q3,
|
32
|
+
/^q4$/ => :q4
|
33
|
+
}, options
|
34
|
+
end
|
35
|
+
|
36
|
+
# token - The Token object we want to scan.
|
37
|
+
#
|
38
|
+
# Returns a new Repeater object.
|
39
|
+
def self.scan_for_season_names(token, options = {})
|
40
|
+
scan_for token, RepeaterSeasonName,
|
41
|
+
{
|
42
|
+
/^springs?$/ => :spring,
|
43
|
+
/^summers?$/ => :summer,
|
44
|
+
/^(autumn)|(fall)s?$/ => :autumn,
|
45
|
+
/^winters?$/ => :winter
|
46
|
+
}, options
|
47
|
+
end
|
48
|
+
|
49
|
+
# token - The Token object we want to scan.
|
50
|
+
#
|
51
|
+
# Returns a new Repeater object.
|
52
|
+
def self.scan_for_month_names(token, options = {})
|
53
|
+
scan_for token, RepeaterMonthName,
|
54
|
+
{
|
55
|
+
/^jan[:\.]?(uary)?$/ => :january,
|
56
|
+
/^feb[:\.]?(ruary)?$/ => :february,
|
57
|
+
/^mar[:\.]?(ch)?$/ => :march,
|
58
|
+
/^apr[:\.]?(il)?$/ => :april,
|
59
|
+
/^may$/ => :may,
|
60
|
+
/^jun[:\.]?e?$/ => :june,
|
61
|
+
/^jul[:\.]?y?$/ => :july,
|
62
|
+
/^aug[:\.]?(ust)?$/ => :august,
|
63
|
+
/^sep[:\.]?(t[:\.]?|tember)?$/ => :september,
|
64
|
+
/^oct[:\.]?(ober)?$/ => :october,
|
65
|
+
/^nov[:\.]?(ember)?$/ => :november,
|
66
|
+
/^dec[:\.]?(ember)?$/ => :december
|
67
|
+
}, options
|
68
|
+
end
|
69
|
+
|
70
|
+
# token - The Token object we want to scan.
|
71
|
+
#
|
72
|
+
# Returns a new Repeater object.
|
73
|
+
def self.scan_for_day_names(token, options = {})
|
74
|
+
scan_for token, RepeaterDayName,
|
75
|
+
{
|
76
|
+
/^m[ou]n(day)?$/ => :monday,
|
77
|
+
/^t(ue|eu|oo|u)s?(day)?$/ => :tuesday,
|
78
|
+
/^we(d|dnes|nds|nns)(day)?$/ => :wednesday,
|
79
|
+
/^th(u|ur|urs|ers)(day)?$/ => :thursday,
|
80
|
+
/^fr[iy](day)?$/ => :friday,
|
81
|
+
/^sat(t?[ue]rday)?$/ => :saturday,
|
82
|
+
/^su[nm](day)?$/ => :sunday
|
83
|
+
}, options
|
84
|
+
end
|
85
|
+
|
86
|
+
# token - The Token object we want to scan.
|
87
|
+
#
|
88
|
+
# Returns a new Repeater object.
|
89
|
+
def self.scan_for_day_portions(token, options = {})
|
90
|
+
scan_for token, RepeaterDayPortion,
|
91
|
+
{
|
92
|
+
/^ams?$/ => :am,
|
93
|
+
/^pms?$/ => :pm,
|
94
|
+
/^mornings?$/ => :morning,
|
95
|
+
/^afternoons?$/ => :afternoon,
|
96
|
+
/^evenings?$/ => :evening,
|
97
|
+
/^(night|nite)s?$/ => :night
|
98
|
+
}, options
|
99
|
+
end
|
100
|
+
|
101
|
+
# token - The Token object we want to scan.
|
102
|
+
#
|
103
|
+
# Returns a new Repeater object.
|
104
|
+
def self.scan_for_times(token, options = {})
|
105
|
+
scan_for token, RepeaterTime, /^\d{1,2}(:?\d{1,2})?([\.:]?\d{1,2}([\.:]\d{1,6})?)?$/, options
|
106
|
+
end
|
107
|
+
|
108
|
+
# token - The Token object we want to scan.
|
109
|
+
#
|
110
|
+
# Returns a new Repeater object.
|
111
|
+
def self.scan_for_units(token, options = {})
|
112
|
+
{
|
113
|
+
/^years?$/ => :year,
|
114
|
+
/^q$/ => :quarter,
|
115
|
+
/^seasons?$/ => :season,
|
116
|
+
/^months?$/ => :month,
|
117
|
+
/^fortnights?$/ => :fortnight,
|
118
|
+
/^weeks?$/ => :week,
|
119
|
+
/^weekends?$/ => :weekend,
|
120
|
+
/^(week|business)days?$/ => :weekday,
|
121
|
+
/^days?$/ => :day,
|
122
|
+
/^hrs?$/ => :hour,
|
123
|
+
/^hours?$/ => :hour,
|
124
|
+
/^mins?$/ => :minute,
|
125
|
+
/^minutes?$/ => :minute,
|
126
|
+
/^secs?$/ => :second,
|
127
|
+
/^seconds?$/ => :second
|
128
|
+
}.each do |item, symbol|
|
129
|
+
if item =~ token.word
|
130
|
+
klass_name = 'Repeater' + symbol.to_s.capitalize
|
131
|
+
klass = Chronic.const_get(klass_name)
|
132
|
+
return klass.new(symbol, nil, options)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
return nil
|
136
|
+
end
|
137
|
+
|
138
|
+
def <=>(other)
|
139
|
+
width <=> other.width
|
140
|
+
end
|
141
|
+
|
142
|
+
# returns the width (in seconds or months) of this repeatable.
|
143
|
+
def width
|
144
|
+
raise('Repeater#width must be overridden in subclasses')
|
145
|
+
end
|
146
|
+
|
147
|
+
# returns the next occurance of this repeatable.
|
148
|
+
def next(pointer)
|
149
|
+
raise('Start point must be set before calling #next') unless @now
|
150
|
+
end
|
151
|
+
|
152
|
+
def this(pointer)
|
153
|
+
raise('Start point must be set before calling #this') unless @now
|
154
|
+
end
|
155
|
+
|
156
|
+
def to_s
|
157
|
+
'repeater'
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|