Skip to content

Commit 17000dc

Browse files
committed
Support milliseconds in dateTime and time datatyped literals.
Fixes #394.
1 parent 903569d commit 17000dc

3 files changed

Lines changed: 24 additions & 12 deletions

File tree

lib/rdf/model/literal/datetime.rb

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ module RDF; class Literal
77
class DateTime < Literal
88
DATATYPE = RDF::URI("http://www.w3.org/2001/XMLSchema#dateTime")
99
GRAMMAR = %r(\A(-?(?:\d{4}|[1-9]\d{4,})-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?)((?:[\+\-]\d{2}:\d{2})|UTC|GMT|Z)?\Z).freeze
10-
FORMAT = '%Y-%m-%dT%H:%M:%S%:z'.freeze
10+
FORMAT = '%Y-%m-%dT%H:%M:%S.%L%:z'.freeze
1111

1212
##
1313
# @param [DateTime] value
@@ -31,9 +31,9 @@ def initialize(value, datatype: nil, lexical: nil, **options)
3131
def canonicalize!
3232
if self.valid?
3333
@string = if has_timezone?
34-
@object.new_offset.new_offset.strftime(FORMAT[0..-4] + 'Z')
34+
@object.new_offset.new_offset.strftime(FORMAT[0..-4] + 'Z').sub('.000', '')
3535
else
36-
@object.strftime(FORMAT[0..-4])
36+
@object.strftime(FORMAT[0..-4]).sub('.000', '')
3737
end
3838
end
3939
self
@@ -80,6 +80,16 @@ def valid?
8080
super && object && value !~ %r(\A0000)
8181
end
8282

83+
##
84+
# Does the literal representation include millisectonds?
85+
#
86+
# @return [Boolean]
87+
# @since 1.1.6
88+
def has_milliseconds?
89+
self.format("%L").to_i > 0
90+
end
91+
alias_method :has_ms?, :has_milliseconds?
92+
8393
##
8494
# Does the literal representation include a timezone? Note that this is only possible if initialized using a string, or `:lexical` option.
8595
#
@@ -98,7 +108,7 @@ def has_timezone?
98108
#
99109
# @return [String]
100110
def to_s
101-
@string || @object.strftime(FORMAT).sub("+00:00", 'Z')
111+
@string || @object.strftime(FORMAT).sub("+00:00", 'Z').sub('.000', '')
102112
end
103113

104114
##

lib/rdf/model/literal/time.rb

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ module RDF; class Literal
1212
class Time < Literal
1313
DATATYPE = RDF::URI("http://www.w3.org/2001/XMLSchema#time")
1414
GRAMMAR = %r(\A(\d{2}:\d{2}:\d{2}(?:\.\d+)?)((?:[\+\-]\d{2}:\d{2})|UTC|GMT|Z)?\Z).freeze
15-
FORMAT = '%H:%M:%S%:z'.freeze
15+
FORMAT = '%H:%M:%S.%L%:z'.freeze
1616

1717
##
1818
# @param [String, DateTime, #to_datetime] value
@@ -43,9 +43,9 @@ def initialize(value, datatype: nil, lexical: nil, **options)
4343
def canonicalize!
4444
if self.valid?
4545
@string = if has_timezone?
46-
@object.new_offset.new_offset.strftime(FORMAT[0..-4] + 'Z')
46+
@object.new_offset.new_offset.strftime(FORMAT[0..-4] + 'Z').sub('.000', '')
4747
else
48-
@object.strftime(FORMAT[0..-4])
48+
@object.strftime(FORMAT[0..-4]).sub('.000', '')
4949
end
5050
end
5151
self
@@ -91,7 +91,7 @@ def has_timezone?
9191
#
9292
# @return [String]
9393
def to_s
94-
@string || @object.strftime(FORMAT).sub("+00:00", 'Z')
94+
@string || @object.strftime(FORMAT).sub("+00:00", 'Z').sub('.000', '')
9595
end
9696

9797
##
@@ -122,7 +122,7 @@ def ==(other)
122122
return super unless other.valid?
123123
# Compare as strings, as time includes a date portion, and adjusting for UTC
124124
# can create a mismatch in the date portion.
125-
self.object.new_offset.strftime('%H%M%S') == other.object.new_offset.strftime('%H%M%S')
125+
self.object.new_offset.strftime('%H%M%S.%L') == other.object.new_offset.strftime('%H%M%S.%L')
126126
when Literal::DateTime, Literal::Date
127127
false
128128
else

spec/model_literal_spec.rb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -681,9 +681,9 @@ def self.literals(*selector)
681681
["2010-01-01T01:00:00+01:00", "2010-01-01T00:00:00Z", "01:00:00 AM +01:00 on Friday, 01 January 2010"],
682682
["2009-12-31T23:00:00-01:00", "2010-01-01T00:00:00Z", "11:00:00 PM -01:00 on Thursday, 31 December 2009"],
683683
["-2010-01-01T00:00:00Z", "-2010-01-01T00:00:00Z","12:00:00 AM UTC on Friday, 01 January -2010"],
684-
#["2014-09-01T12:13:14.567", "2014-09-01T12:13:14", "12:13:14 PM on Monday, 01 September 2014"],
685-
#["2014-09-01T12:13:14.567Z", "2014-09-01T12:13:14Z", "12:13:14 PM UTC on Monday, 01 September 2014"],
686-
#["2014-09-01T12:13:14.567-08:00","2014-09-01T20:13:14Z","12:13:14 PM -08:00 on Monday, 01 September 2014"],
684+
["2014-09-01T12:13:14.567", "2014-09-01T12:13:14.567", "12:13:14 PM on Monday, 01 September 2014"],
685+
["2014-09-01T12:13:14.567Z", "2014-09-01T12:13:14.567Z", "12:13:14 PM UTC on Monday, 01 September 2014"],
686+
["2014-09-01T12:13:14.567-08:00","2014-09-01T20:13:14.567Z","12:13:14 PM -08:00 on Monday, 01 September 2014"],
687687
]
688688
it_behaves_like 'RDF::Literal validation', RDF::XSD.dateTime,
689689
%w(
@@ -834,6 +834,8 @@ def self.literals(*selector)
834834
["00:00:00+00:00", "00:00:00Z", "12:00:00 AM UTC"],
835835
["01:00:00+01:00", "00:00:00Z", "01:00:00 AM +01:00"],
836836
["23:00:00-01:00", "00:00:00Z", "11:00:00 PM -01:00"],
837+
["12:13:14.567", "12:13:14.567", "12:13:14 PM"],
838+
["12:13:14.567Z", "12:13:14.567Z", "12:13:14 PM UTC"],
837839
]
838840
it_behaves_like 'RDF::Literal validation', RDF::XSD.time,
839841
%w(

0 commit comments

Comments
 (0)