iso8601 0.8.0 → 0.8.1
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/.rubocop.yml +38 -0
- data/.travis.yml +12 -1
- data/CHANGELOG.md +4 -0
- data/Gemfile +0 -5
- data/README.md +4 -2
- data/iso8601.gemspec +5 -5
- data/lib/iso8601/atoms.rb +38 -36
- data/lib/iso8601/date.rb +20 -16
- data/lib/iso8601/date_time.rb +29 -26
- data/lib/iso8601/duration.rb +54 -49
- data/lib/iso8601/errors.rb +3 -6
- data/lib/iso8601/time.rb +25 -19
- data/lib/iso8601/version.rb +1 -1
- data/spec/iso8601/duration_spec.rb +2 -1
- data/spec/spec_helper.rb +1 -1
- metadata +11 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ebc324dddf45972ba0a794b182d1cc9057aba93a
|
4
|
+
data.tar.gz: df0e9923948069eff193d5cad7db26e244bb25aa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d475f9aa2193aeb8d1e935fb2b74aaeb98112184d5e3d95243d10768bc10799327a130e57def52791bf0a310de16ae5703962cb7ee8d23e8a3a56ec06b9f6f6c
|
7
|
+
data.tar.gz: d8e4f640791540d73a3388d11c2a4cbf01379cf34db30324b6e83fadf69213be7e099465edb1860387498d0d7221a064b2bf3829379dd37b07a7bb4f58eb15ae
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
Documentation:
|
2
|
+
Enabled: false
|
3
|
+
|
4
|
+
Lint/UselessComparison:
|
5
|
+
Enabled: false
|
6
|
+
|
7
|
+
Metrics/ClassLength:
|
8
|
+
Max: 150
|
9
|
+
|
10
|
+
# Work in progress
|
11
|
+
Metrics/LineLength:
|
12
|
+
Max: 183
|
13
|
+
|
14
|
+
# Work in progress
|
15
|
+
Metrics/MethodLength:
|
16
|
+
Max: 24
|
17
|
+
|
18
|
+
# Work in progress
|
19
|
+
Metrics/PerceivedComplexity:
|
20
|
+
Max: 8
|
21
|
+
|
22
|
+
Style/Blocks:
|
23
|
+
Enabled: false
|
24
|
+
|
25
|
+
Style/NumericLiterals:
|
26
|
+
MinDigits: 10
|
27
|
+
|
28
|
+
Style/RegexpLiteral:
|
29
|
+
MaxSlashes: 0
|
30
|
+
|
31
|
+
Style/StringLiterals:
|
32
|
+
Enabled: false
|
33
|
+
|
34
|
+
Style/Encoding:
|
35
|
+
EnforcedStyle: when_needed
|
36
|
+
|
37
|
+
Style/BracesAroundHashParameters:
|
38
|
+
EnforcedStyle: braces
|
data/.travis.yml
CHANGED
@@ -3,5 +3,16 @@ rvm:
|
|
3
3
|
- 1.9.3
|
4
4
|
- 2.0.0
|
5
5
|
- 2.1.2
|
6
|
-
-
|
6
|
+
- ruby-head
|
7
|
+
- rbx-2
|
8
|
+
- jruby-head
|
7
9
|
script: bundle exec rspec spec
|
10
|
+
matrix:
|
11
|
+
include:
|
12
|
+
- rvm: jruby
|
13
|
+
env: JRUBY_OPTS='--2.0'
|
14
|
+
allow_failures:
|
15
|
+
- rvm: jruby-head
|
16
|
+
- rvm: ruby-head
|
17
|
+
- rvm: rbx-2
|
18
|
+
fast_finish: true
|
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -8,6 +8,8 @@ times) standard.
|
|
8
8
|
|
9
9
|
[](http://travis-ci.org/arnau/ISO8601/)
|
10
10
|
|
11
|
+
[](https://gemnasium.com/arnau/ISO8601)
|
12
|
+
|
11
13
|
## Supported versions
|
12
14
|
|
13
15
|
* MRI 1.9.3
|
@@ -22,8 +24,8 @@ Check the [changelog](https://github.com/arnau/ISO8601/blob/master/CHANGELOG.md)
|
|
22
24
|
|
23
25
|
### Duration sign
|
24
26
|
|
25
|
-
Because Durations and DateTime
|
26
|
-
sign to be able to represent
|
27
|
+
Because `Durations` and `DateTime` have a substraction method, `Durations` has
|
28
|
+
sign to be able to represent negative values:
|
27
29
|
|
28
30
|
(ISO8601::Duration.new('PT10S') - ISO8601::Duration.new('PT12S')).to_s #=> '-PT2S'
|
29
31
|
(ISO8601::Duration.new('-PT10S') + ISO8601::Duration.new('PT12S')).to_s #=> 'PT2S'
|
data/iso8601.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
|
3
|
+
$LOAD_PATH.push File.expand_path('../lib', __FILE__)
|
4
4
|
require 'iso8601/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
@@ -12,8 +12,8 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.homepage = 'https://github.com/arnau/ISO8601'
|
13
13
|
s.summary = "Ruby parser to work with ISO 8601 dateTimes and durations - http://en.wikipedia.org/wiki/ISO_8601"
|
14
14
|
s.description = <<-EOD
|
15
|
-
ISO8601 is a simple implementation in Ruby of the ISO 8601 (Data elements and
|
16
|
-
interchange formats - Information interchange - Representation of dates
|
15
|
+
ISO8601 is a simple implementation in Ruby of the ISO 8601 (Data elements and
|
16
|
+
interchange formats - Information interchange - Representation of dates
|
17
17
|
and times) standard.
|
18
18
|
EOD
|
19
19
|
s.license = 'MIT'
|
@@ -24,7 +24,7 @@ Gem::Specification.new do |s|
|
|
24
24
|
s.require_paths = ['lib']
|
25
25
|
|
26
26
|
s.has_rdoc = 'yard'
|
27
|
-
|
27
|
+
s.required_ruby_version = '>= 1.9.3'
|
28
28
|
s.add_development_dependency 'rspec', '~> 3.1'
|
29
|
-
s.add_development_dependency '
|
29
|
+
s.add_development_dependency 'rubocop', '~> 0.26'
|
30
30
|
end
|
data/lib/iso8601/atoms.rb
CHANGED
@@ -10,9 +10,9 @@ module ISO8601
|
|
10
10
|
# @param [Numeric] atom The atom value
|
11
11
|
# @param [ISO8601::DateTime, nil] base (nil) The base datetime to compute
|
12
12
|
# the atom factor.
|
13
|
-
def initialize(atom, base=nil)
|
14
|
-
|
15
|
-
|
13
|
+
def initialize(atom, base = nil)
|
14
|
+
fail TypeError, "The atom argument for #{inspect} should be a Numeric value." unless atom.is_a?(Numeric)
|
15
|
+
fail TypeError, "The base argument for #{inspect} should be a ISO8601::DateTime instance or nil." unless base.is_a?(ISO8601::DateTime) || base.nil?
|
16
16
|
@atom = atom
|
17
17
|
@base = base
|
18
18
|
end
|
@@ -55,18 +55,18 @@ module ISO8601
|
|
55
55
|
atom * factor
|
56
56
|
end
|
57
57
|
##
|
58
|
-
# @param [#hash]
|
58
|
+
# @param [#hash] other The contrast to compare against
|
59
59
|
#
|
60
60
|
# @return [Boolean]
|
61
|
-
def ==(
|
62
|
-
(hash ==
|
61
|
+
def ==(other)
|
62
|
+
(hash == other.hash)
|
63
63
|
end
|
64
64
|
##
|
65
|
-
# @param [#hash]
|
65
|
+
# @param [#hash] other The contrast to compare against
|
66
66
|
#
|
67
67
|
# @return [Boolean]
|
68
|
-
def eql?(
|
69
|
-
(hash ==
|
68
|
+
def eql?(other)
|
69
|
+
(hash == other.hash)
|
70
70
|
end
|
71
71
|
##
|
72
72
|
# @return [Fixnum]
|
@@ -76,26 +76,27 @@ module ISO8601
|
|
76
76
|
##
|
77
77
|
# The atom factor to compute the amount of seconds for the atom
|
78
78
|
def factor
|
79
|
-
|
79
|
+
fail NotImplementedError,
|
80
|
+
"The #factor method should be implemented by each subclass"
|
80
81
|
end
|
81
82
|
end
|
82
83
|
##
|
83
84
|
# A Years atom in a {ISO8601::Duration}
|
84
85
|
#
|
85
|
-
# A
|
86
|
+
# A "calendar year" is the cyclic time interval in a calendar which is
|
86
87
|
# required for one revolution of the Earth around the Sun and approximated to
|
87
|
-
# an integral number of
|
88
|
+
# an integral number of "calendar days".
|
88
89
|
#
|
89
|
-
# A
|
90
|
-
# the start and/or the end of the corresponding time interval within the
|
91
|
-
# specific
|
90
|
+
# A "duration year" is the duration of 365 or 366 "calendar days" depending
|
91
|
+
# on the start and/or the end of the corresponding time interval within the
|
92
|
+
# specific "calendar year".
|
92
93
|
class Years < ISO8601::Atom
|
93
94
|
##
|
94
95
|
# The Year factor
|
95
96
|
#
|
96
|
-
# The
|
97
|
-
#
|
98
|
-
# years
|
97
|
+
# The "duration year" average is calculated through time intervals of 400
|
98
|
+
# "duration years". Each cycle of 400 "duration years" has 303 "common
|
99
|
+
# years" of 365 "calendar days" and 97 "leap years" of 366 "calendar days".
|
99
100
|
#
|
100
101
|
# @return [Integer]
|
101
102
|
def factor
|
@@ -120,19 +121,19 @@ module ISO8601
|
|
120
121
|
##
|
121
122
|
# A Months atom in a {ISO8601::Duration}
|
122
123
|
#
|
123
|
-
# A
|
124
|
-
#
|
124
|
+
# A "calendar month" is the time interval resulting from the division of a
|
125
|
+
# "calendar year" in 12 time intervals.
|
125
126
|
#
|
126
|
-
# A
|
127
|
+
# A "duration month" is the duration of 28, 29, 30 or 31 "calendar days"
|
127
128
|
# depending on the start and/or the end of the corresponding time interval
|
128
|
-
# within the specific
|
129
|
+
# within the specific "calendar month".
|
129
130
|
class Months < ISO8601::Atom
|
130
131
|
##
|
131
132
|
# The Month factor
|
132
133
|
#
|
133
|
-
# The
|
134
|
-
#
|
135
|
-
# years
|
134
|
+
# The "duration month" average is calculated through time intervals of 400
|
135
|
+
# "duration years". Each cycle of 400 "duration years" has 303 "common
|
136
|
+
# years" of 365 "calendar days" and 97 "leap years" of 366 "calendar days".
|
136
137
|
def factor
|
137
138
|
if base.nil?
|
138
139
|
nobase_calculation
|
@@ -157,25 +158,26 @@ module ISO8601
|
|
157
158
|
end
|
158
159
|
|
159
160
|
def zero_calculation
|
160
|
-
month = (base.month <= 12) ?
|
161
|
+
month = (base.month <= 12) ? base.month : (base.month % 12)
|
161
162
|
year = base.year + ((base.month) / 12).to_i
|
162
163
|
|
163
164
|
(::Time.utc(year, month) - ::Time.utc(base.year, base.month))
|
164
165
|
end
|
165
166
|
|
166
167
|
def calculation
|
167
|
-
|
168
|
+
initial = base.month + atom
|
169
|
+
if initial <= 0
|
168
170
|
month = base.month + atom
|
169
171
|
|
170
|
-
if
|
171
|
-
year = base.year + (
|
172
|
+
if initial % 12 == 0
|
173
|
+
year = base.year + (initial / 12) - 1
|
172
174
|
month = 12
|
173
175
|
else
|
174
|
-
year = base.year + (
|
175
|
-
month = (12 +
|
176
|
+
year = base.year + (initial / 12).floor
|
177
|
+
month = (12 + initial > 0) ? (12 + initial) : (12 + (initial % -12))
|
176
178
|
end
|
177
179
|
else
|
178
|
-
month = (
|
180
|
+
month = (initial <= 12) ? initial : (initial % 12)
|
179
181
|
month = 12 if month.zero?
|
180
182
|
year = base.year + ((base.month + atom) / 12).to_i
|
181
183
|
end
|
@@ -202,9 +204,9 @@ module ISO8601
|
|
202
204
|
##
|
203
205
|
# The Days atom in a {ISO8601::Duration}
|
204
206
|
#
|
205
|
-
# A
|
206
|
-
# at a certain
|
207
|
-
#
|
207
|
+
# A "calendar day" is the time interval which starts at a certain time of day
|
208
|
+
# at a certain "calendar day" and ends at the same time of day at the next
|
209
|
+
# "calendar day".
|
208
210
|
class Days < ISO8601::Atom
|
209
211
|
##
|
210
212
|
# The Day factor
|
@@ -256,7 +258,7 @@ module ISO8601
|
|
256
258
|
#
|
257
259
|
# The second is the base unit of measurement of time in the International
|
258
260
|
# System of Units (SI) as defined by the International Committee of Weights
|
259
|
-
# and Measures
|
261
|
+
# and Measures.
|
260
262
|
class Seconds < ISO8601::Atom
|
261
263
|
##
|
262
264
|
# The Second factor
|
data/lib/iso8601/date.rb
CHANGED
@@ -15,9 +15,11 @@ module ISO8601
|
|
15
15
|
class Date
|
16
16
|
extend Forwardable
|
17
17
|
|
18
|
-
def_delegators(
|
18
|
+
def_delegators(
|
19
|
+
:@date,
|
19
20
|
:to_s, :to_time, :to_date, :to_datetime,
|
20
|
-
:year, :month, :day, :wday
|
21
|
+
:year, :month, :day, :wday
|
22
|
+
)
|
21
23
|
##
|
22
24
|
# The original atoms
|
23
25
|
attr_reader :atoms
|
@@ -41,20 +43,20 @@ module ISO8601
|
|
41
43
|
##
|
42
44
|
# Forwards the date the given amount of days.
|
43
45
|
#
|
44
|
-
# @param [Numeric]
|
46
|
+
# @param [Numeric] other The days to add
|
45
47
|
#
|
46
48
|
# @return [ISO8601::Date] New date resulting of the addition
|
47
|
-
def +(
|
48
|
-
ISO8601::Date.new((@date +
|
49
|
+
def +(other)
|
50
|
+
ISO8601::Date.new((@date + other).iso8601)
|
49
51
|
end
|
50
52
|
##
|
51
53
|
# Backwards the date the given amount of days.
|
52
54
|
#
|
53
|
-
# @param [Numeric]
|
55
|
+
# @param [Numeric] other The days to remove
|
54
56
|
#
|
55
57
|
# @return [ISO8601::Date] New date resulting of the substraction
|
56
|
-
def -(
|
57
|
-
ISO8601::Date.new((@date -
|
58
|
+
def -(other)
|
59
|
+
ISO8601::Date.new((@date - other).iso8601)
|
58
60
|
end
|
59
61
|
##
|
60
62
|
# Converts self to an array of atoms.
|
@@ -62,18 +64,18 @@ module ISO8601
|
|
62
64
|
[year, month, day]
|
63
65
|
end
|
64
66
|
##
|
65
|
-
# @param [#hash]
|
67
|
+
# @param [#hash] other The contrast to compare against
|
66
68
|
#
|
67
69
|
# @return [Boolean]
|
68
|
-
def ==(
|
69
|
-
(hash ==
|
70
|
+
def ==(other)
|
71
|
+
(hash == other.hash)
|
70
72
|
end
|
71
73
|
##
|
72
|
-
# @param [#hash]
|
74
|
+
# @param [#hash] other The contrast to compare against
|
73
75
|
#
|
74
76
|
# @return [Boolean]
|
75
|
-
def eql?(
|
76
|
-
(hash ==
|
77
|
+
def eql?(other)
|
78
|
+
(hash == other.hash)
|
77
79
|
end
|
78
80
|
##
|
79
81
|
# @return [Fixnum]
|
@@ -82,6 +84,7 @@ module ISO8601
|
|
82
84
|
end
|
83
85
|
|
84
86
|
private
|
87
|
+
|
85
88
|
##
|
86
89
|
# Splits the date component into valid atoms.
|
87
90
|
#
|
@@ -101,7 +104,8 @@ module ISO8601
|
|
101
104
|
week_date = /^([+-]?)\d{4}(-?)W\d{2}(?:\2\d)?$/.match(input)
|
102
105
|
return atomize_week_date(input, week_date[2], week_date[1]) unless week_date.nil?
|
103
106
|
|
104
|
-
|
107
|
+
ordinal_regexp = /^([+-]?)(\d{4})(-?)(\d{3})$/
|
108
|
+
_, sign, year, separator, day = ordinal_regexp.match(input).to_a.compact
|
105
109
|
return atomize_ordinal(year, day, separator, sign) unless year.nil?
|
106
110
|
|
107
111
|
_, year, separator, month, day = /^
|
@@ -114,7 +118,7 @@ module ISO8601
|
|
114
118
|
)?
|
115
119
|
$/x.match(input).to_a.compact
|
116
120
|
|
117
|
-
|
121
|
+
fail ISO8601::Errors::UnknownPattern, @original if year.nil?
|
118
122
|
|
119
123
|
@separator = separator
|
120
124
|
|
data/lib/iso8601/date_time.rb
CHANGED
@@ -10,9 +10,11 @@ module ISO8601
|
|
10
10
|
class DateTime
|
11
11
|
extend Forwardable
|
12
12
|
|
13
|
-
def_delegators(
|
13
|
+
def_delegators(
|
14
|
+
:@date_time,
|
14
15
|
:strftime, :to_time, :to_date, :to_datetime,
|
15
|
-
:year, :month, :day, :hour, :minute, :zone
|
16
|
+
:year, :month, :day, :hour, :minute, :zone
|
17
|
+
)
|
16
18
|
|
17
19
|
attr_reader :second
|
18
20
|
|
@@ -29,9 +31,9 @@ module ISO8601
|
|
29
31
|
##
|
30
32
|
# Addition
|
31
33
|
#
|
32
|
-
# @param [Numeric]
|
33
|
-
def +(
|
34
|
-
moment = @date_time.to_time.localtime(zone) +
|
34
|
+
# @param [Numeric] other The seconds to add
|
35
|
+
def +(other)
|
36
|
+
moment = @date_time.to_time.localtime(zone) + other
|
35
37
|
format = moment.subsec.zero? ? FORMAT : FORMAT_WITH_FRACTION
|
36
38
|
|
37
39
|
ISO8601::DateTime.new(moment.strftime(format))
|
@@ -39,9 +41,9 @@ module ISO8601
|
|
39
41
|
##
|
40
42
|
# Substraction
|
41
43
|
#
|
42
|
-
# @param [Numeric]
|
43
|
-
def -(
|
44
|
-
moment = @date_time.to_time.localtime(zone) -
|
44
|
+
# @param [Numeric] other The seconds to substract
|
45
|
+
def -(other)
|
46
|
+
moment = @date_time.to_time.localtime(zone) - other
|
45
47
|
format = moment.subsec.zero? ? FORMAT : FORMAT_WITH_FRACTION
|
46
48
|
|
47
49
|
ISO8601::DateTime.new(moment.strftime(format))
|
@@ -58,18 +60,18 @@ module ISO8601
|
|
58
60
|
[year, month, day, hour, minute, second, zone]
|
59
61
|
end
|
60
62
|
##
|
61
|
-
# @param [#hash]
|
63
|
+
# @param [#hash] other The contrast to compare against
|
62
64
|
#
|
63
65
|
# @return [Boolean]
|
64
|
-
def ==(
|
65
|
-
(hash ==
|
66
|
+
def ==(other)
|
67
|
+
(hash == other.hash)
|
66
68
|
end
|
67
69
|
##
|
68
|
-
# @param [#hash]
|
70
|
+
# @param [#hash] other The contrast to compare against
|
69
71
|
#
|
70
72
|
# @return [Boolean]
|
71
|
-
def eql?(
|
72
|
-
(hash ==
|
73
|
+
def eql?(other)
|
74
|
+
(hash == other.hash)
|
73
75
|
end
|
74
76
|
##
|
75
77
|
# @return [Fixnum]
|
@@ -77,8 +79,8 @@ module ISO8601
|
|
77
79
|
[second, self.class].hash
|
78
80
|
end
|
79
81
|
|
80
|
-
|
81
82
|
private
|
83
|
+
|
82
84
|
##
|
83
85
|
# Parses an ISO date time, where the date and the time components are
|
84
86
|
# optional.
|
@@ -87,20 +89,20 @@ module ISO8601
|
|
87
89
|
#
|
88
90
|
# @param [String] date_time The ISO representation
|
89
91
|
def parse(date_time)
|
90
|
-
|
92
|
+
fail ISO8601::Errors::UnknownPattern, date_time if date_time.empty?
|
91
93
|
|
92
94
|
date, time = date_time.split('T')
|
93
95
|
|
94
|
-
|
95
|
-
|
96
|
-
separators = [
|
96
|
+
date_atoms = parse_date(date)
|
97
|
+
time_atoms = Array(time && parse_time(time))
|
98
|
+
separators = [date_atoms.pop, time_atoms.pop]
|
97
99
|
|
98
|
-
|
99
|
-
|
100
|
+
fail ISO8601::Errors::UnknownPattern,
|
101
|
+
@original unless valid_representation?(date_atoms, time_atoms)
|
102
|
+
fail ISO8601::Errors::UnknownPattern,
|
103
|
+
@original unless valid_separators?(separators)
|
100
104
|
|
101
|
-
|
102
|
-
|
103
|
-
::DateTime.new(*components.compact)
|
105
|
+
::DateTime.new(*(date_atoms + time_atoms).compact)
|
104
106
|
end
|
105
107
|
##
|
106
108
|
# Validates the date has the right pattern.
|
@@ -134,13 +136,14 @@ module ISO8601
|
|
134
136
|
unless separators.all?(&:empty?)
|
135
137
|
return false if (separators.first.length != separators.last.length)
|
136
138
|
end
|
137
|
-
|
139
|
+
|
140
|
+
true
|
138
141
|
end
|
139
142
|
##
|
140
143
|
# If time is provided date must use a complete representation
|
141
144
|
def valid_representation?(date, time)
|
142
145
|
year, month, day = date
|
143
|
-
hour,
|
146
|
+
hour, _ = time
|
144
147
|
|
145
148
|
date.nil? || !(!year.nil? && (month.nil? || day.nil?) && !hour.nil?)
|
146
149
|
end
|
data/lib/iso8601/duration.rb
CHANGED
@@ -3,7 +3,8 @@
|
|
3
3
|
module ISO8601
|
4
4
|
##
|
5
5
|
# A duration representation. When no base is provided, all atoms use an
|
6
|
-
# average factor which affects the result of any computation like
|
6
|
+
# average factor which affects the result of any computation like
|
7
|
+
# `#to_seconds`.
|
7
8
|
#
|
8
9
|
# @example
|
9
10
|
# d = ISO8601::Duration.new('P2Y1MT2H')
|
@@ -14,11 +15,16 @@ module ISO8601
|
|
14
15
|
# d.to_seconds # => 65707200.0
|
15
16
|
#
|
16
17
|
# @example Explicit base date time
|
17
|
-
#
|
18
|
-
# d
|
19
|
-
# d.
|
20
|
-
#
|
21
|
-
# d.
|
18
|
+
# base = ISO8601::DateTime.new('2014-08017')
|
19
|
+
# d = ISO8601::Duration.new('P2Y1MT2H', base)
|
20
|
+
# d.years # => #<ISO8601::Years:0x000000051adee8 @atom=2.0,
|
21
|
+
# @base=#<ISO8601::DateTime...>>
|
22
|
+
# d.months # => #<ISO8601::Months:0x00000004f230b0 @atom=1.0,
|
23
|
+
# @base=#<ISO8601::DateTime...>>
|
24
|
+
# d.days # => #<ISO8601::Days:0x00000005205468 @atom=0,
|
25
|
+
# @base=#<ISO8601::DateTime...>>
|
26
|
+
# d.hours # => #<ISO8601::Hours:0x000000051e02a8 @atom=2.0,
|
27
|
+
# @base=#<ISO8601::DateTime...>>
|
22
28
|
# d.to_seconds # => 65757600.0
|
23
29
|
#
|
24
30
|
# @example Number of seconds versus patterns
|
@@ -111,49 +117,40 @@ module ISO8601
|
|
111
117
|
##
|
112
118
|
# Addition
|
113
119
|
#
|
114
|
-
# @param [ISO8601::Duration]
|
120
|
+
# @param [ISO8601::Duration] other The duration to add
|
115
121
|
#
|
116
122
|
# @raise [ISO8601::Errors::DurationBaseError] If bases doesn't match
|
117
123
|
# @return [ISO8601::Duration]
|
118
|
-
def +(
|
119
|
-
compare_bases(
|
120
|
-
|
121
|
-
d1 = to_seconds
|
122
|
-
d2 = duration.to_seconds
|
123
|
-
|
124
|
-
seconds_to_iso(d1 + d2)
|
124
|
+
def +(other)
|
125
|
+
compare_bases(other)
|
126
|
+
seconds_to_iso(to_seconds + other.to_seconds)
|
125
127
|
end
|
126
128
|
##
|
127
129
|
# Substraction
|
128
130
|
#
|
129
|
-
# @param [ISO8601::Duration]
|
131
|
+
# @param [ISO8601::Duration] other The duration to substract
|
130
132
|
#
|
131
133
|
# @raise [ISO8601::Errors::DurationBaseError] If bases doesn't match
|
132
134
|
# @return [ISO8601::Duration]
|
133
|
-
def -(
|
134
|
-
compare_bases(
|
135
|
-
|
136
|
-
d1 = to_seconds
|
137
|
-
d2 = duration.to_seconds
|
138
|
-
|
139
|
-
seconds_to_iso(d1 - d2)
|
135
|
+
def -(other)
|
136
|
+
compare_bases(other)
|
137
|
+
seconds_to_iso(to_seconds - other.to_seconds)
|
140
138
|
end
|
141
139
|
##
|
142
|
-
# @param [ISO8601::Duration]
|
140
|
+
# @param [ISO8601::Duration] other The duration to compare
|
143
141
|
#
|
144
142
|
# @raise [ISO8601::Errors::DurationBaseError] If bases doesn't match
|
145
143
|
# @return [Boolean]
|
146
|
-
def ==(
|
147
|
-
compare_bases(
|
148
|
-
|
149
|
-
(to_seconds == duration.to_seconds)
|
144
|
+
def ==(other)
|
145
|
+
compare_bases(other)
|
146
|
+
(to_seconds == other.to_seconds)
|
150
147
|
end
|
151
148
|
##
|
152
|
-
# @param [ISO8601::Duration]
|
149
|
+
# @param [ISO8601::Duration] other The duration to compare
|
153
150
|
#
|
154
151
|
# @return [Boolean]
|
155
|
-
def eql?(
|
156
|
-
(hash ==
|
152
|
+
def eql?(other)
|
153
|
+
(hash == other.hash)
|
157
154
|
end
|
158
155
|
##
|
159
156
|
# @return [Fixnum]
|
@@ -165,16 +162,17 @@ module ISO8601
|
|
165
162
|
#
|
166
163
|
# @return [String]
|
167
164
|
def to_pattern
|
168
|
-
(@original.
|
165
|
+
(@original.is_a? Numeric) ? "PT#{@original}S" : @original
|
169
166
|
end
|
170
167
|
##
|
171
168
|
# @return [Numeric] The duration in seconds
|
172
169
|
def to_seconds
|
173
|
-
[years, months, weeks, days, hours, minutes, seconds]
|
170
|
+
atoms = [years, months, weeks, days, hours, minutes, seconds]
|
171
|
+
atoms.map(&:to_seconds).reduce(&:+)
|
174
172
|
end
|
175
173
|
|
176
|
-
|
177
174
|
private
|
175
|
+
|
178
176
|
##
|
179
177
|
# Splits a duration pattern into valid atoms.
|
180
178
|
#
|
@@ -207,15 +205,17 @@ module ISO8601
|
|
207
205
|
) |
|
208
206
|
(?<weeks>\d+(?:[.,]\d+)?W)
|
209
207
|
) # Duration
|
210
|
-
$/x)
|
208
|
+
$/x) || fail(ISO8601::Errors::UnknownPattern, input)
|
211
209
|
|
212
210
|
valid_pattern?(duration)
|
213
211
|
|
214
|
-
@sign = (duration[:sign]
|
212
|
+
@sign = (duration[:sign] == '-') ? -1 : 1
|
215
213
|
|
216
|
-
|
217
|
-
|
218
|
-
|
214
|
+
components = duration.names.zip(duration.captures).map! do |k, v|
|
215
|
+
value = v.nil? ? v : v.tr(',', '.')
|
216
|
+
[k.to_sym, sign * value.to_f]
|
217
|
+
end
|
218
|
+
components = Hash[components]
|
219
219
|
components.delete(:time) # clean time capture
|
220
220
|
|
221
221
|
valid_fractions?(components.values)
|
@@ -258,31 +258,36 @@ module ISO8601
|
|
258
258
|
end
|
259
259
|
|
260
260
|
def validate_base(input)
|
261
|
-
|
261
|
+
fail ISO8601::Errors::TypeError unless input.nil? || input.is_a?(ISO8601::DateTime)
|
262
262
|
|
263
263
|
input
|
264
264
|
end
|
265
|
+
|
265
266
|
def valid_pattern?(components)
|
266
|
-
date = [
|
267
|
-
|
267
|
+
date = [
|
268
|
+
components[:years], components[:months], components[:days]
|
269
|
+
]
|
270
|
+
time = [
|
271
|
+
components[:hours], components[:minutes], components[:seconds]
|
272
|
+
].compact
|
268
273
|
weeks = components[:weeks]
|
269
274
|
all = [date, time, weeks].flatten.compact
|
270
275
|
|
271
|
-
|
272
|
-
|
273
|
-
|
276
|
+
missing_time = (weeks.nil? && !components[:time].nil? && time.empty?)
|
277
|
+
empty = missing_time || all.empty?
|
278
|
+
fail ISO8601::Errors::UnknownPattern, @pattern if empty
|
274
279
|
end
|
275
280
|
|
276
281
|
def valid_fractions?(values)
|
277
282
|
values = values.reject(&:zero?)
|
278
283
|
fractions = values.select { |a| (a % 1) != 0 }
|
279
|
-
|
280
|
-
|
281
|
-
|
284
|
+
consistent = (fractions.size == 1 && fractions.last != values.last)
|
285
|
+
|
286
|
+
fail ISO8601::Errors::InvalidFractions if fractions.size > 1 || consistent
|
282
287
|
end
|
283
288
|
|
284
|
-
def compare_bases(
|
285
|
-
|
289
|
+
def compare_bases(other)
|
290
|
+
fail ISO8601::Errors::DurationBaseError, other if base != other.base
|
286
291
|
end
|
287
292
|
end
|
288
293
|
end
|
data/lib/iso8601/errors.rb
CHANGED
@@ -1,23 +1,20 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
module ISO8601
|
4
2
|
##
|
5
3
|
# Contains all ISO8601-specific errors.
|
6
4
|
module Errors
|
7
|
-
|
8
5
|
class StandardError < ::StandardError
|
9
6
|
end
|
10
7
|
##
|
11
8
|
# Raised when the given pattern doesn't fit as ISO 8601 parser.
|
12
9
|
class UnknownPattern < StandardError
|
13
10
|
def initialize(pattern)
|
14
|
-
super("
|
11
|
+
super("Unknown pattern #{pattern}")
|
15
12
|
end
|
16
13
|
end
|
17
14
|
##
|
18
15
|
# Raised when the given pattern contains an invalid fraction.
|
19
16
|
class InvalidFractions < StandardError
|
20
|
-
def initialize
|
17
|
+
def initialize
|
21
18
|
super("Fractions are only allowed in the last component")
|
22
19
|
end
|
23
20
|
end
|
@@ -25,7 +22,7 @@ module ISO8601
|
|
25
22
|
# Raised when the given date is valid but out of range.
|
26
23
|
class RangeError < StandardError
|
27
24
|
def initialize(pattern)
|
28
|
-
super("
|
25
|
+
super("#{pattern} is out of range")
|
29
26
|
end
|
30
27
|
end
|
31
28
|
##
|
data/lib/iso8601/time.rb
CHANGED
@@ -12,9 +12,11 @@ module ISO8601
|
|
12
12
|
class Time
|
13
13
|
extend Forwardable
|
14
14
|
|
15
|
-
def_delegators(
|
15
|
+
def_delegators(
|
16
|
+
:@time,
|
16
17
|
:to_time, :to_date, :to_datetime,
|
17
|
-
:hour, :minute, :zone
|
18
|
+
:hour, :minute, :zone
|
19
|
+
)
|
18
20
|
##
|
19
21
|
# The separator used in the original ISO 8601 string.
|
20
22
|
attr_reader :separator
|
@@ -39,18 +41,18 @@ module ISO8601
|
|
39
41
|
@second = @time.second + @time.second_fraction.to_f
|
40
42
|
end
|
41
43
|
##
|
42
|
-
# @param [#hash]
|
44
|
+
# @param [#hash] other The contrast to compare against
|
43
45
|
#
|
44
46
|
# @return [Boolean]
|
45
|
-
def ==(
|
46
|
-
(hash ==
|
47
|
+
def ==(other)
|
48
|
+
(hash == other.hash)
|
47
49
|
end
|
48
50
|
##
|
49
|
-
# @param [#hash]
|
51
|
+
# @param [#hash] other The contrast to compare against
|
50
52
|
#
|
51
53
|
# @return [Boolean]
|
52
|
-
def eql?(
|
53
|
-
(hash ==
|
54
|
+
def eql?(other)
|
55
|
+
(hash == other.hash)
|
54
56
|
end
|
55
57
|
##
|
56
58
|
# @return [Fixnum]
|
@@ -60,26 +62,28 @@ module ISO8601
|
|
60
62
|
##
|
61
63
|
# Forwards the time the given amount of seconds.
|
62
64
|
#
|
63
|
-
# @param [Numeric]
|
65
|
+
# @param [Numeric] other The seconds to add
|
64
66
|
#
|
65
67
|
# @return [ISO8601::Time] New time resulting of the addition
|
66
|
-
def +(
|
67
|
-
moment = @time.to_time.localtime(zone) +
|
68
|
+
def +(other)
|
69
|
+
moment = @time.to_time.localtime(zone) + other
|
68
70
|
format = moment.subsec.zero? ? FORMAT : FORMAT_WITH_FRACTION
|
71
|
+
base = ::Date.parse(moment.strftime('%Y-%m-%d'))
|
69
72
|
|
70
|
-
ISO8601::Time.new(moment.strftime(format),
|
73
|
+
ISO8601::Time.new(moment.strftime(format), base)
|
71
74
|
end
|
72
75
|
##
|
73
76
|
# Backwards the date the given amount of seconds.
|
74
77
|
#
|
75
|
-
# @param [Numeric]
|
78
|
+
# @param [Numeric] other The seconds to remove
|
76
79
|
#
|
77
80
|
# @return [ISO8601::Time] New time resulting of the substraction
|
78
|
-
def -(
|
79
|
-
moment = @time.to_time.localtime(zone) -
|
81
|
+
def -(other)
|
82
|
+
moment = @time.to_time.localtime(zone) - other
|
80
83
|
format = moment.subsec.zero? ? FORMAT : FORMAT_WITH_FRACTION
|
84
|
+
base = ::Date.parse(moment.strftime('%Y-%m-%d'))
|
81
85
|
|
82
|
-
ISO8601::Time.new(moment.strftime(format),
|
86
|
+
ISO8601::Time.new(moment.strftime(format), base)
|
83
87
|
end
|
84
88
|
##
|
85
89
|
# Converts self to a time component representation.
|
@@ -94,6 +98,7 @@ module ISO8601
|
|
94
98
|
end
|
95
99
|
|
96
100
|
private
|
101
|
+
|
97
102
|
##
|
98
103
|
# Splits the time component into valid atoms.
|
99
104
|
# Acceptable patterns: hh, hh:mm or hhmm and hh:mm:ss or hhmmss. Any form
|
@@ -111,7 +116,7 @@ module ISO8601
|
|
111
116
|
(\d{2})
|
112
117
|
)$/x.match(time).to_a.compact
|
113
118
|
|
114
|
-
|
119
|
+
fail ISO8601::Errors::UnknownPattern, @original if hour.nil?
|
115
120
|
|
116
121
|
@separator = separator
|
117
122
|
|
@@ -119,13 +124,14 @@ module ISO8601
|
|
119
124
|
minute &&= minute.to_i
|
120
125
|
second &&= second.to_f
|
121
126
|
|
122
|
-
|
127
|
+
fail ISO8601::Errors::UnknownPattern, @original unless valid_zone?(zone)
|
123
128
|
|
124
129
|
[hour, minute, second, zone].compact
|
125
130
|
end
|
126
131
|
|
127
132
|
def valid_zone?(zone)
|
128
|
-
|
133
|
+
zone_regexp = /^(Z|[+-]\d{2}(?:(:?)\d{2})?)$/
|
134
|
+
_, offset, separator = zone_regexp.match(zone).to_a.compact
|
129
135
|
|
130
136
|
wrong_pattern = !zone.nil? && offset.nil?
|
131
137
|
invalid_separators = zone.to_s.match(/^[+-]\d{2}:?\d{2}$/) && (@separator != separator)
|
data/lib/iso8601/version.rb
CHANGED
@@ -221,6 +221,7 @@ RSpec.describe ISO8601::Duration do
|
|
221
221
|
it "should return the seconds of a -PT[n]H[n]M duration" do
|
222
222
|
expect(ISO8601::Duration.new('-PT10S', ISO8601::DateTime.new('2012-01-01T00:00:00')).to_seconds).to eq(Time.utc(2011, 12, 31, 23, 59, 50) - Time.utc(2012, 1))
|
223
223
|
expect(ISO8601::Duration.new('-PT10.4S', ISO8601::DateTime.new('2012-01-01')).to_seconds).to eq(-10.4)
|
224
|
+
expect(ISO8601::Duration.new('-PT10,4S', ISO8601::DateTime.new('2012-01-01')).to_seconds).to eq(-10.4)
|
224
225
|
end
|
225
226
|
end
|
226
227
|
end
|
@@ -250,7 +251,7 @@ RSpec.describe ISO8601::Duration do
|
|
250
251
|
expect(subject).to respond_to(:eql?)
|
251
252
|
end
|
252
253
|
it "should equal by hash identity" do
|
253
|
-
expect(ISO8601::Duration.new('PT1H').eql?
|
254
|
+
expect(ISO8601::Duration.new('PT1H').eql? ISO8601::Duration.new('PT1H')).to be_truthy
|
254
255
|
expect(ISO8601::Duration.new('PT1H').eql? ISO8601::Duration.new('PT60M')).to be_falsy
|
255
256
|
end
|
256
257
|
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: iso8601
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arnau Siches
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-09-
|
11
|
+
date: 2014-09-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -25,28 +25,30 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '3.1'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: rubocop
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '0'
|
33
|
+
version: '0.26'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '0'
|
41
|
-
description:
|
42
|
-
|
43
|
-
|
40
|
+
version: '0.26'
|
41
|
+
description: |2
|
42
|
+
ISO8601 is a simple implementation in Ruby of the ISO 8601 (Data elements and
|
43
|
+
interchange formats - Information interchange - Representation of dates
|
44
|
+
and times) standard.
|
44
45
|
email: arnau.siches@gmail.com
|
45
46
|
executables: []
|
46
47
|
extensions: []
|
47
48
|
extra_rdoc_files: []
|
48
49
|
files:
|
49
50
|
- ".gitignore"
|
51
|
+
- ".rubocop.yml"
|
50
52
|
- ".travis.yml"
|
51
53
|
- CHANGELOG.md
|
52
54
|
- Gemfile
|
@@ -80,7 +82,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
80
82
|
requirements:
|
81
83
|
- - ">="
|
82
84
|
- !ruby/object:Gem::Version
|
83
|
-
version:
|
85
|
+
version: 1.9.3
|
84
86
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
85
87
|
requirements:
|
86
88
|
- - ">="
|