edtf 0.0.3 → 0.0.4

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/Gemfile CHANGED
@@ -6,3 +6,6 @@ group :debug do
6
6
  gem 'ruby-debug', :platforms => [:mri_18, :jruby]
7
7
  gem 'rbx-trepanning', :platforms => [:rbx]
8
8
  end
9
+
10
+ # active_support requires this
11
+ gem 'i18n'
data/edtf.gemspec CHANGED
@@ -15,6 +15,8 @@ Gem::Specification.new do |s|
15
15
  s.description = 'An Extended Date/Time Format (EDTF) Parser for Ruby.'
16
16
  s.license = 'FreeBSD'
17
17
 
18
+ s.add_runtime_dependency('activesupport', ['~>3.0'])
19
+
18
20
  s.add_development_dependency('rake', ['~>0.9'])
19
21
  s.add_development_dependency('racc', ['~>1.4'])
20
22
  s.add_development_dependency('cucumber', ['~>1.0'])
@@ -3,7 +3,7 @@ Feature: EDTF parser parses date strings
3
3
  In order to use dates in EDTF
4
4
  As a user of edtf-ruby
5
5
  I want to parse date strings formatted in EDTF
6
-
6
+
7
7
  Scenario Outline: EDTF parses a date string
8
8
  When I parse the string "<string>"
9
9
  Then the year should be "<year>"
@@ -4,16 +4,15 @@ Feature: EDTF parses ISO 8601 interval strings
4
4
 
5
5
  Scenario Outline: parse intervals
6
6
  When I parse the string "<string>"
7
- Then the interval should start at "<from>"
8
- And the interval should end at "<to>"
7
+ Then the interval should include the date "<date>"
9
8
 
10
9
  @004 @level0
11
10
  Scenarios: specification intervals
