Skip to content

Commit 13bc125

Browse files
committed
Finish 3.2.5
2 parents 28c9831 + 4b9f286 commit 13bc125

File tree

14 files changed

+1842
-670
lines changed

14 files changed

+1842
-670
lines changed

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
3.2.4
1+
3.2.5

etc/xsd.ttl

Lines changed: 171 additions & 171 deletions
Large diffs are not rendered by default.

lib/rdf/model/literal.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ def self.inherited(child)
7777
require 'rdf/model/literal/decimal'
7878
require 'rdf/model/literal/integer'
7979
require 'rdf/model/literal/double'
80+
require 'rdf/model/literal/temporal'
8081
require 'rdf/model/literal/date'
8182
require 'rdf/model/literal/datetime'
8283
require 'rdf/model/literal/time'
@@ -295,7 +296,7 @@ def ==(other)
295296
when self.simple? && other.simple?
296297
self.value_hash == other.value_hash && self.value == other.value
297298
when other.comperable_datatype?(self) || self.comperable_datatype?(other)
298-
# Comoparing plain with undefined datatypes does not generate an error, but returns false
299+
# Comparing plain with undefined datatypes does not generate an error, but returns false
299300
# From data-r2/expr-equal/eq-2-2.
300301
false
301302
else

lib/rdf/model/literal/date.rb

Lines changed: 27 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -4,67 +4,41 @@ module RDF; class Literal
44
#
55
# @see http://www.w3.org/TR/xmlschema11-2/#date
66
# @since 0.2.1
7-
class Date < Literal
7+
class Date < Temporal
88
DATATYPE = RDF::URI("http://www.w3.org/2001/XMLSchema#date")
99
GRAMMAR = %r(\A(-?\d{4}-\d{2}-\d{2})((?:[\+\-]\d{2}:\d{2})|UTC|GMT|Z)?\Z).freeze
1010
FORMAT = '%Y-%m-%d'.freeze
1111

1212
##
13-
# @param [String, Date, #to_date] value
13+
# Internally, a `Date` is represented using a native `::DateTime` object at midnight. If initialized from a `::Date`, there is no timezone component, If initialized from a `::DateTime`, the timezone is taken from that native object, otherwise, a timezone (or no timezone) is taken from the string representation having a matching `zzzzzz` component.
14+
#
15+
# @note If initialized using the `#to_datetime` method, time component is unchanged. Otherewise, it is set to 00:00 (midnight).
16+
#
17+
# @param [String, Date, #to_datetime] value
1418
# @param (see Literal#initialize)
1519
def initialize(value, datatype: nil, lexical: nil, **options)
1620
@datatype = RDF::URI(datatype || self.class.const_get(:DATATYPE))
1721
@string = lexical || (value if value.is_a?(String))
1822
@object = case
19-
when value.is_a?(::Date) then value
20-
when value.respond_to?(:to_date) then value.to_date
21-
else ::Date.parse(value.to_s)
22-
end rescue ::Date.new
23-
end
24-
25-
##
26-
# Converts this literal into its canonical lexical representation.
27-
#
28-
# Note that the timezone is recoverable for xsd:date, where it is not for xsd:dateTime and xsd:time, which are both transformed relative to Z, if a timezone is provided.
29-
#
30-
# @return [RDF::Literal] `self`
31-
# @see http://www.w3.org/TR/xmlschema11-2/#date
32-
def canonicalize!
33-
@string = @object.strftime(FORMAT) + self.tz.to_s if self.valid?
34-
self
35-
end
36-
37-
##
38-
# Returns `true` if the value adheres to the defined grammar of the
39-
# datatype.
40-
#
41-
# Special case for date and dateTime, for which '0000' is not a valid year
42-
#
43-
# @return [Boolean]
44-
# @since 0.2.1
45-
def valid?
46-
super && object && value !~ %r(\A0000)
47-
end
48-
49-
##
50-
# Does the literal representation include a timezone? Note that this is only possible if initialized using a string, or `:lexical` option.
51-
#
52-
# @return [Boolean]
53-
# @since 1.1.6
54-
def timezone?
55-
md = self.to_s.match(GRAMMAR)
56-
md && !!md[2]
57-
end
58-
alias_method :tz?, :timezone?
59-
alias_method :has_tz?, :timezone?
60-
alias_method :has_timezone?, :timezone?
61-
62-
##
63-
# Returns the value as a string.
64-
#
65-
# @return [String]
66-
def to_s
67-
@string || @object.strftime(FORMAT)
23+
when value.class == ::Date
24+
@zone = nil
25+
# Use midnight as midpoint of the interval
26+
::DateTime.parse(value.strftime('%FT00:00:00'))
27+
when value.respond_to?(:to_datetime)
28+
dt = value.to_datetime
29+
@zone = dt.zone
30+
dt
31+
else
32+
md = value.to_s.match(GRAMMAR)
33+
_, dt, tz = Array(md)
34+
if tz
35+
@zone = tz == 'Z' ? '+00:00' : tz
36+
else
37+
@zone = nil # No timezone
38+
end
39+
# Use midnight as midpoint of the interval
40+
::DateTime.parse("#{dt}T00:00:00#{@zone}")
41+
end rescue ::DateTime.new
6842
end
6943

