chronic 0.9.1 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|