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.
- data/HISTORY.md +103 -0
- data/LICENSE +21 -0
- data/Manifest.txt +16 -5
- data/README.md +171 -0
- data/Rakefile +136 -15
- data/benchmark/benchmark.rb +13 -0
- data/chronic.gemspec +82 -0
- data/lib/chronic/chronic.rb +65 -147
- data/lib/chronic/grabber.rb +13 -17
- data/lib/chronic/handlers.rb +216 -138
- data/lib/chronic/mini_date.rb +27 -0
- data/lib/chronic/numerizer.rb +120 -0
- data/lib/chronic/ordinal.rb +11 -16
- data/lib/chronic/pointer.rb +11 -13
- data/lib/chronic/repeater.rb +117 -106
- data/lib/chronic/repeaters/repeater_day.rb +50 -43
- data/lib/chronic/repeaters/repeater_day_name.rb +49 -42
- data/lib/chronic/repeaters/repeater_day_portion.rb +82 -80
- data/lib/chronic/repeaters/repeater_fortnight.rb +60 -53
- data/lib/chronic/repeaters/repeater_hour.rb +50 -43
- data/lib/chronic/repeaters/repeater_minute.rb +50 -43
- data/lib/chronic/repeaters/repeater_month.rb +63 -56
- data/lib/chronic/repeaters/repeater_month_name.rb +91 -82
- data/lib/chronic/repeaters/repeater_season.rb +125 -20
- data/lib/chronic/repeaters/repeater_season_name.rb +43 -22
- data/lib/chronic/repeaters/repeater_second.rb +40 -33
- data/lib/chronic/repeaters/repeater_time.rb +118 -106
- data/lib/chronic/repeaters/repeater_week.rb +64 -57
- data/lib/chronic/repeaters/repeater_weekday.rb +86 -0
- data/lib/chronic/repeaters/repeater_weekend.rb +58 -51
- data/lib/chronic/repeaters/repeater_year.rb +58 -50
- data/lib/chronic/scalar.rb +37 -27
- data/lib/chronic/separator.rb +34 -37
- data/lib/chronic/span.rb +31 -0
- data/lib/chronic/tag.rb +26 -0
- data/lib/chronic/time_zone.rb +9 -10
- data/lib/chronic/token.rb +35 -0
- data/lib/chronic.rb +34 -25
- data/test/helper.rb +6 -0
- data/test/test_Chronic.rb +22 -18
- data/test/test_DaylightSavings.rb +118 -0
- data/test/test_Handler.rb +37 -38
- data/test/test_Numerizer.rb +66 -42
- 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 +16 -17
- data/test/test_RepeaterMonthName.rb +17 -18
- 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 +21 -22
- data/test/test_RepeaterYear.rb +17 -18
- data/test/test_Span.rb +5 -6
- data/test/test_Time.rb +11 -12
- data/test/test_Token.rb +5 -6
- data/test/test_parsing.rb +395 -208
- metadata +68 -52
- data/History.txt +0 -53
- data/README.txt +0 -149
- data/lib/numerizer/numerizer.rb +0 -103
- data/test/suite.rb +0 -9
|
@@ -1,58 +1,66 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
module Chronic
|
|
2
|
+
class RepeaterYear < Repeater #:nodoc:
|
|
3
|
+
YEAR_SECONDS = 31536000 # 365 * 24 * 60 * 60
|
|
4
|
+
|
|
5
|
+
def initialize(type)
|
|
6
|
+
super
|
|
7
|
+
@current_year_start = nil
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def next(pointer)
|
|
11
|
+
super
|
|
12
|
+
|
|
13
|
+
if !@current_year_start
|
|
14
|
+
case pointer
|
|
15
|
+
when :future
|
|
16
|
+
@current_year_start = Time.construct(@now.year + 1)
|
|
17
|
+
when :past
|
|
18
|
+
@current_year_start = Time.construct(@now.year - 1)
|
|
19
|
+
end
|
|
20
|
+
else
|
|
21
|
+
diff = pointer == :future ? 1 : -1
|
|
22
|
+
@current_year_start = Time.construct(@current_year_start.year + diff)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
Span.new(@current_year_start, Time.construct(@current_year_start.year + 1))
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def this(pointer = :future)
|
|
29
|
+
super
|
|
30
|
+
|
|
7
31
|
case pointer
|
|
8
32
|
when :future
|
|
9
|
-
|
|
33
|
+
this_year_start = Time.construct(@now.year, @now.month, @now.day) + RepeaterDay::DAY_SECONDS
|
|
34
|
+
this_year_end = Time.construct(@now.year + 1, 1, 1)
|
|
10
35
|
when :past
|
|
11
|
-
|
|
36
|
+
this_year_start = Time.construct(@now.year, 1, 1)
|
|
37
|
+
this_year_end = Time.construct(@now.year, @now.month, @now.day)
|
|
38
|
+
when :none
|
|
39
|
+
this_year_start = Time.construct(@now.year, 1, 1)
|
|
40
|
+
this_year_end = Time.construct(@now.year + 1, 1, 1)
|
|
12
41
|
end
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
@current_year_start = Time.construct(@current_year_start.year + diff)
|
|
42
|
+
|
|
43
|
+
Span.new(this_year_start, this_year_end)
|
|
16
44
|
end
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
45
|
+
|
|
46
|
+
def offset(span, amount, pointer)
|
|
47
|
+
direction = pointer == :future ? 1 : -1
|
|
48
|
+
|
|
49
|
+
sb = span.begin
|
|
50
|
+
new_begin = Time.construct(sb.year + (amount * direction), sb.month, sb.day, sb.hour, sb.min, sb.sec)
|
|
51
|
+
|
|
52
|
+
se = span.end
|
|
53
|
+
new_end = Time.construct(se.year + (amount * direction), se.month, se.day, se.hour, se.min, se.sec)
|
|
54
|
+
|
|
55
|
+
Span.new(new_begin, new_end)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def width
|
|
59
|
+
(365 * 24 * 60 * 60)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def to_s
|
|
63
|
+
super << '-year'
|
|
34
64
|
end
|
|
35
|
-
|
|
36
|
-
Chronic::Span.new(this_year_start, this_year_end)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def offset(span, amount, pointer)
|
|
40
|
-
direction = pointer == :future ? 1 : -1
|
|
41
|
-
|
|
42
|
-
sb = span.begin
|
|
43
|
-
new_begin = Time.construct(sb.year + (amount * direction), sb.month, sb.day, sb.hour, sb.min, sb.sec)
|
|
44
|
-
|
|
45
|
-
se = span.end
|
|
46
|
-
new_end = Time.construct(se.year + (amount * direction), se.month, se.day, se.hour, se.min, se.sec)
|
|
47
|
-
|
|
48
|
-
Chronic::Span.new(new_begin, new_end)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def width
|
|
52
|
-
(365 * 24 * 60 * 60)
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def to_s
|
|
56
|
-
super << '-year'
|
|
57
65
|
end
|
|
58
66
|
end
|
data/lib/chronic/scalar.rb
CHANGED
|
@@ -1,74 +1,84 @@
|
|
|
1
1
|
module Chronic
|
|
2
2
|
|
|
3
3
|
class Scalar < Tag #:nodoc:
|
|
4
|
-
|
|
4
|
+
DAY_PORTIONS = %w( am pm morning afternoon evening night )
|
|
5
|
+
|
|
6
|
+
def self.scan(tokens, options)
|
|
5
7
|
# for each token
|
|
6
8
|
tokens.each_index do |i|
|
|
7
|
-
if t =
|
|
8
|
-
if t =
|
|
9
|
-
if t =
|
|
10
|
-
if t =
|
|
9
|
+
if t = scan_for_scalars(tokens[i], tokens[i + 1]) then tokens[i].tag(t) end
|
|
10
|
+
if t = scan_for_days(tokens[i], tokens[i + 1]) then tokens[i].tag(t) end
|
|
11
|
+
if t = scan_for_months(tokens[i], tokens[i + 1]) then tokens[i].tag(t) end
|
|
12
|
+
if t = scan_for_years(tokens[i], tokens[i + 1], options) then tokens[i].tag(t) end
|
|
11
13
|
end
|
|
12
|
-
tokens
|
|
13
14
|
end
|
|
14
|
-
|
|
15
|
+
|
|
15
16
|
def self.scan_for_scalars(token, post_token)
|
|
16
17
|
if token.word =~ /^\d*$/
|
|
17
|
-
unless post_token &&
|
|
18
|
+
unless post_token && DAY_PORTIONS.include?(post_token.word)
|
|
18
19
|
return Scalar.new(token.word.to_i)
|
|
19
20
|
end
|
|
20
21
|
end
|
|
21
|
-
return nil
|
|
22
22
|
end
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
def self.scan_for_days(token, post_token)
|
|
25
25
|
if token.word =~ /^\d\d?$/
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
toi = token.word.to_i
|
|
27
|
+
unless toi > 31 || toi < 1 || (post_token && DAY_PORTIONS.include?(post_token.word))
|
|
28
|
+
return ScalarDay.new(toi)
|
|
28
29
|
end
|
|
29
30
|
end
|
|
30
|
-
return nil
|
|
31
31
|
end
|
|
32
|
-
|
|
32
|
+
|
|
33
33
|
def self.scan_for_months(token, post_token)
|
|
34
34
|
if token.word =~ /^\d\d?$/
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
toi = token.word.to_i
|
|
36
|
+
unless toi > 12 || toi < 1 || (post_token && DAY_PORTIONS.include?(post_token.word))
|
|
37
|
+
return ScalarMonth.new(toi)
|
|
37
38
|
end
|
|
38
39
|
end
|
|
39
|
-
return nil
|
|
40
40
|
end
|
|
41
|
-
|
|
42
|
-
def self.scan_for_years(token, post_token)
|
|
41
|
+
|
|
42
|
+
def self.scan_for_years(token, post_token, options)
|
|
43
43
|
if token.word =~ /^([1-9]\d)?\d\d?$/
|
|
44
|
-
unless post_token &&
|
|
45
|
-
|
|
44
|
+
unless post_token && DAY_PORTIONS.include?(post_token.word)
|
|
45
|
+
year = make_year(token.word.to_i, options[:ambiguous_year_future_bias])
|
|
46
|
+
return ScalarYear.new(year.to_i)
|
|
46
47
|
end
|
|
47
48
|
end
|
|
48
|
-
return nil
|
|
49
49
|
end
|
|
50
|
-
|
|
50
|
+
|
|
51
|
+
# Build a year from a 2 digit suffix
|
|
52
|
+
def self.make_year(year, bias)
|
|
53
|
+
return year if year.to_s.size > 2
|
|
54
|
+
start_year = Chronic.time_class.now.year - bias
|
|
55
|
+
century = (start_year / 100) * 100
|
|
56
|
+
full_year = century + year
|
|
57
|
+
full_year += 100 if full_year < start_year
|
|
58
|
+
full_year
|
|
59
|
+
end
|
|
60
|
+
|
|
51
61
|
def to_s
|
|
52
62
|
'scalar'
|
|
53
63
|
end
|
|
54
64
|
end
|
|
55
|
-
|
|
65
|
+
|
|
56
66
|
class ScalarDay < Scalar #:nodoc:
|
|
57
67
|
def to_s
|
|
58
68
|
super << '-day-' << @type.to_s
|
|
59
69
|
end
|
|
60
70
|
end
|
|
61
|
-
|
|
71
|
+
|
|
62
72
|
class ScalarMonth < Scalar #:nodoc:
|
|
63
73
|
def to_s
|
|
64
74
|
super << '-month-' << @type.to_s
|
|
65
75
|
end
|
|
66
76
|
end
|
|
67
|
-
|
|
77
|
+
|
|
68
78
|
class ScalarYear < Scalar #:nodoc:
|
|
69
79
|
def to_s
|
|
70
80
|
super << '-year-' << @type.to_s
|
|
71
81
|
end
|
|
72
82
|
end
|
|
73
83
|
|
|
74
|
-
end
|
|
84
|
+
end
|
data/lib/chronic/separator.rb
CHANGED
|
@@ -1,76 +1,73 @@
|
|
|
1
1
|
module Chronic
|
|
2
2
|
|
|
3
3
|
class Separator < Tag #:nodoc:
|
|
4
|
-
def self.scan(tokens)
|
|
4
|
+
def self.scan(tokens, options)
|
|
5
5
|
tokens.each_index do |i|
|
|
6
|
-
if t =
|
|
7
|
-
if t =
|
|
8
|
-
if t =
|
|
9
|
-
if t =
|
|
6
|
+
if t = scan_for_commas(tokens[i]) then tokens[i].tag(t); next end
|
|
7
|
+
if t = scan_for_slash_or_dash(tokens[i]) then tokens[i].tag(t); next end
|
|
8
|
+
if t = scan_for_at(tokens[i]) then tokens[i].tag(t); next end
|
|
9
|
+
if t = scan_for_in(tokens[i]) then tokens[i].tag(t); next end
|
|
10
|
+
if t = scan_for_on(tokens[i]) then tokens[i].tag(t); next end
|
|
10
11
|
end
|
|
11
|
-
tokens
|
|
12
12
|
end
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
def self.scan_for_commas(token)
|
|
15
|
-
|
|
16
|
-
scanner.keys.each do |scanner_item|
|
|
17
|
-
return SeparatorComma.new(scanner[scanner_item]) if scanner_item =~ token.word
|
|
18
|
-
end
|
|
19
|
-
return nil
|
|
15
|
+
scan_for token, SeparatorComma, { /^,$/ => :comma }
|
|
20
16
|
end
|
|
21
|
-
|
|
17
|
+
|
|
22
18
|
def self.scan_for_slash_or_dash(token)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return nil
|
|
19
|
+
scan_for token, SeparatorSlashOrDash,
|
|
20
|
+
{
|
|
21
|
+
/^-$/ => :dash,
|
|
22
|
+
/^\/$/ => :slash
|
|
23
|
+
}
|
|
29
24
|
end
|
|
30
|
-
|
|
25
|
+
|
|
31
26
|
def self.scan_for_at(token)
|
|
32
|
-
|
|
33
|
-
scanner.keys.each do |scanner_item|
|
|
34
|
-
return SeparatorAt.new(scanner[scanner_item]) if scanner_item =~ token.word
|
|
35
|
-
end
|
|
36
|
-
return nil
|
|
27
|
+
scan_for token, SeparatorAt, { /^(at|@)$/ => :at }
|
|
37
28
|
end
|
|
38
|
-
|
|
29
|
+
|
|
39
30
|
def self.scan_for_in(token)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
31
|
+
scan_for token, SeparatorIn, { /^in$/ => :in }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.scan_for_on(token)
|
|
35
|
+
scan_for token, SeparatorOn, { /^on$/ => :on }
|
|
45
36
|
end
|
|
46
|
-
|
|
37
|
+
|
|
47
38
|
def to_s
|
|
48
39
|
'separator'
|
|
49
40
|
end
|
|
50
41
|
end
|
|
51
|
-
|
|
42
|
+
|
|
52
43
|
class SeparatorComma < Separator #:nodoc:
|
|
53
44
|
def to_s
|
|
54
45
|
super << '-comma'
|
|
55
46
|
end
|
|
56
47
|
end
|
|
57
|
-
|
|
48
|
+
|
|
58
49
|
class SeparatorSlashOrDash < Separator #:nodoc:
|
|
59
50
|
def to_s
|
|
60
51
|
super << '-slashordash-' << @type.to_s
|
|
61
52
|
end
|
|
62
53
|
end
|
|
63
|
-
|
|
54
|
+
|
|
64
55
|
class SeparatorAt < Separator #:nodoc:
|
|
65
56
|
def to_s
|
|
66
57
|
super << '-at'
|
|
67
58
|
end
|
|
68
59
|
end
|
|
69
|
-
|
|
60
|
+
|
|
70
61
|
class SeparatorIn < Separator #:nodoc:
|
|
71
62
|
def to_s
|
|
72
63
|
super << '-in'
|
|
73
64
|
end
|
|
74
65
|
end
|
|
75
66
|
|
|
76
|
-
|
|
67
|
+
class SeparatorOn < Separator #:nodoc:
|
|
68
|
+
def to_s
|
|
69
|
+
super << '-on'
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
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,26 @@
|
|
|
1
|
+
module Chronic
|
|
2
|
+
# Tokens are tagged with subclassed instances of this class when
|
|
3
|
+
# they match specific criteria
|
|
4
|
+
class Tag #:nodoc:
|
|
5
|
+
attr_accessor :type
|
|
6
|
+
|
|
7
|
+
def initialize(type)
|
|
8
|
+
@type = type
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def start=(s)
|
|
12
|
+
@now = s
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
class << self
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def scan_for(token, klass, items={})
|
|
19
|
+
items.each do |item, symbol|
|
|
20
|
+
return klass.new(symbol) if item =~ token.word
|
|
21
|
+
end
|
|
22
|
+
nil
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
data/lib/chronic/time_zone.rb
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
module Chronic
|
|
2
|
-
class TimeZone < Tag #:nodoc:
|
|
3
|
-
def self.scan(tokens)
|
|
2
|
+
class TimeZone < Tag #:nodoc:
|
|
3
|
+
def self.scan(tokens, options)
|
|
4
4
|
tokens.each_index do |i|
|
|
5
|
-
if t =
|
|
5
|
+
if t = scan_for_all(tokens[i]) then tokens[i].tag(t); next end
|
|
6
6
|
end
|
|
7
|
-
tokens
|
|
8
7
|
end
|
|
9
8
|
|
|
10
9
|
def self.scan_for_all(token)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
scan_for token, self,
|
|
11
|
+
{
|
|
12
|
+
/[PMCE][DS]T/i => :tz,
|
|
13
|
+
/(tzminus)?\d{4}/ => :tz
|
|
14
|
+
}
|
|
16
15
|
end
|
|
17
16
|
|
|
18
17
|
def to_s
|
|
19
18
|
'timezone'
|
|
20
19
|
end
|
|
21
20
|
end
|
|
22
|
-
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Chronic
|
|
2
|
+
class Token #:nodoc:
|
|
3
|
+
attr_accessor :word, :tags
|
|
4
|
+
|
|
5
|
+
def initialize(word)
|
|
6
|
+
@word = word
|
|
7
|
+
@tags = []
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Tag this token with the specified tag
|
|
11
|
+
def tag(new_tag)
|
|
12
|
+
@tags << new_tag
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Remove all tags of the given class
|
|
16
|
+
def untag(tag_class)
|
|
17
|
+
@tags.delete_if { |m| m.kind_of? tag_class }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Return true if this token has any tags
|
|
21
|
+
def tagged?
|
|
22
|
+
@tags.size > 0
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Return the Tag that matches the given class
|
|
26
|
+
def get_tag(tag_class)
|
|
27
|
+
@tags.find { |m| m.kind_of? tag_class }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Print this Token in a pretty way
|
|
31
|
+
def to_s
|
|
32
|
+
@word << '(' << @tags.join(', ') << ') '
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
data/lib/chronic.rb
CHANGED
|
@@ -7,10 +7,27 @@
|
|
|
7
7
|
#
|
|
8
8
|
#=============================================================================
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
module Chronic
|
|
11
|
+
VERSION = "0.4.0"
|
|
12
|
+
|
|
13
|
+
class << self
|
|
14
|
+
attr_accessor :debug
|
|
15
|
+
attr_accessor :time_class
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
self.debug = false
|
|
19
|
+
self.time_class = Time
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
require 'time'
|
|
23
|
+
require 'date'
|
|
11
24
|
|
|
12
25
|
require 'chronic/chronic'
|
|
13
26
|
require 'chronic/handlers'
|
|
27
|
+
require 'chronic/mini_date'
|
|
28
|
+
require 'chronic/tag'
|
|
29
|
+
require 'chronic/span'
|
|
30
|
+
require 'chronic/token'
|
|
14
31
|
|
|
15
32
|
require 'chronic/repeater'
|
|
16
33
|
require 'chronic/repeaters/repeater_year'
|
|
@@ -21,6 +38,7 @@ require 'chronic/repeaters/repeater_month_name'
|
|
|
21
38
|
require 'chronic/repeaters/repeater_fortnight'
|
|
22
39
|
require 'chronic/repeaters/repeater_week'
|
|
23
40
|
require 'chronic/repeaters/repeater_weekend'
|
|
41
|
+
require 'chronic/repeaters/repeater_weekday'
|
|
24
42
|
require 'chronic/repeaters/repeater_day'
|
|
25
43
|
require 'chronic/repeaters/repeater_day_name'
|
|
26
44
|
require 'chronic/repeaters/repeater_day_portion'
|
|
@@ -36,28 +54,15 @@ require 'chronic/ordinal'
|
|
|
36
54
|
require 'chronic/separator'
|
|
37
55
|
require 'chronic/time_zone'
|
|
38
56
|
|
|
39
|
-
require '
|
|
40
|
-
|
|
41
|
-
module Chronic
|
|
42
|
-
VERSION = "0.2.3"
|
|
43
|
-
|
|
44
|
-
def self.debug; false; end
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
alias p_orig p
|
|
48
|
-
|
|
49
|
-
def p(val)
|
|
50
|
-
p_orig val
|
|
51
|
-
puts
|
|
52
|
-
end
|
|
57
|
+
require 'chronic/numerizer'
|
|
53
58
|
|
|
54
59
|
# class Time
|
|
55
60
|
# def self.construct(year, month = 1, day = 1, hour = 0, minute = 0, second = 0)
|
|
56
61
|
# # extra_seconds = second > 60 ? second - 60 : 0
|
|
57
62
|
# # extra_minutes = minute > 59 ? minute - 59 : 0
|
|
58
63
|
# # extra_hours = hour > 23 ? hour - 23 : 0
|
|
59
|
-
# # extra_days = day >
|
|
60
|
-
#
|
|
64
|
+
# # extra_days = day >
|
|
65
|
+
#
|
|
61
66
|
# if month > 12
|
|
62
67
|
# if month % 12 == 0
|
|
63
68
|
# year += (month - 12) / 12
|
|
@@ -67,7 +72,7 @@ end
|
|
|
67
72
|
# month = month % 12
|
|
68
73
|
# end
|
|
69
74
|
# end
|
|
70
|
-
#
|
|
75
|
+
#
|
|
71
76
|
# base = Time.local(year, month)
|
|
72
77
|
# puts base
|
|
73
78
|
# offset = ((day - 1) * 24 * 60 * 60) + (hour * 60 * 60) + (minute * 60) + second
|
|
@@ -84,17 +89,17 @@ class Time
|
|
|
84
89
|
minute += second / 60
|
|
85
90
|
second = second % 60
|
|
86
91
|
end
|
|
87
|
-
|
|
92
|
+
|
|
88
93
|
if minute >= 60
|
|
89
94
|
hour += minute / 60
|
|
90
95
|
minute = minute % 60
|
|
91
96
|
end
|
|
92
|
-
|
|
97
|
+
|
|
93
98
|
if hour >= 24
|
|
94
99
|
day += hour / 24
|
|
95
100
|
hour = hour % 24
|
|
96
101
|
end
|
|
97
|
-
|
|
102
|
+
|
|
98
103
|
# determine if there is a day overflow. this is complicated by our crappy calendar
|
|
99
104
|
# system (non-constant number of days per month)
|
|
100
105
|
day <= 56 || raise("day must be no more than 56 (makes month resolution easier)")
|
|
@@ -109,7 +114,7 @@ class Time
|
|
|
109
114
|
day = day % days_this_month
|
|
110
115
|
end
|
|
111
116
|
end
|
|
112
|
-
|
|
117
|
+
|
|
113
118
|
if month > 12
|
|
114
119
|
if month % 12 == 0
|
|
115
120
|
year += (month - 12) / 12
|
|
@@ -119,7 +124,11 @@ class Time
|
|
|
119
124
|
month = month % 12
|
|
120
125
|
end
|
|
121
126
|
end
|
|
122
|
-
|
|
123
|
-
|
|
127
|
+
|
|
128
|
+
Chronic.time_class.local(year, month, day, hour, minute, second)
|
|
124
129
|
end
|
|
125
|
-
|
|
130
|
+
|
|
131
|
+
def to_minidate
|
|
132
|
+
Chronic::MiniDate.new(self.month, self.day)
|
|
133
|
+
end
|
|
134
|
+
end
|
data/test/helper.rb
ADDED
data/test/test_Chronic.rb
CHANGED
|
@@ -1,50 +1,54 @@
|
|
|
1
|
-
require
|
|
2
|
-
require 'test/unit'
|
|
1
|
+
require File.join(File.dirname(__FILE__), *%w[helper])
|
|
3
2
|
|
|
4
3
|
class TestChronic < Test::Unit::TestCase
|
|
5
|
-
|
|
4
|
+
|
|
6
5
|
def setup
|
|
7
6
|
# Wed Aug 16 14:00:00 UTC 2006
|
|
8
7
|
@now = Time.local(2006, 8, 16, 14, 0, 0, 0)
|
|
9
8
|
end
|
|
10
|
-
|
|
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
|
+
|
|
11
15
|
def test_post_normalize_am_pm_aliases
|
|
12
16
|
# affect wanted patterns
|
|
13
|
-
|
|
17
|
+
|
|
14
18
|
tokens = [Chronic::Token.new("5:00"), Chronic::Token.new("morning")]
|
|
15
19
|
tokens[0].tag(Chronic::RepeaterTime.new("5:00"))
|
|
16
20
|
tokens[1].tag(Chronic::RepeaterDayPortion.new(:morning))
|
|
17
|
-
|
|
21
|
+
|
|
18
22
|
assert_equal :morning, tokens[1].tags[0].type
|
|
19
|
-
|
|
23
|
+
|
|
20
24
|
tokens = Chronic.dealias_and_disambiguate_times(tokens, {})
|
|
21
|
-
|
|
25
|
+
|
|
22
26
|
assert_equal :am, tokens[1].tags[0].type
|
|
23
27
|
assert_equal 2, tokens.size
|
|
24
|
-
|
|
28
|
+
|
|
25
29
|
# don't affect unwanted patterns
|
|
26
|
-
|
|
30
|
+
|
|
27
31
|
tokens = [Chronic::Token.new("friday"), Chronic::Token.new("morning")]
|
|
28
32
|
tokens[0].tag(Chronic::RepeaterDayName.new(:friday))
|
|
29
33
|
tokens[1].tag(Chronic::RepeaterDayPortion.new(:morning))
|
|
30
|
-
|
|
34
|
+
|
|
31
35
|
assert_equal :morning, tokens[1].tags[0].type
|
|
32
|
-
|
|
36
|
+
|
|
33
37
|
tokens = Chronic.dealias_and_disambiguate_times(tokens, {})
|
|
34
|
-
|
|
38
|
+
|
|
35
39
|
assert_equal :morning, tokens[1].tags[0].type
|
|
36
40
|
assert_equal 2, tokens.size
|
|
37
41
|
end
|
|
38
|
-
|
|
42
|
+
|
|
39
43
|
def test_guess
|
|
40
44
|
span = Chronic::Span.new(Time.local(2006, 8, 16, 0), Time.local(2006, 8, 17, 0))
|
|
41
45
|
assert_equal Time.local(2006, 8, 16, 12), Chronic.guess(span)
|
|
42
|
-
|
|
46
|
+
|
|
43
47
|
span = Chronic::Span.new(Time.local(2006, 8, 16, 0), Time.local(2006, 8, 17, 0, 0, 1))
|
|
44
48
|
assert_equal Time.local(2006, 8, 16, 12), Chronic.guess(span)
|
|
45
|
-
|
|
49
|
+
|
|
46
50
|
span = Chronic::Span.new(Time.local(2006, 11), Time.local(2006, 12))
|
|
47
51
|
assert_equal Time.local(2006, 11, 16), Chronic.guess(span)
|
|
48
52
|
end
|
|
49
|
-
|
|
50
|
-
end
|
|
53
|
+
|
|
54
|
+
end
|