chronic 0.9.1 → 0.10.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.
- checksums.yaml +4 -4
- data/.gitignore +4 -4
- data/HISTORY.md +12 -0
- data/README.md +8 -0
- data/Rakefile +28 -8
- data/chronic.gemspec +7 -5
- data/lib/chronic.rb +10 -10
- data/lib/chronic/date.rb +82 -0
- data/lib/chronic/handler.rb +34 -25
- data/lib/chronic/handlers.rb +22 -3
- data/lib/chronic/ordinal.rb +22 -20
- data/lib/chronic/parser.rb +31 -26
- data/lib/chronic/repeater.rb +18 -18
- data/lib/chronic/repeaters/repeater_day.rb +4 -3
- data/lib/chronic/repeaters/repeater_day_name.rb +5 -4
- data/lib/chronic/repeaters/repeater_day_portion.rb +4 -3
- data/lib/chronic/repeaters/repeater_fortnight.rb +4 -3
- data/lib/chronic/repeaters/repeater_hour.rb +4 -3
- data/lib/chronic/repeaters/repeater_minute.rb +4 -3
- data/lib/chronic/repeaters/repeater_month.rb +5 -4
- data/lib/chronic/repeaters/repeater_month_name.rb +4 -3
- data/lib/chronic/repeaters/repeater_season.rb +5 -3
- data/lib/chronic/repeaters/repeater_second.rb +4 -3
- data/lib/chronic/repeaters/repeater_time.rb +35 -25
- data/lib/chronic/repeaters/repeater_week.rb +4 -3
- data/lib/chronic/repeaters/repeater_weekday.rb +4 -3
- data/lib/chronic/repeaters/repeater_weekend.rb +4 -3
- data/lib/chronic/repeaters/repeater_year.rb +5 -4
- data/lib/chronic/scalar.rb +34 -69
- data/lib/chronic/separator.rb +107 -8
- data/lib/chronic/sign.rb +49 -0
- data/lib/chronic/tag.rb +5 -4
- data/lib/chronic/time.rb +40 -0
- data/test/helper.rb +2 -2
- data/test/test_chronic.rb +4 -4
- data/test/test_handler.rb +20 -0
- data/test/test_parsing.rb +72 -3
- data/test/test_repeater_time.rb +18 -0
- metadata +24 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ce57c7b6e3bd50e021abd796f6cdbb4cad313dbc
|
4
|
+
data.tar.gz: 2ea7a98ce486caf4bbcf394903ee150a920ad888
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0737803ebaad62093afbdb8cabece3ec65f0921c174e1a0dc4869fa7c48c539e82bd01834d8ecf3b763e7692016cfc6bbe7893d5650a2ca8cfcd9df98e554012
|
7
|
+
data.tar.gz: c508780a3ea66dbce11b4351ed546483e30adb6cfa27c5f86dc466132f07447d0e5f13165990d7053d6015277f05464dfc5018f365b40e82e775f3bc7e5e6ba0
|
data/.gitignore
CHANGED
data/HISTORY.md
CHANGED
@@ -1,3 +1,15 @@
|
|
1
|
+
# 0.10.0 / 2013-08-25
|
2
|
+
|
3
|
+
* Chronic will parse subseconds correctly
|
4
|
+
for all supported date/time formats (#195, #198 and #200)
|
5
|
+
* Support for date format: dd.mm.yyyy (#197)
|
6
|
+
* Option `:hours24` to parse as 24 hour clock (#201 and #202)
|
7
|
+
* `:guess` option allows to specify which part of Span to return.
|
8
|
+
(accepted values `false`,`true`,`:begin`, `:middle`, `:end`)
|
9
|
+
* Replace `rcov` with `SimpleCov` for coverage generation
|
10
|
+
* Add more tests
|
11
|
+
* Various changes in codebase (#202 and #206)
|
12
|
+
|
1
13
|
# 0.9.1 / 2013-02-25
|
2
14
|
|
3
15
|
* Ensure Chronic strips periods from day portions (#173)
|
data/README.md
CHANGED
@@ -37,8 +37,15 @@ Chronic.parse('may 27th', :guess => false)
|
|
37
37
|
|
38
38
|
Chronic.parse('6/4/2012', :endian_precedence => :little)
|
39
39
|
#=> Fri Apr 06 00:00:00 PDT 2012
|
40
|
+
|
41
|
+
Chronic.parse('INVALID DATE')
|
42
|
+
#=> nil
|
40
43
|
```
|
41
44
|
|
45
|
+
If the parser can find a date or time, either a Time or Chronic::Span
|
46
|
+
will be returned (depending on the value of `:guess`). If no
|
47
|
+
date or time can be found, `nil` will be returned.
|
48
|
+
|
42
49
|
See `Chronic.parse` for detailed usage instructions.
|
43
50
|
|
44
51
|
## Examples
|
@@ -128,6 +135,7 @@ Specific Times (many of the above with an added time)
|
|
128
135
|
* January 5 at 7pm
|
129
136
|
* 22nd of june at 8am
|
130
137
|
* 1979-05-27 05:00:00
|
138
|
+
* 03/01/2012 07:25:09.234567
|
131
139
|
* etc
|
132
140
|
|
133
141
|
|
data/Rakefile
CHANGED
@@ -5,17 +5,37 @@ def version
|
|
5
5
|
contents[/VERSION = "([^"]+)"/, 1]
|
6
6
|
end
|
7
7
|
|
8
|
-
|
8
|
+
def do_test
|
9
9
|
$:.unshift './test'
|
10
10
|
Dir.glob('test/test_*.rb').each { |t| require File.basename(t) }
|
11
11
|
end
|
12
12
|
|
13
|
-
|
13
|
+
def open_command
|
14
|
+
case RUBY_PLATFORM
|
15
|
+
when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
|
16
|
+
'start'
|
17
|
+
when /darwin|mac os/
|
18
|
+
'open'
|
19
|
+
else
|
20
|
+
'xdg-open'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
task :test do
|
25
|
+
do_test
|
26
|
+
end
|
27
|
+
|
28
|
+
desc "Generate SimpleCov test coverage and open in your browser"
|
14
29
|
task :coverage do
|
15
|
-
require '
|
16
|
-
|
17
|
-
|
18
|
-
|
30
|
+
require 'simplecov'
|
31
|
+
FileUtils.rm_rf("./coverage")
|
32
|
+
SimpleCov.command_name 'Unit Tests'
|
33
|
+
SimpleCov.at_exit do
|
34
|
+
SimpleCov.result.format!
|
35
|
+
sh "#{open_command} #{SimpleCov.coverage_path}/index.html"
|
36
|
+
end
|
37
|
+
SimpleCov.start
|
38
|
+
do_test
|
19
39
|
end
|
20
40
|
|
21
41
|
desc "Open an irb session preloaded with this library"
|
@@ -38,9 +58,9 @@ end
|
|
38
58
|
|
39
59
|
desc "Build a gem from the gemspec"
|
40
60
|
task :build do
|
41
|
-
|
61
|
+
FileUtils.mkdir_p "pkg"
|
42
62
|
sh "gem build chronic.gemspec"
|
43
|
-
|
63
|
+
FileUtils.mv("./chronic-#{version}.gem", "pkg")
|
44
64
|
end
|
45
65
|
|
46
66
|
task :default => :test
|
data/chronic.gemspec
CHANGED
@@ -8,13 +8,15 @@ Gem::Specification.new do |s|
|
|
8
8
|
s.summary = 'Natural language date/time parsing.'
|
9
9
|
s.description = 'Chronic is a natural language date/time parser written in pure Ruby.'
|
10
10
|
s.authors = ['Tom Preston-Werner', 'Lee Jarvis']
|
11
|
-
s.email = ['tom@mojombo.com', '
|
11
|
+
s.email = ['tom@mojombo.com', 'ljjarvis@gmail.com']
|
12
12
|
s.homepage = 'http://github.com/mojombo/chronic'
|
13
|
+
s.license = 'MIT'
|
13
14
|
s.rdoc_options = ['--charset=UTF-8']
|
14
15
|
s.extra_rdoc_files = %w[README.md HISTORY.md LICENSE]
|
15
|
-
s.files = `git ls-files`.split(
|
16
|
-
s.test_files = `git ls-files -- test`.split(
|
16
|
+
s.files = `git ls-files`.split($/)
|
17
|
+
s.test_files = `git ls-files -- test`.split($/)
|
17
18
|
|
18
19
|
s.add_development_dependency 'rake'
|
19
|
-
s.add_development_dependency '
|
20
|
-
|
20
|
+
s.add_development_dependency 'simplecov'
|
21
|
+
s.add_development_dependency 'minitest', '~> 5.0'
|
22
|
+
end
|
data/lib/chronic.rb
CHANGED
@@ -2,6 +2,8 @@ require 'time'
|
|
2
2
|
require 'date'
|
3
3
|
|
4
4
|
require 'chronic/parser'
|
5
|
+
require 'chronic/date'
|
6
|
+
require 'chronic/time'
|
5
7
|
|
6
8
|
require 'chronic/handler'
|
7
9
|
require 'chronic/handlers'
|
@@ -14,6 +16,7 @@ require 'chronic/pointer'
|
|
14
16
|
require 'chronic/scalar'
|
15
17
|
require 'chronic/ordinal'
|
16
18
|
require 'chronic/separator'
|
19
|
+
require 'chronic/sign'
|
17
20
|
require 'chronic/time_zone'
|
18
21
|
require 'chronic/numerizer'
|
19
22
|
require 'chronic/season'
|
@@ -50,7 +53,7 @@ require 'chronic/repeaters/repeater_time'
|
|
50
53
|
# Chronic.parse('monday', :context => :past)
|
51
54
|
# #=> Mon Aug 21 12:00:00 PDT 2006
|
52
55
|
module Chronic
|
53
|
-
VERSION = "0.
|
56
|
+
VERSION = "0.10.0"
|
54
57
|
|
55
58
|
class << self
|
56
59
|
|
@@ -72,7 +75,7 @@ module Chronic
|
|
72
75
|
end
|
73
76
|
|
74
77
|
self.debug = false
|
75
|
-
self.time_class = Time
|
78
|
+
self.time_class = ::Time
|
76
79
|
|
77
80
|
|
78
81
|
# Parses a string containing a natural language date or time.
|
@@ -98,7 +101,7 @@ module Chronic
|
|
98
101
|
# second - Integer second.
|
99
102
|
#
|
100
103
|
# Returns a new Time object constructed from these params.
|
101
|
-
def self.construct(year, month = 1, day = 1, hour = 0, minute = 0, second = 0)
|
104
|
+
def self.construct(year, month = 1, day = 1, hour = 0, minute = 0, second = 0, offset = nil)
|
102
105
|
if second >= 60
|
103
106
|
minute += second / 60
|
104
107
|
second = second % 60
|
@@ -117,11 +120,8 @@ module Chronic
|
|
117
120
|
# determine if there is a day overflow. this is complicated by our crappy calendar
|
118
121
|
# system (non-constant number of days per month)
|
119
122
|
day <= 56 || raise("day must be no more than 56 (makes month resolution easier)")
|
120
|
-
if day > 28
|
121
|
-
|
122
|
-
leap_year_month_days = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
123
|
-
common_year_month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
124
|
-
days_this_month = Date.leap?(year) ? leap_year_month_days[month - 1] : common_year_month_days[month - 1]
|
123
|
+
if day > 28 # no month ever has fewer than 28 days, so only do this if necessary
|
124
|
+
days_this_month = ::Date.leap?(year) ? Date::MONTH_DAYS_LEAP[month] : Date::MONTH_DAYS[month]
|
125
125
|
if day > days_this_month
|
126
126
|
month += day / days_this_month
|
127
127
|
day = day % days_this_month
|
@@ -137,8 +137,8 @@ module Chronic
|
|
137
137
|
month = month % 12
|
138
138
|
end
|
139
139
|
end
|
140
|
-
|
141
|
-
Chronic.time_class.
|
140
|
+
offset = Time::normalize_offset(offset) if Chronic.time_class.is_a?(DateTime)
|
141
|
+
Chronic.time_class.new(year, month, day, hour, minute, second, offset)
|
142
142
|
end
|
143
143
|
|
144
144
|
end
|
data/lib/chronic/date.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
module Chronic
|
2
|
+
class Date
|
3
|
+
YEAR_MONTHS = 12
|
4
|
+
MONTH_DAYS = [nil, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
5
|
+
MONTH_DAYS_LEAP = [nil, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
6
|
+
YEAR_SECONDS = 31_536_000 # 365 * 24 * 60 * 60
|
7
|
+
SEASON_SECONDS = 7_862_400 # 91 * 24 * 60 * 60
|
8
|
+
MONTH_SECONDS = 2_592_000 # 30 * 24 * 60 * 60
|
9
|
+
FORTNIGHT_SECONDS = 1_209_600 # 14 * 24 * 60 * 60
|
10
|
+
WEEK_SECONDS = 604_800 # 7 * 24 * 60 * 60
|
11
|
+
WEEKEND_SECONDS = 172_800 # 2 * 24 * 60 * 60
|
12
|
+
DAY_SECONDS = 86_400 # 24 * 60 * 60
|
13
|
+
MONTHS = {
|
14
|
+
:january => 1,
|
15
|
+
:february => 2,
|
16
|
+
:march => 3,
|
17
|
+
:april => 4,
|
18
|
+
:may => 5,
|
19
|
+
:june => 6,
|
20
|
+
:july => 7,
|
21
|
+
:august => 8,
|
22
|
+
:september => 9,
|
23
|
+
:october => 10,
|
24
|
+
:november => 11,
|
25
|
+
:december => 12
|
26
|
+
}
|
27
|
+
DAYS = {
|
28
|
+
:sunday => 0,
|
29
|
+
:monday => 1,
|
30
|
+
:tuesday => 2,
|
31
|
+
:wednesday => 3,
|
32
|
+
:thursday => 4,
|
33
|
+
:friday => 5,
|
34
|
+
:saturday => 6
|
35
|
+
}
|
36
|
+
|
37
|
+
# Checks if given number could be day
|
38
|
+
def self.could_be_day?(day)
|
39
|
+
day >= 1 && day <= 31
|
40
|
+
end
|
41
|
+
|
42
|
+
# Checks if given number could be month
|
43
|
+
def self.could_be_month?(month)
|
44
|
+
month >= 1 && month <= 12
|
45
|
+
end
|
46
|
+
|
47
|
+
# Checks if given number could be year
|
48
|
+
def self.could_be_year?(year)
|
49
|
+
year >= 1 && year <= 9999
|
50
|
+
end
|
51
|
+
|
52
|
+
# Build a year from a 2 digit suffix.
|
53
|
+
#
|
54
|
+
# year - The two digit Integer year to build from.
|
55
|
+
# bias - The Integer amount of future years to bias.
|
56
|
+
#
|
57
|
+
# Examples:
|
58
|
+
#
|
59
|
+
# make_year(96, 50) #=> 1996
|
60
|
+
# make_year(79, 20) #=> 2079
|
61
|
+
# make_year(00, 50) #=> 2000
|
62
|
+
#
|
63
|
+
# Returns The Integer 4 digit year.
|
64
|
+
def self.make_year(year, bias)
|
65
|
+
return year if year.to_s.size > 2
|
66
|
+
start_year = Chronic.time_class.now.year - bias
|
67
|
+
century = (start_year / 100) * 100
|
68
|
+
full_year = century + year
|
69
|
+
full_year += 100 if full_year < start_year
|
70
|
+
full_year
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.month_overflow?(year, month, day)
|
74
|
+
if ::Date.leap?(year)
|
75
|
+
day > Date::MONTH_DAYS_LEAP[month]
|
76
|
+
else
|
77
|
+
day > Date::MONTH_DAYS[month]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
data/lib/chronic/handler.rb
CHANGED
@@ -19,39 +19,48 @@ module Chronic
|
|
19
19
|
# Returns true if a match is found.
|
20
20
|
def match(tokens, definitions)
|
21
21
|
token_index = 0
|
22
|
+
@pattern.each do |elements|
|
23
|
+
was_optional = false
|
24
|
+
elements = [elements] unless elements.is_a?(Array)
|
25
|
+
|
26
|
+
elements.each_index do |i|
|
27
|
+
name = elements[i].to_s
|
28
|
+
optional = name[-1, 1] == '?'
|
29
|
+
name = name.chop if optional
|
30
|
+
|
31
|
+
case elements[i]
|
32
|
+
when Symbol
|
33
|
+
if tags_match?(name, tokens, token_index)
|
34
|
+
token_index += 1
|
35
|
+
break
|
36
|
+
else
|
37
|
+
if optional
|
38
|
+
was_optional = true
|
39
|
+
next
|
40
|
+
elsif i + 1 < elements.count
|
41
|
+
next
|
42
|
+
else
|
43
|
+
return false unless was_optional
|
44
|
+
end
|
45
|
+
end
|
22
46
|
|
23
|
-
|
24
|
-
|
25
|
-
optional = name[-1, 1] == '?'
|
26
|
-
name = name.chop if optional
|
47
|
+
when String
|
48
|
+
return true if optional && token_index == tokens.size
|
27
49
|
|
28
|
-
|
29
|
-
|
30
|
-
if tags_match?(name, tokens, token_index)
|
31
|
-
token_index += 1
|
32
|
-
next
|
33
|
-
else
|
34
|
-
if optional
|
35
|
-
next
|
50
|
+
if definitions.key?(name.to_sym)
|
51
|
+
sub_handlers = definitions[name.to_sym]
|
36
52
|
else
|
37
|
-
|
53
|
+
raise "Invalid subset #{name} specified"
|
38
54
|
end
|
39
|
-
end
|
40
|
-
when String
|
41
|
-
return true if optional && token_index == tokens.size
|
42
55
|
|
43
|
-
|
44
|
-
|
56
|
+
sub_handlers.each do |sub_handler|
|
57
|
+
return true if sub_handler.match(tokens[token_index..tokens.size], definitions)
|
58
|
+
end
|
45
59
|
else
|
46
|
-
raise "Invalid
|
47
|
-
end
|
48
|
-
|
49
|
-
sub_handlers.each do |sub_handler|
|
50
|
-
return true if sub_handler.match(tokens[token_index..tokens.size], definitions)
|
60
|
+
raise "Invalid match type: #{elements[i].class}"
|
51
61
|
end
|
52
|
-
else
|
53
|
-
raise "Invalid match type: #{element.class}"
|
54
62
|
end
|
63
|
+
|
55
64
|
end
|
56
65
|
|
57
66
|
return false if token_index != tokens.size
|
data/lib/chronic/handlers.rb
CHANGED
@@ -134,6 +134,8 @@ module Chronic
|
|
134
134
|
def handle_generic(tokens, options)
|
135
135
|
t = Chronic.time_class.parse(options[:text])
|
136
136
|
Span.new(t, t + 1)
|
137
|
+
rescue ArgumentError => e
|
138
|
+
raise e unless e.message =~ /out of range/
|
137
139
|
end
|
138
140
|
|
139
141
|
# Handle repeater-month-name/scalar-day/scalar-year
|
@@ -304,6 +306,23 @@ module Chronic
|
|
304
306
|
end
|
305
307
|
end
|
306
308
|
|
309
|
+
# Handle RepeaterDayName RepeaterMonthName OrdinalDay ScalarYear
|
310
|
+
def handle_rdn_rmn_od_sy(tokens, options)
|
311
|
+
month = tokens[1].get_tag(RepeaterMonthName)
|
312
|
+
day = tokens[2].get_tag(OrdinalDay).type
|
313
|
+
year = tokens[3].get_tag(ScalarYear).type
|
314
|
+
|
315
|
+
return if month_overflow?(year, month.index, day)
|
316
|
+
|
317
|
+
begin
|
318
|
+
start_time = Chronic.time_class.local(year, month.index, day)
|
319
|
+
end_time = time_with_rollover(year, month.index, day + 1)
|
320
|
+
Span.new(start_time, end_time)
|
321
|
+
rescue ArgumentError
|
322
|
+
nil
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
307
326
|
# Handle RepeaterDayName OrdinalDay
|
308
327
|
def handle_rdn_od(tokens, options)
|
309
328
|
day = tokens[1].get_tag(OrdinalDay).type
|
@@ -476,9 +495,9 @@ module Chronic
|
|
476
495
|
def day_or_time(day_start, time_tokens, options)
|
477
496
|
outer_span = Span.new(day_start, day_start + (24 * 60 * 60))
|
478
497
|
|
479
|
-
|
498
|
+
unless time_tokens.empty?
|
480
499
|
self.now = outer_span.begin
|
481
|
-
get_anchor(dealias_and_disambiguate_times(time_tokens, options), options)
|
500
|
+
get_anchor(dealias_and_disambiguate_times(time_tokens, options), options.merge(:context => :future))
|
482
501
|
else
|
483
502
|
outer_span
|
484
503
|
end
|
@@ -525,7 +544,7 @@ module Chronic
|
|
525
544
|
end
|
526
545
|
|
527
546
|
def month_overflow?(year, month, day)
|
528
|
-
if Date.leap?(year)
|
547
|
+
if ::Date.leap?(year)
|
529
548
|
day > RepeaterMonth::MONTH_DAYS_LEAP[month - 1]
|
530
549
|
else
|
531
550
|
day > RepeaterMonth::MONTH_DAYS[month - 1]
|
data/lib/chronic/ordinal.rb
CHANGED
@@ -9,26 +9,16 @@ module Chronic
|
|
9
9
|
#
|
10
10
|
# Returns an Array of tokens.
|
11
11
|
def self.scan(tokens, options)
|
12
|
-
tokens.
|
13
|
-
if
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
Ordinal.new($1.to_i) if token.word =~ /^(\d*)(st|nd|rd|th)$/
|
23
|
-
end
|
24
|
-
|
25
|
-
# token - The Token object we want to scan.
|
26
|
-
#
|
27
|
-
# Returns a new Ordinal object.
|
28
|
-
def self.scan_for_days(token)
|
29
|
-
if token.word =~ /^(\d*)(st|nd|rd|th)$/
|
30
|
-
unless $1.to_i > 31 || $1.to_i < 1
|
31
|
-
OrdinalDay.new(token.word.to_i)
|
12
|
+
tokens.each_index do |i|
|
13
|
+
if tokens[i].word =~ /^(\d+)(st|nd|rd|th|\.)$/
|
14
|
+
ordinal = $1.to_i
|
15
|
+
tokens[i].tag(Ordinal.new(ordinal))
|
16
|
+
tokens[i].tag(OrdinalDay.new(ordinal)) if Chronic::Date::could_be_day?(ordinal)
|
17
|
+
tokens[i].tag(OrdinalMonth.new(ordinal)) if Chronic::Date::could_be_month?(ordinal)
|
18
|
+
if Chronic::Date::could_be_year?(ordinal)
|
19
|
+
year = Chronic::Date::make_year(ordinal, options[:ambiguous_year_future_bias])
|
20
|
+
tokens[i].tag(OrdinalYear.new(year.to_i))
|
21
|
+
end
|
32
22
|
end
|
33
23
|
end
|
34
24
|
end
|
@@ -44,4 +34,16 @@ module Chronic
|
|
44
34
|
end
|
45
35
|
end
|
46
36
|
|
37
|
+
class OrdinalMonth < Ordinal #:nodoc:
|
38
|
+
def to_s
|
39
|
+
super << '-month-' << @type.to_s
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class OrdinalYear < Ordinal #:nodoc:
|
44
|
+
def to_s
|
45
|
+
super << '-year-' << @type.to_s
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
47
49
|
end
|