chronic-mmlac 0.6.4.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gemtest +0 -0
- data/.gitignore +5 -0
- data/.yardopts +3 -0
- data/HISTORY.md +174 -0
- data/LICENSE +21 -0
- data/README.md +177 -0
- data/Rakefile +46 -0
- data/chronic.gemspec +17 -0
- data/lib/chronic/chronic.rb +325 -0
- data/lib/chronic/grabber.rb +31 -0
- data/lib/chronic/handler.rb +90 -0
- data/lib/chronic/handlers.rb +465 -0
- data/lib/chronic/mini_date.rb +38 -0
- data/lib/chronic/numerizer.rb +121 -0
- data/lib/chronic/ordinal.rb +44 -0
- data/lib/chronic/pointer.rb +30 -0
- data/lib/chronic/repeater.rb +135 -0
- data/lib/chronic/repeaters/repeater_day.rb +53 -0
- data/lib/chronic/repeaters/repeater_day_name.rb +52 -0
- data/lib/chronic/repeaters/repeater_day_portion.rb +94 -0
- data/lib/chronic/repeaters/repeater_fortnight.rb +71 -0
- data/lib/chronic/repeaters/repeater_hour.rb +58 -0
- data/lib/chronic/repeaters/repeater_minute.rb +58 -0
- data/lib/chronic/repeaters/repeater_month.rb +79 -0
- data/lib/chronic/repeaters/repeater_month_name.rb +94 -0
- data/lib/chronic/repeaters/repeater_season.rb +109 -0
- data/lib/chronic/repeaters/repeater_season_name.rb +43 -0
- data/lib/chronic/repeaters/repeater_second.rb +42 -0
- data/lib/chronic/repeaters/repeater_time.rb +128 -0
- data/lib/chronic/repeaters/repeater_week.rb +74 -0
- data/lib/chronic/repeaters/repeater_weekday.rb +85 -0
- data/lib/chronic/repeaters/repeater_weekend.rb +66 -0
- data/lib/chronic/repeaters/repeater_year.rb +77 -0
- data/lib/chronic/scalar.rb +109 -0
- data/lib/chronic/season.rb +37 -0
- data/lib/chronic/separator.rb +88 -0
- 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 +118 -0
- data/test/helper.rb +6 -0
- data/test/test_Chronic.rb +148 -0
- data/test/test_DaylightSavings.rb +118 -0
- data/test/test_Handler.rb +104 -0
- data/test/test_MiniDate.rb +32 -0
- data/test/test_Numerizer.rb +72 -0
- data/test/test_RepeaterDayName.rb +51 -0
- data/test/test_RepeaterFortnight.rb +62 -0
- data/test/test_RepeaterHour.rb +68 -0
- data/test/test_RepeaterMinute.rb +34 -0
- data/test/test_RepeaterMonth.rb +50 -0
- data/test/test_RepeaterMonthName.rb +56 -0
- data/test/test_RepeaterSeason.rb +40 -0
- data/test/test_RepeaterTime.rb +70 -0
- data/test/test_RepeaterWeek.rb +62 -0
- data/test/test_RepeaterWeekday.rb +55 -0
- data/test/test_RepeaterWeekend.rb +74 -0
- data/test/test_RepeaterYear.rb +69 -0
- data/test/test_Span.rb +23 -0
- data/test/test_Token.rb +25 -0
- data/test/test_parsing.rb +886 -0
- metadata +132 -0
@@ -0,0 +1,109 @@
|
|
1
|
+
module Chronic
|
2
|
+
class Scalar < Tag
|
3
|
+
DAY_PORTIONS = %w( am pm morning afternoon evening night )
|
4
|
+
|
5
|
+
# Scan an Array of {Token}s and apply any necessary Scalar tags to
|
6
|
+
# each token
|
7
|
+
#
|
8
|
+
# @param [Array<Token>] tokens Array of tokens to scan
|
9
|
+
# @param [Hash] options Options specified in {Chronic.parse}
|
10
|
+
# @return [Array] list of tokens
|
11
|
+
def self.scan(tokens, options)
|
12
|
+
tokens.each_index do |i|
|
13
|
+
if t = scan_for_scalars(tokens[i], tokens[i + 1]) then tokens[i].tag(t) end
|
14
|
+
if t = scan_for_days(tokens[i], tokens[i + 1]) then tokens[i].tag(t) end
|
15
|
+
if t = scan_for_months(tokens[i], tokens[i + 1]) then tokens[i].tag(t) end
|
16
|
+
if t = scan_for_years(tokens[i], tokens[i + 1], options) then tokens[i].tag(t) end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# @param [Token] token
|
21
|
+
# @param [Token] post_token
|
22
|
+
# @return [Scalar, nil]
|
23
|
+
def self.scan_for_scalars(token, post_token)
|
24
|
+
if token.word =~ /^\d*$/
|
25
|
+
unless post_token && DAY_PORTIONS.include?(post_token.word)
|
26
|
+
return Scalar.new(token.word.to_i)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# @param [Token] token
|
32
|
+
# @param [Token] post_token
|
33
|
+
# @return [ScalarDay, nil]
|
34
|
+
def self.scan_for_days(token, post_token)
|
35
|
+
if token.word =~ /^\d\d?$/
|
36
|
+
toi = token.word.to_i
|
37
|
+
unless toi > 31 || toi < 1 || (post_token && DAY_PORTIONS.include?(post_token.word))
|
38
|
+
return ScalarDay.new(toi)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# @param [Token] token
|
44
|
+
# @param [Token] post_token
|
45
|
+
# @return [ScalarMonth, nil]
|
46
|
+
def self.scan_for_months(token, post_token)
|
47
|
+
if token.word =~ /^\d\d?$/
|
48
|
+
toi = token.word.to_i
|
49
|
+
unless toi > 12 || toi < 1 || (post_token && DAY_PORTIONS.include?(post_token.word))
|
50
|
+
return ScalarMonth.new(toi)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# @param [Token] token
|
56
|
+
# @param [Token] post_token
|
57
|
+
# @param [Hash] options Options specified in {Chronic.parse}
|
58
|
+
# @return [ScalarYear, nil]
|
59
|
+
def self.scan_for_years(token, post_token, options)
|
60
|
+
if token.word =~ /^([1-9]\d)?\d\d?$/
|
61
|
+
unless post_token && DAY_PORTIONS.include?(post_token.word)
|
62
|
+
year = make_year(token.word.to_i, options[:ambiguous_year_future_bias])
|
63
|
+
return ScalarYear.new(year.to_i)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Build a year from a 2 digit suffix
|
69
|
+
#
|
70
|
+
# @example
|
71
|
+
# make_year(96, 50) #=> 1996
|
72
|
+
# make_year(79, 20) #=> 2079
|
73
|
+
# make_year(00, 50) #=> 2000
|
74
|
+
#
|
75
|
+
# @param [Integer] year The two digit year to build from
|
76
|
+
# @param [Integer] bias The amount of future years to bias
|
77
|
+
# @return [Integer] The 4 digit year
|
78
|
+
def self.make_year(year, bias)
|
79
|
+
return year if year.to_s.size > 2
|
80
|
+
start_year = Chronic.time_class.now.year - bias
|
81
|
+
century = (start_year / 100) * 100
|
82
|
+
full_year = century + year
|
83
|
+
full_year += 100 if full_year < start_year
|
84
|
+
full_year
|
85
|
+
end
|
86
|
+
|
87
|
+
def to_s
|
88
|
+
'scalar'
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
class ScalarDay < Scalar #:nodoc:
|
93
|
+
def to_s
|
94
|
+
super << '-day-' << @type.to_s
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class ScalarMonth < Scalar #:nodoc:
|
99
|
+
def to_s
|
100
|
+
super << '-month-' << @type.to_s
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class ScalarYear < Scalar #:nodoc:
|
105
|
+
def to_s
|
106
|
+
super << '-year-' << @type.to_s
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Chronic
|
2
|
+
class Season
|
3
|
+
# @return [MiniDate]
|
4
|
+
attr_reader :start
|
5
|
+
|
6
|
+
# @return [MiniDate]
|
7
|
+
attr_reader :end
|
8
|
+
|
9
|
+
# @param [MiniDate] start_date
|
10
|
+
# @param [MiniDate] end_date
|
11
|
+
def initialize(start_date, end_date)
|
12
|
+
@start = start_date
|
13
|
+
@end = end_date
|
14
|
+
end
|
15
|
+
|
16
|
+
# @param [Symbol] season The season name
|
17
|
+
# @param [Integer] pointer The direction (-1 for past, 1 for future)
|
18
|
+
# @return [Symbol] The new season name
|
19
|
+
def self.find_next_season(season, pointer)
|
20
|
+
lookup = [:spring, :summer, :autumn, :winter]
|
21
|
+
next_season_num = (lookup.index(season) + 1 * pointer) % 4
|
22
|
+
lookup[next_season_num]
|
23
|
+
end
|
24
|
+
|
25
|
+
# @param [Symbol] season The season name
|
26
|
+
# @return [Symbol] The new season name
|
27
|
+
def self.season_after(season)
|
28
|
+
find_next_season(season, +1)
|
29
|
+
end
|
30
|
+
|
31
|
+
# @param [Symbol] season The season name
|
32
|
+
# @return [Symbol] The new season name
|
33
|
+
def self.season_before(season)
|
34
|
+
find_next_season(season, -1)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Chronic
|
2
|
+
class Separator < Tag
|
3
|
+
|
4
|
+
# Scan an Array of {Token}s and apply any necessary Separator 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_commas(token) then token.tag(t); next end
|
13
|
+
if t = scan_for_slash_or_dash(token) then token.tag(t); next end
|
14
|
+
if t = scan_for_at(token) then token.tag(t); next end
|
15
|
+
if t = scan_for_in(token) then token.tag(t); next end
|
16
|
+
if t = scan_for_on(token) then token.tag(t); next end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# @param [Token] token
|
21
|
+
# @return [SeparatorComma, nil]
|
22
|
+
def self.scan_for_commas(token)
|
23
|
+
scan_for token, SeparatorComma, { /^,$/ => :comma }
|
24
|
+
end
|
25
|
+
|
26
|
+
# @param [Token] token
|
27
|
+
# @return [SeparatorSlashOrDash, nil]
|
28
|
+
def self.scan_for_slash_or_dash(token)
|
29
|
+
scan_for token, SeparatorSlashOrDash,
|
30
|
+
{
|
31
|
+
/^-$/ => :dash,
|
32
|
+
/^\/$/ => :slash
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
# @param [Token] token
|
37
|
+
# @return [SeparatorAt, nil]
|
38
|
+
def self.scan_for_at(token)
|
39
|
+
scan_for token, SeparatorAt, { /^(at|@)$/ => :at }
|
40
|
+
end
|
41
|
+
|
42
|
+
# @param [Token] token
|
43
|
+
# @return [SeparatorIn, nil]
|
44
|
+
def self.scan_for_in(token)
|
45
|
+
scan_for token, SeparatorIn, { /^in$/ => :in }
|
46
|
+
end
|
47
|
+
|
48
|
+
# @param [Token] token
|
49
|
+
# @return [SeparatorOn, nil]
|
50
|
+
def self.scan_for_on(token)
|
51
|
+
scan_for token, SeparatorOn, { /^on$/ => :on }
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_s
|
55
|
+
'separator'
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class SeparatorComma < Separator #:nodoc:
|
60
|
+
def to_s
|
61
|
+
super << '-comma'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class SeparatorSlashOrDash < Separator #:nodoc:
|
66
|
+
def to_s
|
67
|
+
super << '-slashordash-' << @type.to_s
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
class SeparatorAt < Separator #:nodoc:
|
72
|
+
def to_s
|
73
|
+
super << '-at'
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class SeparatorIn < Separator #:nodoc:
|
78
|
+
def to_s
|
79
|
+
super << '-in'
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class SeparatorOn < Separator #:nodoc:
|
84
|
+
def to_s
|
85
|
+
super << '-on'
|
86
|
+
end
|
87
|
+
end
|
88
|
+
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? unless RUBY_VERSION =~ /^1.9/
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
data/lib/chronic/tag.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
module Chronic
|
2
|
+
# Tokens are tagged with subclassed instances of this class when
|
3
|
+
# they match specific criteria
|
4
|
+
class Tag
|
5
|
+
|
6
|
+
# @return [Symbol]
|
7
|
+
attr_accessor :type
|
8
|
+
|
9
|
+
# @param [Symbol] type
|
10
|
+
def initialize(type)
|
11
|
+
@type = type
|
12
|
+
end
|
13
|
+
|
14
|
+
# @param [Time] s Set the start timestamp for this Tag
|
15
|
+
def start=(s)
|
16
|
+
@now = s
|
17
|
+
end
|
18
|
+
|
19
|
+
class << self
|
20
|
+
private
|
21
|
+
|
22
|
+
# @param [Token] token
|
23
|
+
# @param [Class] klass The class instance to create
|
24
|
+
# @param [Regexp, Hash] items
|
25
|
+
# @return [Object, nil] either a new instance of `klass` or `nil` if
|
26
|
+
# no match is found
|
27
|
+
def scan_for(token, klass, items={})
|
28
|
+
case items
|
29
|
+
when Regexp
|
30
|
+
return klass.new(token.word) if items =~ token.word
|
31
|
+
when Hash
|
32
|
+
items.each do |item, symbol|
|
33
|
+
return klass.new(symbol) if item =~ token.word
|
34
|
+
end
|
35
|
+
end
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Chronic
|
2
|
+
class TimeZone < Tag
|
3
|
+
|
4
|
+
# Scan an Array of {Token}s and apply any necessary TimeZone 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); next end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# @param [Token] token
|
17
|
+
# @return [TimeZone, nil]
|
18
|
+
def self.scan_for_all(token)
|
19
|
+
scan_for token, self,
|
20
|
+
{
|
21
|
+
/[PMCE][DS]T|UTC/i => :tz,
|
22
|
+
/(tzminus)?\d{2}:?\d{2}/ => :tz
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
'timezone'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Chronic
|
2
|
+
class Token
|
3
|
+
|
4
|
+
# @return [String] The word this Token represents
|
5
|
+
attr_accessor :word
|
6
|
+
|
7
|
+
# @return [Array] A list of tag associated with this Token
|
8
|
+
attr_accessor :tags
|
9
|
+
|
10
|
+
def initialize(word)
|
11
|
+
@word = word
|
12
|
+
@tags = []
|
13
|
+
end
|
14
|
+
|
15
|
+
# Tag this token with the specified tag
|
16
|
+
#
|
17
|
+
# @param [Tag] new_tag An instance of {Tag} or one of its subclasses
|
18
|
+
def tag(new_tag)
|
19
|
+
@tags << new_tag
|
20
|
+
end
|
21
|
+
|
22
|
+
# Remove all tags of the given class
|
23
|
+
#
|
24
|
+
# @param [Class] The tag class to remove
|
25
|
+
def untag(tag_class)
|
26
|
+
@tags.delete_if { |m| m.kind_of? tag_class }
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [Boolean] true if this token has any tags
|
30
|
+
def tagged?
|
31
|
+
@tags.size > 0
|
32
|
+
end
|
33
|
+
|
34
|
+
# @param [Class] tag_class The tag class to search for
|
35
|
+
# @return [Tag] The first Tag that matches the given class
|
36
|
+
def get_tag(tag_class)
|
37
|
+
@tags.find { |m| m.kind_of? tag_class }
|
38
|
+
end
|
39
|
+
|
40
|
+
# Print this Token in a pretty way
|
41
|
+
def to_s
|
42
|
+
@word << '(' << @tags.join(', ') << ') '
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/chronic.rb
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
# Parse natural language dates and times into Time or {Chronic::Span} objects
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# require 'chronic'
|
8
|
+
#
|
9
|
+
# Time.now #=> Sun Aug 27 23:18:25 PDT 2006
|
10
|
+
#
|
11
|
+
# Chronic.parse('tomorrow')
|
12
|
+
# #=> Mon Aug 28 12:00:00 PDT 2006
|
13
|
+
#
|
14
|
+
# Chronic.parse('monday', :context => :past)
|
15
|
+
# #=> Mon Aug 21 12:00:00 PDT 2006
|
16
|
+
#
|
17
|
+
# Chronic.parse('this tuesday 5:00')
|
18
|
+
# #=> Tue Aug 29 17:00:00 PDT 2006
|
19
|
+
#
|
20
|
+
# Chronic.parse('this tuesday 5:00', :ambiguous_time_range => :none)
|
21
|
+
# #=> Tue Aug 29 05:00:00 PDT 2006
|
22
|
+
#
|
23
|
+
# Chronic.parse('may 27th', :now => Time.local(2000, 1, 1))
|
24
|
+
# #=> Sat May 27 12:00:00 PDT 2000
|
25
|
+
#
|
26
|
+
# Chronic.parse('may 27th', :guess => false)
|
27
|
+
# #=> Sun May 27 00:00:00 PDT 2007..Mon May 28 00:00:00 PDT 2007
|
28
|
+
#
|
29
|
+
# @author Tom Preston-Werner, Lee Jarvis
|
30
|
+
module Chronic
|
31
|
+
VERSION = "0.6.4.2"
|
32
|
+
|
33
|
+
class << self
|
34
|
+
|
35
|
+
# @return [Boolean] true when debug mode is enabled
|
36
|
+
attr_accessor :debug
|
37
|
+
|
38
|
+
# @example
|
39
|
+
# require 'chronic'
|
40
|
+
# require 'active_support/time'
|
41
|
+
#
|
42
|
+
# Time.zone = 'UTC'
|
43
|
+
# Chronic.time_class = Time.zone
|
44
|
+
# Chronic.parse('June 15 2006 at 5:54 AM')
|
45
|
+
# # => Thu, 15 Jun 2006 05:45:00 UTC +00:00
|
46
|
+
#
|
47
|
+
# @return [Time] The time class Chronic uses internally
|
48
|
+
attr_accessor :time_class
|
49
|
+
|
50
|
+
# The current Time Chronic is using to base from
|
51
|
+
#
|
52
|
+
# @example
|
53
|
+
# Time.now #=> 2011-06-06 14:13:43 +0100
|
54
|
+
# Chronic.parse('yesterday') #=> 2011-06-05 12:00:00 +0100
|
55
|
+
#
|
56
|
+
# now = Time.local(2025, 12, 24)
|
57
|
+
# Chronic.parse('tomorrow', :now => now) #=> 2025-12-25 12:00:00 +0000
|
58
|
+
#
|
59
|
+
# @return [Time, nil]
|
60
|
+
attr_accessor :now
|
61
|
+
|
62
|
+
# To ensure thread safety, this value is thread-local.
|
63
|
+
def time_class
|
64
|
+
Thread.current[:__Chronic_time_class] || ::Time
|
65
|
+
end
|
66
|
+
def time_class=(time)
|
67
|
+
Thread.current[:__Chronic_time_class] = time
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
self.debug = false
|
72
|
+
end
|
73
|
+
|
74
|
+
require 'chronic/chronic'
|
75
|
+
require 'chronic/handler'
|
76
|
+
require 'chronic/handlers'
|
77
|
+
require 'chronic/mini_date'
|
78
|
+
require 'chronic/tag'
|
79
|
+
require 'chronic/span'
|
80
|
+
require 'chronic/token'
|
81
|
+
require 'chronic/grabber'
|
82
|
+
require 'chronic/pointer'
|
83
|
+
require 'chronic/scalar'
|
84
|
+
require 'chronic/ordinal'
|
85
|
+
require 'chronic/separator'
|
86
|
+
require 'chronic/time_zone'
|
87
|
+
require 'chronic/numerizer'
|
88
|
+
require 'chronic/season'
|
89
|
+
|
90
|
+
require 'chronic/repeater'
|
91
|
+
require 'chronic/repeaters/repeater_year'
|
92
|
+
require 'chronic/repeaters/repeater_season'
|
93
|
+
require 'chronic/repeaters/repeater_season_name'
|
94
|
+
require 'chronic/repeaters/repeater_month'
|
95
|
+
require 'chronic/repeaters/repeater_month_name'
|
96
|
+
require 'chronic/repeaters/repeater_fortnight'
|
97
|
+
require 'chronic/repeaters/repeater_week'
|
98
|
+
require 'chronic/repeaters/repeater_weekend'
|
99
|
+
require 'chronic/repeaters/repeater_weekday'
|
100
|
+
require 'chronic/repeaters/repeater_day'
|
101
|
+
require 'chronic/repeaters/repeater_day_name'
|
102
|
+
require 'chronic/repeaters/repeater_day_portion'
|
103
|
+
require 'chronic/repeaters/repeater_hour'
|
104
|
+
require 'chronic/repeaters/repeater_minute'
|
105
|
+
require 'chronic/repeaters/repeater_second'
|
106
|
+
require 'chronic/repeaters/repeater_time'
|
107
|
+
|
108
|
+
class Time
|
109
|
+
def self.construct(year, month = 1, day = 1, hour = 0, minute = 0, second = 0)
|
110
|
+
warn "Chronic.construct will be deprecated in version 0.7.0. Please use Chronic.construct instead"
|
111
|
+
Chronic.construct(year, month, day, hour, minute, second)
|
112
|
+
end
|
113
|
+
|
114
|
+
def to_minidate
|
115
|
+
warn "Time.to_minidate will be deprecated in version 0.7.0. Please use Chronic::MiniDate.from_time(time) instead"
|
116
|
+
Chronic::MiniDate.from_time(self)
|
117
|
+
end
|
118
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class TestChronic < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
# Wed Aug 16 14:00:00 UTC 2006
|
7
|
+
@now = Time.local(2006, 8, 16, 14, 0, 0, 0)
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_pre_normalize_numerized_string
|
11
|
+
string = 'two and a half years'
|
12
|
+
assert_equal Chronic::Numerizer.numerize(string), Chronic.pre_normalize(string)
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_post_normalize_am_pm_aliases
|
16
|
+
# affect wanted patterns
|
17
|
+
|
18
|
+
tokens = [Chronic::Token.new("5:00"), Chronic::Token.new("morning")]
|
19
|
+
tokens[0].tag(Chronic::RepeaterTime.new("5:00"))
|
20
|
+
tokens[1].tag(Chronic::RepeaterDayPortion.new(:morning))
|
21
|
+
|
22
|
+
assert_equal :morning, tokens[1].tags[0].type
|
23
|
+
|
24
|
+
tokens = Chronic::Handlers.dealias_and_disambiguate_times(tokens, {})
|
25
|
+
|
26
|
+
assert_equal :am, tokens[1].tags[0].type
|
27
|
+
assert_equal 2, tokens.size
|
28
|
+
|
29
|
+
# don't affect unwanted patterns
|
30
|
+
|
31
|
+
tokens = [Chronic::Token.new("friday"), Chronic::Token.new("morning")]
|
32
|
+
tokens[0].tag(Chronic::RepeaterDayName.new(:friday))
|
33
|
+
tokens[1].tag(Chronic::RepeaterDayPortion.new(:morning))
|
34
|
+
|
35
|
+
assert_equal :morning, tokens[1].tags[0].type
|
36
|
+
|
37
|
+
tokens = Chronic::Handlers.dealias_and_disambiguate_times(tokens, {})
|
38
|
+
|
39
|
+
assert_equal :morning, tokens[1].tags[0].type
|
40
|
+
assert_equal 2, tokens.size
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_guess
|
44
|
+
span = Chronic::Span.new(Time.local(2006, 8, 16, 0), Time.local(2006, 8, 17, 0))
|
45
|
+
assert_equal Time.local(2006, 8, 16, 12), Chronic.guess(span)
|
46
|
+
|
47
|
+
span = Chronic::Span.new(Time.local(2006, 8, 16, 0), Time.local(2006, 8, 17, 0, 0, 1))
|
48
|
+
assert_equal Time.local(2006, 8, 16, 12), Chronic.guess(span)
|
49
|
+
|
50
|
+
span = Chronic::Span.new(Time.local(2006, 11), Time.local(2006, 12))
|
51
|
+
assert_equal Time.local(2006, 11, 16), Chronic.guess(span)
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_now
|
55
|
+
Chronic.parse('now', :now => Time.local(2006, 01))
|
56
|
+
assert_equal Time.local(2006, 01), Chronic.now
|
57
|
+
|
58
|
+
Chronic.parse('now', :now => Time.local(2007, 01))
|
59
|
+
assert_equal Time.local(2007, 01), Chronic.now
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_endian_definitions
|
63
|
+
# middle, little
|
64
|
+
endians = [
|
65
|
+
Chronic::Handler.new([:scalar_month, :separator_slash_or_dash, :scalar_day, :separator_slash_or_dash, :scalar_year, :separator_at?, 'time?'], :handle_sm_sd_sy),
|
66
|
+
Chronic::Handler.new([:scalar_day, :separator_slash_or_dash, :scalar_month, :separator_slash_or_dash, :scalar_year, :separator_at?, 'time?'], :handle_sd_sm_sy)
|
67
|
+
]
|
68
|
+
|
69
|
+
assert_equal endians, Chronic.definitions[:endian]
|
70
|
+
|
71
|
+
defs = Chronic.definitions(:endian_precedence => :little)
|
72
|
+
assert_equal endians.reverse, defs[:endian]
|
73
|
+
|
74
|
+
defs = Chronic.definitions(:endian_precedence => [:little, :middle])
|
75
|
+
assert_equal endians.reverse, defs[:endian]
|
76
|
+
|
77
|
+
assert_raises(ArgumentError) do
|
78
|
+
Chronic.definitions(:endian_precedence => :invalid)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_passing_options
|
83
|
+
assert_raises(ArgumentError) do
|
84
|
+
Chronic.parse('now', :invalid => :option)
|
85
|
+
end
|
86
|
+
|
87
|
+
assert_raises(ArgumentError) do
|
88
|
+
Chronic.parse('now', :context => :invalid_context)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_debug
|
93
|
+
require 'stringio'
|
94
|
+
$stdout = StringIO.new
|
95
|
+
Chronic.debug = true
|
96
|
+
|
97
|
+
Chronic.parse 'now'
|
98
|
+
assert $stdout.string.include?('this(grabber-this)')
|
99
|
+
ensure
|
100
|
+
$stdout = STDOUT
|
101
|
+
Chronic.debug = false
|
102
|
+
end
|
103
|
+
|
104
|
+
# Chronic.construct
|
105
|
+
|
106
|
+
def test_normal
|
107
|
+
assert_equal Time.local(2006, 1, 2, 0, 0, 0), Chronic.construct(2006, 1, 2, 0, 0, 0)
|
108
|
+
assert_equal Time.local(2006, 1, 2, 3, 0, 0), Chronic.construct(2006, 1, 2, 3, 0, 0)
|
109
|
+
assert_equal Time.local(2006, 1, 2, 3, 4, 0), Chronic.construct(2006, 1, 2, 3, 4, 0)
|
110
|
+
assert_equal Time.local(2006, 1, 2, 3, 4, 5), Chronic.construct(2006, 1, 2, 3, 4, 5)
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_second_overflow
|
114
|
+
assert_equal Time.local(2006, 1, 1, 0, 1, 30), Chronic.construct(2006, 1, 1, 0, 0, 90)
|
115
|
+
assert_equal Time.local(2006, 1, 1, 0, 5, 0), Chronic.construct(2006, 1, 1, 0, 0, 300)
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_minute_overflow
|
119
|
+
assert_equal Time.local(2006, 1, 1, 1, 30), Chronic.construct(2006, 1, 1, 0, 90)
|
120
|
+
assert_equal Time.local(2006, 1, 1, 5), Chronic.construct(2006, 1, 1, 0, 300)
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_hour_overflow
|
124
|
+
assert_equal Time.local(2006, 1, 2, 12), Chronic.construct(2006, 1, 1, 36)
|
125
|
+
assert_equal Time.local(2006, 1, 7), Chronic.construct(2006, 1, 1, 144)
|
126
|
+
end
|
127
|
+
|
128
|
+
def test_day_overflow
|
129
|
+
assert_equal Time.local(2006, 2, 1), Chronic.construct(2006, 1, 32)
|
130
|
+
assert_equal Time.local(2006, 3, 5), Chronic.construct(2006, 2, 33)
|
131
|
+
assert_equal Time.local(2004, 3, 4), Chronic.construct(2004, 2, 33)
|
132
|
+
assert_equal Time.local(2000, 3, 4), Chronic.construct(2000, 2, 33)
|
133
|
+
|
134
|
+
assert_nothing_raised do
|
135
|
+
Chronic.construct(2006, 1, 56)
|
136
|
+
end
|
137
|
+
|
138
|
+
assert_raise(RuntimeError) do
|
139
|
+
Chronic.construct(2006, 1, 57)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def test_month_overflow
|
144
|
+
assert_equal Time.local(2006, 1), Chronic.construct(2005, 13)
|
145
|
+
assert_equal Time.local(2005, 12), Chronic.construct(2000, 72)
|
146
|
+
end
|
147
|
+
|
148
|
+
end
|