iso8601 0.4.1 → 0.5.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 +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
|
[](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
|