12
- | string | from | to |
13
- | 1964/2008 | 1964-01-01 | 2008-01-01 |
14
- | 2004-06/2006-08 | 2004-06-01 | 2006-08-01 |
15
- | 2004-01-01/2004-01-02 | 2004-01-01 | 2004-01-02 |
16
- | 2004-02-01/2005-02-08 | 2004-02-01 | 2005-02-08 |
17
- | 2004-02-01/2005-02 | 2004-02-01 | 2005-02-01 |
18
- | 2004-02-01/2005 | 2004-02-01 | 2005-01-01 |
19
- | 2005/2006-02 | 2005-01-01 | 2006-02-01 |
11
+ | string | date |
12
+ | 1964/2008 | 1964-01-01 |
13
+ | 2004-06/2006-08 | 2006-08-31 |
14
+ | 2004-01-01/2004-01-02 | 2004-01-01 |
15
+ | 2004-02-01/2005-02-08 | 2005-02-08 |
16
+ | 2004-02-01/2005-02 | 2004-12-01 |
17
+ | 2004-02-01/2005 | 2005-12-31 |
18
+ | 2005/2006-02 | 2005-01-01 |
@@ -0,0 +1,68 @@
1
+ Feature: Print Date/Time objects as Level 1 EDTF strings
2
+ As a Ruby programmer
3
+ I want to convert Date/Time objects to EDTF strings
4
+
5
+ Scenario: Convert simple dates
6
+ Given the date "2004-08-12"
7
+ When I convert the date
8
+ Then the EDTF string should be "2004-08-12"
9
+
10
+ Scenario: Convert simple dates with precision
11
+ Given the date "1980-08-24" with precision set to "day"
12
+ When I convert the date
13
+ Then the EDTF string should be "1980-08-24"
14
+ Given the date "1980-08-24" with precision set to "month"
15
+ When I convert the date
16
+ Then the EDTF string should be "1980-08"
17
+ Given the date "1980-08-24" with precision set to "year"
18
+ When I convert the date
19
+ Then the EDTF string should be "1980"
20
+
21
+ Scenario: Date Roundtrips
22
+ When I parse the string "2001-02-03"
23
+ When I convert the date
24
+ Then the EDTF string should be "2001-02-03"
25
+ When I parse the string "2001-02"
26
+ When I convert the date
27
+ Then the EDTF string should be "2001-02"
28
+ When I parse the string "2001"
29
+ When I convert the date
30
+ Then the EDTF string should be "2001"
31
+ When I parse the string "-9909"
32
+ When I convert the date
33
+ Then the EDTF string should be "-9909"
34
+ When I parse the string "0000"
35
+ When I convert the date
36
+ Then the EDTF string should be "0000"
37
+
38
+ Scenario: DateTime Roundtrips
39
+ When I parse the string "2001-02-03T09:30:01"
40
+ When I convert the date
41
+ Then the EDTF string should be "2001-02-03T09:30:01+00:00"
42
+ When I parse the string "2004-01-01T10:10:10Z"
43
+ When I convert the date
44
+ Then the EDTF string should be "2004-01-01T10:10:10+00:00"
45
+ When I parse the string "2004-01-01T10:10:10+05:00"
46
+ When I convert the date
47
+ Then the EDTF string should be "2004-01-01T10:10:10+05:00"
48
+
49
+ Scenario: Interval Roundtrips
50
+ When I parse the string "1964/2008"
51
+ When I convert the date
52
+ Then the EDTF string should be "1964/2008"
53
+ When I parse the string "2004-06/2006-08"
54
+ When I convert the date
55
+ Then the EDTF string should be "2004-06/2006-08"
56
+ When I parse the string "2004-02-01/2005-02-08"
57
+ When I convert the date
58
+ Then the EDTF string should be "2004-02-01/2005-02-08"
59
+ When I parse the string "2004-02-01/2005-02"
60
+ When I convert the date
61
+ Then the EDTF string should be "2004-02-01/2005-02"
62
+ When I parse the string "2004-02-01/2005"
63
+ When I convert the date
64
+ Then the EDTF string should be "2004-02-01/2005"
65
+ When I parse the string "2005/2006-02"
66
+ When I convert the date
67
+ Then the EDTF string should be "2005/2006-02"
68
+
@@ -0,0 +1,8 @@
1
+ Feature: Print Date/Time objects as Level 2 EDTF strings
2
+ As a Ruby programmer
3
+ I want to convert Date/Time objects to EDTF strings
4
+
5
+ Scenario: Uncertain or approximate dates
6
+ When I parse the string "2001?"
7
+ When I convert the date
8
+ Then the EDTF string should be "2001?"
@@ -1,80 +1,98 @@
1
+ Given /^the date "([^"]*)"(?: with precision set to "([^"]*)")?$/ do |date, precision|
2
+ @date = Date.parse(date)
3
+ @date.precision = precision unless precision.nil?
4
+ end
5
+
6
+ When /^I convert the date$/ do
7
+ @string = @date.edtf
8
+ end
9
+
10
+ Then /^the EDTF String should be "([^"]*)"$/i do |edtf|
11
+ @string.should == edtf
12
+ end
13
+
1
14
  When /^I parse the string "([^"]*)"$/ do |string|
2
- @edtf = EDTF.parse(string)
15
+ @date = EDTF.parse(string)
3
16
  end
4
17
 
5
18
  Then /^the year should be "([^"]*)"$/ do |year|
6
- @edtf.year.should == year.to_i
19
+ @date.year.should == year.to_i
7
20
  end
8
21
 
9
22
  Then /^the month should be "([^"]*)"$/ do |month|
10
- @edtf.month.should == month.to_i
23
+ @date.month.should == month.to_i
11
24
  end
12
25
 
13
26
  Then /^the day should be "([^"]*)"$/ do |day|
14
- @edtf.day.should == day.to_i
27
+ @date.day.should == day.to_i
15
28
  end
16
29
 
17
30
  Then /^the hours should be "([^"]*)"$/ do |hours|
18
- @edtf.hour.should == hours.to_i
31
+ @date.hour.should == hours.to_i
19
32
  end
20
33
 
21
34
  Then /^the year should be "([^"]*)" \(UTC\)$/ do |year|
22
- @edtf.to_time.utc.year.should == year.to_i
35
+ @date.to_time.utc.year.should == year.to_i
23
36
  end
24
37
 
25
38
  Then /^the month should be "([^"]*)" \(UTC\)$/ do |month|
26
- @edtf.to_time.utc.month.should == month.to_i
39
+ @date.to_time.utc.month.should == month.to_i
27
40
  end
28
41
 
29
42
  Then /^the day should be "([^"]*)" \(UTC\)$/ do |day|
30
- @edtf.to_time.utc.day.should == day.to_i
43
+ @date.to_time.utc.day.should == day.to_i
31
44
  end
32
45
 