7044
##
@@ -75,42 +49,13 @@ def to_s
7549
def humanize(lang = :en)
7650
d = object.strftime("%A, %d %B %Y")
7751
if timezone?
78-
d += if self.tz == 'Z'
52+
d += if @zone == '+00:00'
7953
" UTC"
8054
else
81-
" #{self.tz}"
55+
" #{@zone}"
8256
end
8357
end
8458
d
8559
end
86-
87-
##
88-
# Returns the timezone part of arg as a simple literal. Returns the empty string if there is no timezone.
89-
#
90-
# @return [RDF::Literal]
91-
# @since 1.1.6
92-
def tz
93-
md = self.to_s.match(GRAMMAR)
94-
zone = md[2].to_s
95-
zone = "Z" if zone == "+00:00"
96-
RDF::Literal(zone)
97-
end
98-
99-
##
100-
# Equal compares as Date objects
101-
def ==(other)
102-
# If lexically invalid, use regular literal testing
103-
return super unless self.valid?
104-
105-
case other
106-
when Literal::Date
107-
return super unless other.valid?
108-
self.object == other.object
109-
when Literal::Time, Literal::DateTime
110-
false
111-
else
112-
super
113-
end
114-
end
11560
end # Date
11661
end; end # RDF::Literal

lib/rdf/model/literal/datetime.rb

Lines changed: 22 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -2,117 +2,38 @@ module RDF; class Literal
22
##
33
# A date/time literal.
44
#
5-
# @see http://www.w3.org/TR/xmlschema11-2/#dateTime#boolean
5+
# @see http://www.w3.org/TR/xmlschema11-2/#dateTime
66
# @since 0.2.1
7-
class DateTime < Literal
7+
class DateTime < Temporal
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.%L%:z'.freeze
10+
FORMAT = '%Y-%m-%dT%H:%M:%S.%L'.freeze
1111

1212
##
13+
# Internally, a `DateTime` is represented using a native `::DateTime`. If initialized from a `::Date`, there is no timezone component, If initialized from a `::DateTime`, the timezone is taken from that native object, otherwise, a timezone (or no timezone) is taken from the string representation having a matching `zzzzzz` component.
14+
#
1315
# @param [DateTime] value
1416
# @option options [String] :lexical (nil)
1517
def initialize(value, datatype: nil, lexical: nil, **options)
1618
@datatype = RDF::URI(datatype || self.class.const_get(:DATATYPE))
1719
@string = lexical || (value if value.is_a?(String))
1820
@object = case
19-
when value.is_a?(::DateTime) then value
20-
when value.respond_to?(:to_datetime) then value.to_datetime
21-
else ::DateTime.parse(value.to_s)
22-
end rescue ::DateTime.new
23-
end
24-
25-
##
26-
# Converts this literal into its canonical lexical representation.
27-
# with date and time normalized to UTC.
28-
#
29-
# @return [RDF::Literal] `self`
30-
# @see http://www.w3.org/TR/xmlschema11-2/#dateTime
31-
def canonicalize!
32-
if self.valid?
33-
@string = if timezone?
34-
@object.new_offset.new_offset.strftime(FORMAT[0..-4] + 'Z').sub('.000', '')
21+
when value.is_a?(::DateTime)
22+
@zone = value.zone
23+
value
24+
when value.respond_to?(:to_datetime)
25+
@zone = value.to_datetime.zone
26+
value.to_datetime
3527
else
36-
@object.strftime(FORMAT[0..-4]).sub('.000', '')
37-
end
38-
end
39-
self
40-
end
41-
42-
##
43-
# Returns the timezone part of arg as a simple literal. Returns the empty string if there is no timezone.
44-
#
45-
# @return [RDF::Literal]
46-
# @see http://www.w3.org/TR/sparql11-query/#func-tz
47-
def tz
48-
zone = timezone? ? object.zone : ""
49-
zone = "Z" if zone == "+00:00"
50-
RDF::Literal(zone)
51-
end
52-
53-
##
54-
# Returns the timezone part of arg as an xsd:dayTimeDuration, or `nil`
55-
# if lexical form of literal does not include a timezone.
56-
#
57-
# @return [RDF::Literal]
58-
def timezone
59-
if tz == 'Z'
60-
RDF::Literal("PT0S", datatype: RDF::URI("http://www.w3.org/2001/XMLSchema#dayTimeDuration"))
61-
elsif md = tz.to_s.match(/^([+-])?(\d+):(\d+)?$/)
62-
plus_minus, hour, min = md[1,3]
63-
plus_minus = nil unless plus_minus == "-"
64-
hour = hour.to_i
65-
min = min.to_i
66-
res = "#{plus_minus}PT#{hour}H#{"#{min}M" if min > 0}"
67-
RDF::Literal(res, datatype: RDF::URI("http://www.w3.org/2001/XMLSchema#dayTimeDuration"))
68-
end
69-
end
70-
71-
##
72-
# Returns `true` if the value adheres to the defined grammar of the
73-
# datatype.
74-
#
75-
# Special case for date and dateTime, for which '0000' is not a valid year
76-
#
77-
# @return [Boolean]
78-
# @since 0.2.1
79-
def valid?
80-
super && object && value !~ %r(\A0000)
81-
end
82-
83-
##
84-
# Does the literal representation include millisectonds?
85-
#
86-
# @return [Boolean]
87-
# @since 1.1.6
88-
def milliseconds?
89-
self.format("%L").to_i > 0
90-
end
91-
alias_method :has_milliseconds?, :milliseconds?
92-
alias_method :has_ms?, :milliseconds?
93-
alias_method :ms?, :milliseconds?
94-
95-
##
96-
# Does the literal representation include a timezone? Note that this is only possible if initialized using a string, or `:lexical` option.
97-
#
98-
# @return [Boolean]
99-
# @since 1.1.6
100-
def timezone?
101-
md = self.to_s.match(GRAMMAR)
102-
md && !!md[2]
103-
end
104-
alias_method :tz?, :timezone?
105-
alias_method :has_tz?, :timezone?
106-
alias_method :has_timezone?, :timezone?
107-
108-
##
109-
# Returns the `timezone` of the literal. If the
110-
##
111-
# Returns the value as a string.
112-
#
113-
# @return [String]
114-
def to_s
115-
@string || @object.strftime(FORMAT).sub("+00:00", 'Z').sub('.000', '')
28+
md = value.to_s.match(GRAMMAR)
29+
_, _, tz = Array(md)
30+
if tz
31+
@zone = tz == 'Z' ? '+00:00' : tz
32+
else
33+
@zone = nil # No timezone
34+
end
35+
::DateTime.parse(value.to_s)
36+
end rescue ::DateTime.new
11637
end
11738

