iso8601 0.4.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.travis.yml +2 -2
- data/LICENSE +1 -1
- data/README.md +78 -18
- data/iso8601.gemspec +3 -3
- data/lib/iso8601.rb +4 -4
- data/lib/iso8601/atoms.rb +4 -4
- data/lib/iso8601/date.rb +125 -0
- data/lib/iso8601/dateTime.rb +98 -102
- data/lib/iso8601/errors.rb +11 -7
- data/lib/iso8601/time.rb +115 -0
- data/lib/iso8601/version.rb +5 -0
- data/spec/iso8601/atoms_spec.rb +14 -14
- data/spec/iso8601/dateTime_spec.rb +74 -70
- data/spec/iso8601/date_spec.rb +83 -0
- data/spec/iso8601/duration_spec.rb +11 -13
- data/spec/iso8601/time_spec.rb +81 -0
- data/spec/spec_helper.rb +10 -0
- metadata +34 -24
- data/.rspec +0 -2
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 7f9e6e9d7f640ea9bb6d3393080c7fe50f4a1b68
|
4
|
+
data.tar.gz: 45717b674acf7e8eb1a5193f398d52320d4fce89
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 40249de2e3e817cb21e7c441ab3ffd1e18a852ebaf76241e913f9275924dc710dd0d9acad440778c5241ff0d6a8be7f430bab6ae9ebc66aaf03ff5373c16f9d2
|
7
|
+
data.tar.gz: 3733a36fb5764007920b519bed2cc938d76382416ab6592140b74dbd32ca014c8804d8ce973efe7aea9c55ca28db3cdadefcf051469b4659d8d3d670c0aa3fac
|
data/.travis.yml
CHANGED
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,46 +1,107 @@
|
|
1
1
|
# ISO8601
|
2
2
|
|
3
|
-
ISO8601 is a simple implementation of the ISO 8601 (Data elements and
|
4
|
-
interchange formats — Information interchange — Representation of dates and
|
3
|
+
ISO8601 is a simple implementation of the ISO 8601 (Data elements and
|
4
|
+
interchange formats — Information interchange — Representation of dates and
|
5
5
|
times) standard.
|
6
6
|
|
7
7
|
## Build status
|
8
8
|
|
9
9
|
[![Build Status](https://secure.travis-ci.org/arnau/ISO8601.png?branch=master)](http://travis-ci.org/arnau/ISO8601/)
|
10
10
|
|
11
|
+
## Supported versions
|
12
|
+
|
13
|
+
* MRI 1.9.3
|
14
|
+
* MRI 2.0
|
15
|
+
* MRI 2.1
|
16
|
+
* RBX 2
|
17
|
+
|
11
18
|
|
12
19
|
## Comments
|
13
20
|
|
14
21
|
### Duration sign
|
15
22
|
|
16
|
-
Because Durations and DateTime has a substraction method, Durations has
|
23
|
+
Because Durations and DateTime has a substraction method, Durations has
|
24
|
+
sign to be able to represent a negative value:
|
17
25
|
|
18
|
-
(ISO8601::Duration.new('PT10S') - ISO8601::Duration.new('PT12S')).to_s
|
26
|
+
(ISO8601::Duration.new('PT10S') - ISO8601::Duration.new('PT12S')).to_s #=> '-PT2S'
|
19
27
|
(ISO8601::Duration.new('-PT10S') + ISO8601::Duration.new('PT12S')).to_s #=> 'PT2S'
|
20
28
|
|
21
|
-
### Separators
|
22
29
|
|
23
|
-
|
30
|
+
## Differences with core Date, Time and DateTime
|
31
|
+
|
32
|
+
Core `Date.parse` and `DateTime.parse` doesn't allow reduced precision. For
|
33
|
+
example:
|
34
|
+
|
35
|
+
DateTime.parse('2014-05') # => ArgumentError: invalid date
|
36
|
+
|
37
|
+
But the standard covers this situation assuming any missing piece as its lower
|
38
|
+
value:
|
39
|
+
|
40
|
+
ISO8601::DateTime.new('2014-05').to_s # => "2014-05-01T00:00:00+00:00"
|
41
|
+
ISO8601::DateTime.new('2014').to_s # => "2014-01-01T00:00:00+00:00"
|
42
|
+
|
43
|
+
The same assumption happens in core classes with `.new`:
|
44
|
+
|
45
|
+
DateTime.new(2014,5) # => #<DateTime: 2014-05-01T00:00:00+00:00 ((2456779j,0s,0n),+0s,2299161j)>
|
46
|
+
DateTime.new(2014) # => #<DateTime: 2014-01-01T00:00:00+00:00 ((2456659j,0s,0n),+0s,2299161j)>
|
47
|
+
|
48
|
+
|
49
|
+
The value of second in core classes are handled by two methods: `#second` and
|
50
|
+
`#second_fraction`:
|
51
|
+
|
52
|
+
dt = DateTime.parse('2014-05-06T10:11:12.5')
|
53
|
+
dt.second # => 12
|
54
|
+
dt.second_fraction # => (1/2)
|
55
|
+
|
56
|
+
This gem approaches second fraction using floats:
|
24
57
|
|
25
|
-
|
58
|
+
dt = ISO8601::DateTime.new('2014-05-06T10:11:12.5')
|
59
|
+
dt.second # => 12.5
|
26
60
|
|
27
|
-
|
28
|
-
|
61
|
+
Unmatching precison is handled strongly. Notice the time fragment is lost in
|
62
|
+
`DateTime.parse` without warning only if the loose precision is in the time
|
63
|
+
component.
|
29
64
|
|
30
|
-
|
31
|
-
|
65
|
+
ISO8601::DateTime.new('2014-05-06T101112') # => ISO8601::Errors::UnknownPattern
|
66
|
+
DateTime.parse('2014-05-06T101112') # => #<DateTime: 2014-05-06T00:00:00+00:00 ((2456784j,0s,0n),+0s,2299161j)>
|
32
67
|
|
33
|
-
ISO8601::DateTime.new('
|
34
|
-
|
68
|
+
ISO8601::DateTime.new('20140506T10:11:12') # => ISO8601::Errors::UnknownPattern
|
69
|
+
DateTime.parse('20140506T10:11:12') # => #<DateTime: 2014-05-06T10:11:12+00:00 ((2456784j,0s,0n),+0s,2299161j)>
|
70
|
+
|
71
|
+
|
72
|
+
`DateTime#to_a` allow decomposing to an array of atoms:
|
73
|
+
|
74
|
+
atoms = ISO8601::DateTime.new('2014-05-31T10:11:12Z').to_a # => [2014, 5, 31, 10, 11, 12, '+00:00']
|
75
|
+
dt = DateTime.new(*atoms)
|
76
|
+
|
77
|
+
Ordinal dates keep the sign. `2014-001` is not the same as `-2014-001`.
|
78
|
+
|
79
|
+
Week dates raise an error when two digit days provied instead of return monday:
|
80
|
+
|
81
|
+
ISO8601::DateTime.new('2014-W15-02') # => ISO8601::Errors::UnknownPattern
|
82
|
+
DateTime.new('2014-W15-02') # => #<Date: 2014-04-07 ((2456755j,0s,0n),+0s,2299161j)>
|
83
|
+
|
84
|
+
|
85
|
+
## Changes since 0.5
|
86
|
+
|
87
|
+
* Drop support for Ruby 1.8.7
|
88
|
+
* Add support for Rubinius 2
|
89
|
+
* `ISO8601::DateTime#century` no longer exists. Truncated representations were
|
90
|
+
removed in ISO 8601:2004.
|
91
|
+
* `ISO8601::DateTime#zone` delegates to core `DateTime#zone`.
|
92
|
+
* `ISO8601::DateTime#timezone` no longer exists. Now it delegates to
|
93
|
+
`DateTime#zone`.
|
94
|
+
* A date can have sign: `-1000-01-01`, `+2014-05-06T10:11:12Z`.
|
95
|
+
* A date time can be converted to an array of atoms with `#to_a`.
|
96
|
+
* Ordinal dates supported.
|
97
|
+
* A date component is represented by `ISO8601::Date`.
|
98
|
+
* Week date pattern (YYYY-Wdww, YYYY-Www-D).
|
35
99
|
|
36
100
|
|
37
101
|
## TODO
|
38
102
|
|
39
|
-
* Decimal fraction in dateTime patterns
|
40
103
|
* Recurring time intervals
|
41
|
-
|
42
|
-
* Week date pattern (YYYY-Www-D)
|
43
|
-
* Treat the `201005` as `2000-10-05` instead of `2010-05`
|
104
|
+
|
44
105
|
|
45
106
|
## Contributors
|
46
107
|
|
@@ -53,4 +114,3 @@ so:
|
|
53
114
|
## License
|
54
115
|
|
55
116
|
Arnau Siches under the [MIT License](https://github.com/arnau/ISO8601/blob/master/LICENSE)
|
56
|
-
|
data/iso8601.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
$:.push File.expand_path('../lib', __FILE__)
|
4
|
-
require 'iso8601'
|
4
|
+
require 'iso8601/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = 'iso8601'
|
@@ -25,6 +25,6 @@ Gem::Specification.new do |s|
|
|
25
25
|
|
26
26
|
s.has_rdoc = 'yard'
|
27
27
|
|
28
|
-
s.add_development_dependency 'rspec', '~> 2.
|
29
|
-
s.add_development_dependency '
|
28
|
+
s.add_development_dependency 'rspec', '~> 2.14'
|
29
|
+
s.add_development_dependency 'rerun'
|
30
30
|
end
|
data/lib/iso8601.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
module ISO8601
|
4
|
-
VERSION = '0.4.1'
|
5
|
-
end
|
6
|
-
|
7
3
|
require 'time'
|
4
|
+
require 'forwardable'
|
8
5
|
|
6
|
+
require 'iso8601/version'
|
9
7
|
require 'iso8601/errors'
|
10
8
|
require 'iso8601/atoms'
|
9
|
+
require 'iso8601/date'
|
10
|
+
require 'iso8601/time'
|
11
11
|
require 'iso8601/dateTime'
|
12
12
|
require 'iso8601/duration'
|
data/lib/iso8601/atoms.rb
CHANGED
@@ -54,10 +54,10 @@ module ISO8601
|
|
54
54
|
((365 * 303 + 366 * 97) / 400) * 86400
|
55
55
|
elsif @atom == 0
|
56
56
|
year = (@base.year).to_i
|
57
|
-
(Time.utc(year) - Time.utc(@base.year))
|
57
|
+
(::Time.utc(year) - ::Time.utc(@base.year))
|
58
58
|
else
|
59
59
|
year = (@base.year + @atom).to_i
|
60
|
-
(Time.utc(year) - Time.utc(@base.year)) / @atom
|
60
|
+
(::Time.utc(year) - ::Time.utc(@base.year)) / @atom
|
61
61
|
end
|
62
62
|
end
|
63
63
|
end
|
@@ -83,11 +83,11 @@ module ISO8601
|
|
83
83
|
elsif @atom == 0
|
84
84
|
month = (@base.month <= 12) ? (@base.month) : ((@base.month) % 12)
|
85
85
|
year = @base.year + ((@base.month) / 12).to_i
|
86
|
-
(Time.utc(year, month) - Time.utc(@base.year, @base.month))
|
86
|
+
(::Time.utc(year, month) - ::Time.utc(@base.year, @base.month))
|
87
87
|
else
|
88
88
|
month = (@base.month + @atom <= 12) ? (@base.month + @atom) : ((@base.month + @atom) % 12)
|
89
89
|
year = @base.year + ((@base.month + @atom) / 12).to_i
|
90
|
-
(Time.utc(year, month) - Time.utc(@base.year, @base.month)) / @atom
|
90
|
+
(::Time.utc(year, month) - ::Time.utc(@base.year, @base.month)) / @atom
|
91
91
|
end
|
92
92
|
end
|
93
93
|
end
|
data/lib/iso8601/date.rb
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
module ISO8601
|
2
|
+
##
|
3
|
+
# A Date representation
|
4
|
+
#
|
5
|
+
# @example
|
6
|
+
# d = Date.new('2014-05-28')
|
7
|
+
# d.year # => 2014
|
8
|
+
# d.month # => 5
|
9
|
+
#
|
10
|
+
# @example Week dates
|
11
|
+
# d = Date.new('2014-W15-2')
|
12
|
+
# d.day # => 27
|
13
|
+
# d.wday # => 2
|
14
|
+
# d.week # => 15
|
15
|
+
class Date
|
16
|
+
extend Forwardable
|
17
|
+
|
18
|
+
def_delegators(:@date,
|
19
|
+
:to_s, :to_time, :to_date, :to_datetime,
|
20
|
+
:year, :month, :day, :wday)
|
21
|
+
##
|
22
|
+
# The original atoms
|
23
|
+
attr_reader :atoms
|
24
|
+
##
|
25
|
+
# The separator used in the original ISO 8601 string.
|
26
|
+
attr_reader :separator
|
27
|
+
##
|
28
|
+
# @param [String] input The date pattern
|
29
|
+
def initialize(input)
|
30
|
+
@original = input
|
31
|
+
|
32
|
+
@atoms = atomize(input)
|
33
|
+
@date = ::Date.new(*@atoms)
|
34
|
+
rescue ArgumentError => error
|
35
|
+
raise ISO8601::Errors::RangeError, input
|
36
|
+
end
|
37
|
+
##
|
38
|
+
# The calendar week number (1-53)
|
39
|
+
def week
|
40
|
+
@date.cweek
|
41
|
+
end
|
42
|
+
##
|
43
|
+
# Forwards the date the given amount of days.
|
44
|
+
#
|
45
|
+
# @param [Numeric] days The days to add
|
46
|
+
#
|
47
|
+
# @return [ISO8601::Date] New date resulting of the addition
|
48
|
+
def +(days)
|
49
|
+
ISO8601::Date.new((@date + days).iso8601)
|
50
|
+
end
|
51
|
+
##
|
52
|
+
# Backwards the date the given amount of days.
|
53
|
+
#
|
54
|
+
# @param [Numeric] days The days to remove
|
55
|
+
#
|
56
|
+
# @return [ISO8601::Date] New date resulting of the substraction
|
57
|
+
def -(days)
|
58
|
+
ISO8601::Date.new((@date - days).iso8601)
|
59
|
+
end
|
60
|
+
##
|
61
|
+
# Converts self to an array of atoms.
|
62
|
+
def to_a
|
63
|
+
[year, month, day]
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
##
|
68
|
+
# Splits the date component into valid atoms.
|
69
|
+
#
|
70
|
+
# Acceptable patterns:
|
71
|
+
#
|
72
|
+
# * YYYY
|
73
|
+
# * YYYY-MM but not YYYYMM
|
74
|
+
# * YYYY-MM-DD, YYYYMMDD
|
75
|
+
# * YYYY-Www, YYYYWdd
|
76
|
+
# * YYYY-Www-D, YYYYWddD
|
77
|
+
#
|
78
|
+
# @param [String] input
|
79
|
+
#
|
80
|
+
# @return [Array<Integer>]
|
81
|
+
def atomize(input)
|
82
|
+
_, year, separator, month, day = /^(?:
|
83
|
+
([+-]?\d{4})(-?)(\d{2})\2(\d{2}) | # YYYY-MM-DD
|
84
|
+
([+-]?\d{4})(-?)(\d{3}) | # YYYY-DDD
|
85
|
+
([+-]?\d{4})(-)(\d{2}) | # YYYY-MM
|
86
|
+
([+-]?\d{4}) # YYYY
|
87
|
+
)$/x.match(input).to_a.compact
|
88
|
+
|
89
|
+
if year.nil?
|
90
|
+
# Check if it's a Week date
|
91
|
+
_, year, separator, week, wday = /^(?:
|
92
|
+
([+-]?\d{4})(-?)(W\d{2})\2(\d) | # YYYY-Www-D
|
93
|
+
([+-]?\d{4})(-?)(W\d{2}) # YYYY-Www
|
94
|
+
)$/x.match(input).to_a.compact
|
95
|
+
|
96
|
+
unless week.nil?
|
97
|
+
d = ::Date.parse(input)
|
98
|
+
year = d.year
|
99
|
+
month = d.month
|
100
|
+
day = d.day
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
raise ISO8601::Errors::UnknownPattern.new(@original) if year.nil?
|
105
|
+
|
106
|
+
@separator = separator
|
107
|
+
|
108
|
+
return atomize_ordinal(year, month) if month && month.to_s.length == 3
|
109
|
+
|
110
|
+
[year, month, day].compact.map(&:to_i)
|
111
|
+
end
|
112
|
+
##
|
113
|
+
# Parses an ordinal date (YYYY-DDD) and returns its atoms.
|
114
|
+
#
|
115
|
+
# @param [String] year in YYYY form.
|
116
|
+
# @param [String] day in DDD form.
|
117
|
+
#
|
118
|
+
# @return [Array<Integer>] date atoms
|
119
|
+
def atomize_ordinal(year, day)
|
120
|
+
date = ::Date.parse([year, day].join('-'))
|
121
|
+
|
122
|
+
[date.year, date.month, date.day]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
data/lib/iso8601/dateTime.rb
CHANGED
@@ -4,124 +4,120 @@ module ISO8601
|
|
4
4
|
##
|
5
5
|
# A DateTime representation
|
6
6
|
#
|
7
|
-
# @
|
8
|
-
#
|
9
|
-
#
|
7
|
+
# @example
|
8
|
+
# dt = DateTime.new('2014-05-28T19:53Z')
|
9
|
+
# dt.year #=> 2014
|
10
10
|
class DateTime
|
11
|
-
|
11
|
+
extend Forwardable
|
12
|
+
|
13
|
+
def_delegators(:@date_time,
|
14
|
+
:strftime, :to_time, :to_date, :to_datetime,
|
15
|
+
:year, :month, :day, :hour, :minute, :zone)
|
16
|
+
|
17
|
+
attr_reader :second
|
18
|
+
|
12
19
|
##
|
13
20
|
# @param [String] date_time The datetime pattern
|
14
21
|
def initialize(date_time)
|
15
|
-
@
|
16
|
-
|
17
|
-
|
18
|
-
(-)?(\d{2})
|
19
|
-
)? # Month with an optional separator
|
20
|
-
(?:
|
21
|
-
(\3)?(\d{2}) # Day with an optional separator which is the same for the Month
|
22
|
-
)?
|
23
|
-
)? # Date
|
24
|
-
(?:
|
25
|
-
T(\d{2}) # Hour
|
26
|
-
(?:
|
27
|
-
(:)?(\d{2}) # Minute with an optional separator
|
28
|
-
)?
|
29
|
-
(?:
|
30
|
-
(\8)?(\d{2}) # Second with an optional separator which is the same that for the Minute
|
31
|
-
)? # Time
|
32
|
-
(
|
33
|
-
Z|([+-])
|
34
|
-
(\d{2}) # Timezone hour
|
35
|
-
(?:
|
36
|
-
(\8)? # Separator which should be the same that for the Minute
|
37
|
-
(\d{2}) # Timezone minute
|
38
|
-
)?
|
39
|
-
)? # Timezone
|
40
|
-
)?
|
41
|
-
$/x.match(date_time) or raise ISO8601::Errors::UnknownPattern.new(date_time)
|
42
|
-
|
43
|
-
@date_time = date_time
|
44
|
-
@time = @dt[7]
|
45
|
-
@date_separator = @dt[3] == @dt[5] ? @dt[3] : nil
|
46
|
-
@time_separator =
|
47
|
-
if (!@dt[8].nil? and (!@dt[10].nil? and !@dt[11].nil?) and (!@dt[15].nil? and !@dt[16].nil?)) or
|
48
|
-
(!@dt[8].nil? and (!@dt[10].nil? and !@dt[11].nil?) and @dt[16].nil?) or
|
49
|
-
(!@dt[8].nil? and @dt[11].nil? and @dt[16].nil?)
|
50
|
-
@dt[8]
|
51
|
-
else
|
52
|
-
nil
|
53
|
-
end
|
54
|
-
@century = @dt[1].to_i
|
55
|
-
@year = @dt[2].nil? ? nil : (@dt[1] + @dt[2]).to_i
|
56
|
-
@month = @dt[4].nil? ? nil : @dt[4].to_i
|
57
|
-
@day = @dt[6].nil? ? nil : @dt[6].to_i
|
58
|
-
@hour = @dt[7].nil? ? nil : @dt[7].to_i
|
59
|
-
@minute = @dt[9].nil? ? nil : @dt[9].to_i
|
60
|
-
@second = @dt[11].nil? ? nil : @dt[11].to_i
|
61
|
-
@timezone = {
|
62
|
-
:full => @dt[12].nil? ? (Time.now.gmt_offset / 3600) : (@dt[12] == "Z" ? 0 : @dt[12]),
|
63
|
-
:sign => @dt[13],
|
64
|
-
:hour => @dt[12].nil? ? (Time.now.gmt_offset / 3600) : (@dt[12] == "Z" ? 0 : @dt[14].to_i),
|
65
|
-
:minute => (@dt[12].nil? or @dt[12] == "Z") ? 0 : @dt[13].to_i
|
66
|
-
}
|
67
|
-
|
68
|
-
valid_pattern?
|
69
|
-
valid_range?
|
22
|
+
@original = date_time
|
23
|
+
@date_time = parse(date_time)
|
24
|
+
@second = @date_time.second + @date_time.second_fraction.to_f
|
70
25
|
end
|
71
26
|
##
|
72
|
-
#
|
73
|
-
|
74
|
-
|
27
|
+
# Addition
|
28
|
+
#
|
29
|
+
# @param [Numeric] seconds The seconds to add
|
30
|
+
def +(seconds)
|
31
|
+
moment = @date_time.to_time.localtime(zone) + seconds
|
32
|
+
format = moment.subsec.zero? ? "%Y-%m-%dT%H:%M:%S%:z" : "%Y-%m-%dT%H:%M:%S.%16N%:z"
|
33
|
+
|
34
|
+
ISO8601::DateTime.new(moment.strftime(format))
|
75
35
|
end
|
76
36
|
##
|
77
|
-
#
|
37
|
+
# Substraction
|
78
38
|
#
|
79
|
-
# @
|
80
|
-
def
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
date = [@year, @month, '01'].join('-')
|
86
|
-
Time.parse(date).getutc
|
87
|
-
else
|
88
|
-
Time.parse(@date_time).getutc
|
89
|
-
end
|
39
|
+
# @param [Numeric] seconds The seconds to substract
|
40
|
+
def -(seconds)
|
41
|
+
moment = @date_time.to_time.localtime(zone) - seconds.round(1)
|
42
|
+
format = moment.subsec.zero? ? "%Y-%m-%dT%H:%M:%S%:z" : "%Y-%m-%dT%H:%M:%S.%2N%:z"
|
43
|
+
|
44
|
+
ISO8601::DateTime.new(moment.strftime(format))
|
90
45
|
end
|
91
46
|
##
|
92
|
-
#
|
47
|
+
# Converts DateTime to a formated string
|
48
|
+
def to_s
|
49
|
+
format = @date_time.second_fraction.zero? ? "%Y-%m-%dT%H:%M:%S%:z" : "%Y-%m-%dT%H:%M:%S.%2N%:z"
|
50
|
+
@date_time.strftime(format)
|
51
|
+
end
|
52
|
+
##
|
53
|
+
# Converts DateTime to an array of atoms.
|
54
|
+
def to_a
|
55
|
+
[year, month, day, hour, minute, second, zone]
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
##
|
60
|
+
# Parses an ISO date time, where the date and the time components are
|
61
|
+
# optional.
|
93
62
|
#
|
94
|
-
#
|
95
|
-
|
96
|
-
|
97
|
-
|
63
|
+
# It enhances the parsing capabilities of the native DateTime.
|
64
|
+
#
|
65
|
+
# @param [String] date_time The ISO representation
|
66
|
+
def parse(date_time)
|
67
|
+
date, time = date_time.split('T')
|
68
|
+
|
69
|
+
date_components = parse_date(date)
|
70
|
+
time_components = Array(time && parse_time(time))
|
71
|
+
separators = [date_components.pop, time_components.pop]
|
72
|
+
|
73
|
+
raise ISO8601::Errors::UnknownPattern, @original unless valid_representation?(date_components, time_components)
|
74
|
+
raise ISO8601::Errors::UnknownPattern, @original unless valid_separators?(separators)
|
75
|
+
|
76
|
+
components = date_components + time_components
|
77
|
+
|
78
|
+
::DateTime.new(*components.compact)
|
98
79
|
end
|
99
80
|
##
|
100
|
-
#
|
81
|
+
# Validates the date has the right pattern.
|
82
|
+
#
|
83
|
+
# Acceptable patterns: YYYY, YYYY-MM-DD, YYYYMMDD or YYYY-MM but not YYYYMM
|
101
84
|
#
|
102
|
-
# @param [
|
103
|
-
|
104
|
-
|
105
|
-
|
85
|
+
# @param [String] input A date component
|
86
|
+
#
|
87
|
+
# @return [Array<String, nil>]
|
88
|
+
def parse_date(input)
|
89
|
+
today = ::Date.today
|
90
|
+
return [today.year, today.month, today.day, :ignore] if input.empty?
|
91
|
+
|
92
|
+
date = ISO8601::Date.new(input)
|
93
|
+
|
94
|
+
date.atoms << date.separator
|
106
95
|
end
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
end
|
123
|
-
rescue ArgumentError
|
124
|
-
raise RangeError
|
96
|
+
##
|
97
|
+
# @return [Array<String, nil>]
|
98
|
+
def parse_time(input)
|
99
|
+
time = ISO8601::Time.new(input)
|
100
|
+
|
101
|
+
time.atoms << time.separator
|
102
|
+
end
|
103
|
+
|
104
|
+
def valid_separators?(separators)
|
105
|
+
separators = separators.compact
|
106
|
+
|
107
|
+
return true if separators.length == 1 || separators[0] == :ignore
|
108
|
+
|
109
|
+
unless separators.all?(&:empty?)
|
110
|
+
return false if (separators.first.length != separators.last.length)
|
125
111
|
end
|
112
|
+
return true
|
113
|
+
end
|
114
|
+
##
|
115
|
+
# If time is provided date must use a complete representation
|
116
|
+
def valid_representation?(date, time)
|
117
|
+
year, month, day = date
|
118
|
+
hour, minute, second = time
|
119
|
+
|
120
|
+
date.nil? || !(!year.nil? && (month.nil? || day.nil?) && !hour.nil?)
|
121
|
+
end
|
126
122
|
end
|
127
123
|
end
|