33
46
  Then /^the hours should be "([^"]*)" \(UTC\)$/ do |hours|
34
- @edtf.to_time.utc.hour.should == hours.to_i
47
+ @date.to_time.utc.hour.should == hours.to_i
35
48
  end
36
49
 
37
50
 
38
51
  Then /^the minutes should be "([^"]*)"$/ do |minutes|
39
- @edtf.min.should == minutes.to_i
52
+ @date.min.should == minutes.to_i
40
53
  end
41
54
 
42
55
  Then /^the seconds should be "([^"]*)"$/ do |seconds|
43
- @edtf.sec.should == seconds.to_i
56
+ @date.sec.should == seconds.to_i
44
57
  end
45
58
 
46
59
  Then /^the duration should range from "([^"]*)" to "([^"]*)"$/ do |from,to|
47
- [@edtf.begin.year.to_s, @edtf.end.year.to_s].should == [from,to]
60
+ [@date.begin.year.to_s, @date.end.year.to_s].should == [from,to]
48
61
  end
49
62
 
50
63
  Then /^the interval should start at "([^"]*)"$/ do |date|
51
- @edtf.begin.to_s.should == date
64
+ @date.begin.to_s.should == date
52
65
  end
53
66
 
54
67
  Then /^the interval should end at "([^"]*)"$/ do |date|
55
- @edtf.end.to_s.should == date
68
+ @date.end.to_s.should == date
69
+ end
70
+
71
+ Then /^the interval should include the date "([^"]*)"$/ do |date|
72
+ @date.should include(Date.parse(date))
56
73
  end
57
74
 
75
+
58
76
  Then /^the date should be uncertain\? "([^"]*)"$/ do |arg1|
59
- @edtf.uncertain?.should == !!(arg1 =~ /y(es)?/i)
77
+ @date.uncertain?.should == !!(arg1 =~ /y(es)?/i)
60
78
  end
61
79
 
62
80
  Then /^the year should be uncertain\? "([^"]*)"$/ do |arg1|
63
- @edtf.uncertain?(:year).should == !!(arg1 =~ /y(es)?/i)
81
+ @date.uncertain?(:year).should == !!(arg1 =~ /y(es)?/i)
64
82
  end
65
83
 
66
84
  Then /^the month should be uncertain\? "([^"]*)"$/ do |arg1|
67
- @edtf.uncertain?(:month).should == !!(arg1 =~ /y(es)?/i)
85
+ @date.uncertain?(:month).should == !!(arg1 =~ /y(es)?/i)
68
86
  end
69
87
 
70
88
  Then /^the day should be uncertain\? "([^"]*)"$/ do |arg1|
71
- @edtf.uncertain?(:day).should == !!(arg1 =~ /y(es)?/i)
89
+ @date.uncertain?(:day).should == !!(arg1 =~ /y(es)?/i)
72
90
  end
73
91
 
74
92
  Then /^the date should be approximate\? "([^"]*)"$/ do |arg1|
75
- @edtf.approximate?.should == !!(arg1 =~ /y(es)?/i)
93
+ @date.approximate?.should == !!(arg1 =~ /y(es)?/i)
76
94
  end
77
95
 
78
96
  Then /^the unspecified string code be "([^"]*)"$/ do |arg1|
79
- @edtf.unspecified.to_s.should == arg1
97
+ @date.unspecified.to_s.should == arg1
80
98
  end
data/lib/edtf.rb CHANGED
@@ -33,13 +33,21 @@ autoload :Rational, 'rational'
33
33
 
34
34
  require 'forwardable'
35
35
 
36
+ require 'active_support/core_ext/date/calculations'
37
+ require 'active_support/core_ext/date_time/calculations'
38
+ require 'active_support/core_ext/time/calculations'
39
+
40
+
41
+ require 'active_support/core_ext/date/conversions'
42
+
36
43
  require 'edtf/compatibility'
37
44
 
38
45
  require 'edtf/version'
39
46
  require 'edtf/uncertainty'
40
- require 'edtf/seasons'
41
47
  require 'edtf/date'
42
48
  require 'edtf/date_time'
49
+ require 'edtf/epoch'
50
+ require 'edtf/season'
43
51
  require 'edtf/interval'
44
52
  require 'edtf/parser'
45
53
  require 'edtf/extensions'
@@ -1,11 +1,10 @@
1
1
 
