gitlab-chronic 0.10.3
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.
- 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
|