11839
##
@@ -123,31 +44,10 @@ def to_s
12344
def humanize(lang = :en)
12445
d = object.strftime("%r on %A, %d %B %Y")
12546
if timezone?
126-
zone = if self.tz == 'Z'
127-
"UTC"
128-
else
129-
self.tz
130-
end
131-
d.sub!(" on ", " #{zone} on ")
47+
z = @zone == '+00:00' ? "UTC" : @zone
48+
d.sub!(" on ", " #{z} on ")
13249
end
13350
d
13451
end
135-
136-
##
137-
# Equal compares as DateTime objects
138-
def ==(other)
139-
# If lexically invalid, use regular literal testing
140-
return super unless self.valid?
141-
142-
case other
143-
when Literal::DateTime
144-
return super unless other.valid?
145-
self.object == other.object
146-
when Literal::Time, Literal::Date
147-
false
148-
else
149-
super
150-
end
151-
end
15252
end # DateTime
15353
end; end # RDF::Literal

lib/rdf/model/literal/decimal.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,10 @@ def canonicalize!
5454
##
5555
# Returns the absolute value of `self`.
5656
#
57+
# From the XQuery function [fn:abs](https://www.w3.org/TR/xpath-functions/#func-abs).
58+
#
5759
# @return [RDF::Literal]
60+
# @see https://www.w3.org/TR/xpath-functions/#func-abs
5861
# @since 0.2.3
5962
def abs
6063
(d = to_d) && d > 0 ? self : RDF::Literal(d.abs)
@@ -63,7 +66,10 @@ def abs
6366
##
6467
# Returns the number with no fractional part that is closest to the argument. If there are two such numbers, then the one that is closest to positive infinity is returned. An error is raised if arg is not a numeric value.
6568
#
69+
# From the XQuery function [fn:round](https://www.w3.org/TR/xpath-functions/#func-round).
70+
#
6671
# @return [RDF::Literal::Decimal]
72+
# @see https://www.w3.org/TR/xpath-functions/#func-round
6773
def round
6874
rounded = to_d.round(half: (to_d < 0 ? :down : :up))
6975
if rounded == -0.0
@@ -77,21 +83,27 @@ def round
7783
##
7884
# Returns the smallest integer greater than or equal to `self`.
7985
#
86+
# From the XQuery function [fn:ceil](https://www.w3.org/TR/xpath-functions/#func-ceil).
87+
#
8088
# @example
8189
# RDF::Literal(1).ceil #=> RDF::Literal(1)
8290
#
8391
# @return [RDF::Literal::Integer]
92+
# @see https://www.w3.org/TR/xpath-functions/#func-ceil
8493
def ceil
8594
RDF::Literal::Integer.new(to_d.ceil)
8695
end
8796

8897
##
8998
# Returns the largest integer less than or equal to `self`.
9099
#
100+
# From the XQuery function [fn:floor](https://www.w3.org/TR/xpath-functions/#func-floor).
101+
#
91102
# @example
92103
# RDF::Literal(1).floor #=> RDF::Literal(1)
93104
#
94105
# @return [RDF::Literal::Integer]
106+
# @see https://www.w3.org/TR/xpath-functions/#func-floor
95107
def floor
96108
RDF::Literal::Integer.new(to_d.floor)
97109
end

0 commit comments

Comments
 (0)