2
- unless DateTime.respond_to?(:to_time)
3
- require 'time'
4
-
5
- class DateTime
6
- def to_time
7
- Time.parse(to_s)
8
- end
9
- end
10
-
11
- end
2
+ # unless DateTime.respond_to?(:to_time)
3
+ # require 'time'
4
+ #
5
+ # class DateTime
6
+ # def to_time
7
+ # Time.parse(to_s)
8
+ # end
9
+ # end
10
+ # end
data/lib/edtf/date.rb CHANGED
@@ -1,140 +1,187 @@
1
- module EDTF
1
+ class Date
2
2
 
3
3
  PRECISIONS = [:year, :month, :day].freeze
4
+ FORMATS = %w{ %04d %02d %02d }.freeze
4
5
 
5
- module ExtendedDate
6
-
7
- extend Forwardable
8
-
9
- include Seasons
10
-
11
- attr_accessor :calendar
12
-
13
- attr_reader :precision
14
-
15
- def precision=(precision)
16
- raise ArgumentError, "invalid precision #{precision.inspect}" unless PRECISIONS.include?(precision.to_sym)
17
- @precision = precision.to_sym
18
- update_precision_filter[-1]
19
- end
20
-
21
- def self.included(base)
22
- base.extend(ClassMethods)
6
+ EXTENDED_ATTRIBUTES = %w{ calendar precision uncertain approximate
7
+ unspecified }.map(&:intern).freeze
8
+
9
+ extend Forwardable
10
+
11
+ class << self
12
+ def edtf(input, options = {})
13
+ ::EDTF::Parser.new(options).parse(input)
23
14
  end
24
-
25
- def uncertain
26
- @uncertain ||= Uncertainty.new
15
+ end
16
+
17
+ attr_accessor :calendar
18
+
19
+ PRECISIONS.each do |p|
20
+ define_method("#{p}_precision?") { @precision == p }
21
+ define_method("#{p}_precision!") { @precision = p }
22
+ end
23
+
24
+ def dup
25
+ d = super
26
+ d.uncertain = uncertain.dup
27
+ d.approximate = approximate.dup
28
+ d.unspecified = unspecified.dup
29
+ d
30
+ end
31
+
32
+ # Alias change method from Active Support.
33
+ alias original_change change
34
+
35
+ # Returns a new Date where one or more of the elements have been changed according to the +options+ parameter.
36
+ def change(options)
37
+ d = original_change(options)
38
+ EXTENDED_ATTRIBUTES.each do |attribute|
39
+ d.send("#{attribute}=", options[attribute] || send(attribute))
27
40
  end
41
+ d
42
+ end
43
+
44
+ # Returns this Date's precision.
45
+ def precision
46
+ @precision ||= :day
47
+ end
48
+
49
+ def precision=(precision)
50
+ precision = precision.intern
51
+ raise ArgumentError, "invalid precision #{precision.inspect}" unless PRECISIONS.include?(precision)
52
+ @precision = precision
53
+ update_precision_filter[-1]
54
+ end
55
+
56
+ def self.included(base)
57
+ base.extend(ClassMethods)
58
+ end
59
+
60
+ def uncertain
61
+ @uncertain ||= EDTF::Uncertainty.new
62
+ end
28
63
 
29
- def approximate
30
- @approximate ||= Uncertainty.new
31
- end
64
+ def approximate
65
+ @approximate ||= EDTF::Uncertainty.new
66
+ end
32
67
 
33
- def unspecified
34
- @unspecified ||= Unspecified.new
35
- end
36
-
37
- def_delegators :uncertain, :uncertain?, :certain?
68
+ def unspecified
69
+ @unspecified ||= EDTF::Unspecified.new
70
+ end
71
+
72
+ def_delegators :uncertain, :uncertain?, :certain?
38
73
 
39
- def certain!(*arguments)
40
- uncertain.certain!(*arguments)
41
- self
42
- end
43
-
44
- def uncertain!(*arguments)
45
- uncertain.uncertain!(*arguments)
46
- self
47
- end
74
+ def certain!(*arguments)
75
+ uncertain.certain!(*arguments)
76
+ self
77
+ end
78
+
79
+ def uncertain!(*arguments)
80
+ uncertain.uncertain!(*arguments)
81
+ self
82
+ end
48
83
 
49
- def approximate?(*arguments)
50
- approximate.uncertain?(*arguments)
51
- end
52
-
53
- alias approximately? approximate?
54
-
55
- def approximate!(*arguments)
56
- approximate.uncertain!(*arguments)
57
- self
58
- end
59
-
60
- alias approximately! approximate!
61
-
62
- def precise?(*arguments)
63
- !approximate?(*arguments)
64
- end
65
-
66
- alias precisely? precise?
84
+ def approximate?(*arguments)
85
+ approximate.uncertain?(*arguments)
86
+ end
87
+
88
+ alias approximately? approximate?
89
+
90
+ def approximate!(*arguments)
91
+ approximate.uncertain!(*arguments)
92
+ self
93
+ end
94
+
95
+ alias approximately! approximate!
96
+
97
+ def precise?(*arguments)
98
+ !approximate?(*arguments)
99
+ end
100
+
101
+ alias precisely? precise?
67
102
 
68
- def precise!(*arguments)
69
- approximate.certain!(*arguments)
70
- self
71
- end
103
+ def precise!(*arguments)
104
+ approximate.certain!(*arguments)
105
+ self
106
+ end
72
107
 
73
- alias precisely! precise!
74
-
75
- def_delegators :unspecified, :unspecified?, :specified?, :unsepcific?, :specific?
76
-
77
- def unspecified!(*arguments)
78
- unspecified.unspecified!(*arguments)
79
- self
80
- end
108
+ alias precisely! precise!
109
+
110
+ def_delegators :unspecified, :unspecified?, :specified?, :unsepcific?, :specific?
111
+
112
+ def unspecified!(*arguments)
113
+ unspecified.unspecified!(*arguments)
114
+ self
115
+ end
81
116
 
82
- alias unspecific! unspecified!
117
+ alias unspecific! unspecified!
83
118
 
84
- def specified!(*arguments)
85
- unspecified.specified!(*arguments)
86
- self
87
- end
119
+ def specified!(*arguments)
120
+ unspecified.specified!(*arguments)
121
+ self
122
+ end
88
123
 
89
- alias specific! specified!
90
-
91
- def to_edtf
92
- "TODO"
93
- end
124
+ alias specific! specified!
125
+
126
+ def season?; false; end
127
+
128
+ def season
129
+ Season.new(self)
130
+ end
131
+
132
+ def edtf
133
+ FORMATS.take(values.length).join('-') % values
134
+ end
94
135
 
95
- # TODO take precision into account
96
- def next
97
- super
98
- end
99
-
100
- def values
101
- precision_filter.map { |p| send(p) }
102
- end
103
-
104
- # Returns the same date with negated year
105
- def negate
106
- v = values
107
- y = -1 * v.shift
108
- self.class.new(y, *v) # TODO copy extended attributes
109
- end
110
-
111
- alias -@ negate
112
-
113
-
114
- private
115
-
116
- def precision_filter
117
- @precision_filter ||= update_precision_filter
118
- end
119
-
120
- def update_precision_filter
121
- case @precision
122
- when :year
123
- [:year]
124
- when :month
125
- [:year,:month]
126
- else
127
- [:year,:month,:day]
128
- end
129
- end
136
+ alias to_edtf edtf
137
+
138
+ def next
139
+ send("next_#{precision}")
140
+ end
141
+
142
+ # def succ
143
+ # end
130
144
 
131
-
132
- module ClassMethods
133
- def edtf(input, options = {})
134
- ::EDTF::Parser.new(options).parse(input)
135
- end
145
+ # def ==(other)
146
+ # end
147
+
148
+ # def <=>(other)
149
+ # end
150
+
151
+ # def ===(other)
152
+ # end
153
+
154
+ def values
155
+ precision_filter.map { |p| send(p) }
156
+ end
157
+
158
+ # Returns the same date with negated year
159
+ def negate
160
+ change(:year => year * -1)
161
+ end
162
+
163
+ alias -@ negate
164
+
165
+
166
+ private
167
+
168
+ def precision_filter
169
+ @precision_filter ||= update_precision_filter
170
+ end
171
+
172
+ def update_precision_filter
173
+ case @precision
174
+ when :year
175
+ [:year]
176
+ when :month
177
+ [:year,:month]
178
+ else
179
+ [:year,:month,:day]
136
180
  end
137
-
138
181
  end
182
+
183
+ protected
184
+
185
+ attr_writer :uncertain, :unspecified, :approximate
139
186
 
140
- end
187
+ end