rdf 3.2.4 → 3.2.5
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/VERSION +1 -1
- data/lib/rdf/model/literal/date.rb +27 -82
- data/lib/rdf/model/literal/datetime.rb +22 -122
- data/lib/rdf/model/literal/decimal.rb +12 -0
- data/lib/rdf/model/literal/double.rb +20 -0
- data/lib/rdf/model/literal/integer.rb +6 -0
- data/lib/rdf/model/literal/numeric.rb +154 -4
- data/lib/rdf/model/literal/temporal.rb +310 -0
- data/lib/rdf/model/literal/time.rb +26 -98
- data/lib/rdf/model/literal.rb +2 -1
- data/lib/rdf/vocab/xsd.rb +98 -98
- metadata +10 -3
| @@ -67,10 +67,9 @@ module RDF; class Literal | |
| 67 67 | 
             
                ##
         | 
| 68 68 | 
             
                # Returns the sum of `self` plus `other`.
         | 
| 69 69 | 
             
                #
         | 
| 70 | 
            -
                #  | 
| 71 | 
            -
                # | 
| 72 | 
            -
                # If both operands are  | 
| 73 | 
            -
                # and the other is -INF, NaN is returned.
         | 
| 70 | 
            +
                # From the XQuery function [op:numeric-add](https://www.w3.org/TR/xpath-functions/#func-numeric-add).
         | 
| 71 | 
            +
                #
         | 
| 72 | 
            +
                # @note For `xs:float` or `xs:double` values, if one of the operands is a zero or a finite number and the other is `INF` or `-INF`, `INF` or `-INF` is returned. If both operands are `INF`, `INF` is returned. If both operands are `-INF`, `-INF` is returned. If one of the operands is `INF` and the other is `-INF`, `NaN` is returned.
         | 
| 74 73 | 
             
                # @param  [Literal::Numeric, #to_i, #to_f, #to_d] other
         | 
| 75 74 | 
             
                # @return [RDF::Literal::Numeric]
         | 
| 76 75 | 
             
                # @since  0.2.3
         | 
| @@ -90,6 +89,8 @@ module RDF; class Literal | |
| 90 89 | 
             
                ##
         | 
| 91 90 | 
             
                # Returns the difference of `self` minus `other`.
         | 
| 92 91 | 
             
                #
         | 
| 92 | 
            +
                # From the XQuery function [op:numeric-subtract](https://www.w3.org/TR/xpath-functions/#func-numeric-subtract).
         | 
| 93 | 
            +
                #
         | 
| 93 94 | 
             
                # @param  [Literal::Numeric, #to_i, #to_f, #to_d] other
         | 
| 94 95 | 
             
                # @return [RDF::Literal::Numeric]
         | 
| 95 96 | 
             
                # @since  0.2.3
         | 
| @@ -109,6 +110,8 @@ module RDF; class Literal | |
| 109 110 | 
             
                ##
         | 
| 110 111 | 
             
                # Returns the product of `self` times `other`.
         | 
| 111 112 | 
             
                #
         | 
| 113 | 
            +
                # From the XQuery function [op:numeric-multiply](https://www.w3.org/TR/xpath-functions/#func-numeric-multiply).
         | 
| 114 | 
            +
                #
         | 
| 112 115 | 
             
                # @param  [Literal::Numeric, #to_i, #to_f, #to_d] other
         | 
| 113 116 | 
             
                # @return [RDF::Literal::Numeric]
         | 
| 114 117 | 
             
                # @since  0.2.3
         | 
| @@ -130,6 +133,8 @@ module RDF; class Literal | |
| 130 133 | 
             
                #
         | 
| 131 134 | 
             
                # Promotes values, as necessary, with the result type depending on the input values.
         | 
| 132 135 | 
             
                #
         | 
| 136 | 
            +
                # From the XQuery function [math:pow](https://www.w3.org/TR/xpath-functions/#func-numeric-pow).
         | 
| 137 | 
            +
                #
         | 
| 133 138 | 
             
                # @param  [Literal::Numeric, #to_i, #to_f, #to_d] other
         | 
| 134 139 | 
             
                # @return [RDF::Literal::Numeric]
         | 
| 135 140 | 
             
                # @since  0.2.3
         | 
| @@ -143,6 +148,8 @@ module RDF; class Literal | |
| 143 148 | 
             
                ##
         | 
| 144 149 | 
             
                # Exponent − Performs remainder of `self` divided by `other`.
         | 
| 145 150 | 
             
                #
         | 
| 151 | 
            +
                # From the XQuery function [math:mod](https://www.w3.org/TR/xpath-functions/#func-numeric-mod).
         | 
| 152 | 
            +
                #
         | 
| 146 153 | 
             
                # @param  [Literal::Numeric, #to_i, #to_f, #to_d] other
         | 
| 147 154 | 
             
                # @return [RDF::Literal]
         | 
| 148 155 | 
             
                # @since  0.2.3
         | 
| @@ -165,6 +172,8 @@ module RDF; class Literal | |
| 165 172 | 
             
                # As a special case, if the types of both $arg1 and $arg2 are xsd:integer,
         | 
| 166 173 | 
             
                # then the return type is xsd:decimal.
         | 
| 167 174 | 
             
                #
         | 
| 175 | 
            +
                # From the XQuery function [op:numeric-divide](https://www.w3.org/TR/xpath-functions/#func-numeric-divide).
         | 
| 176 | 
            +
                #
         | 
| 168 177 | 
             
                # @param  [Literal::Numeric, #to_i, #to_f, #to_d] other
         | 
| 169 178 | 
             
                # @return [RDF::Literal::Numeric]
         | 
| 170 179 | 
             
                # @raise  [ZeroDivisionError] if divided by zero
         | 
| @@ -183,8 +192,11 @@ module RDF; class Literal | |
| 183 192 | 
             
                ##
         | 
| 184 193 | 
             
                # Returns the absolute value of `self`.
         | 
| 185 194 | 
             
                #
         | 
| 195 | 
            +
                # From the XQuery function [fn:abs](https://www.w3.org/TR/xpath-functions/#func-abs).
         | 
| 196 | 
            +
                #
         | 
| 186 197 | 
             
                # @return [RDF::Literal]
         | 
| 187 198 | 
             
                # @raise  [NotImplementedError] unless implemented in subclass
         | 
| 199 | 
            +
                # @see https://www.w3.org/TR/xpath-functions/#func-abs
         | 
| 188 200 | 
             
                def abs
         | 
| 189 201 | 
             
                  raise NotImplementedError
         | 
| 190 202 | 
             
                end
         | 
| @@ -192,8 +204,11 @@ module RDF; class Literal | |
| 192 204 | 
             
                ##
         | 
| 193 205 | 
             
                # 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.
         | 
| 194 206 | 
             
                #
         | 
| 207 | 
            +
                # From the XQuery function [fn:round](https://www.w3.org/TR/xpath-functions/#func-round).
         | 
| 208 | 
            +
                #
         | 
| 195 209 | 
             
                # @return [RDF::Literal]
         | 
| 196 210 | 
             
                # @raise  [NotImplementedError] unless implemented in subclass
         | 
| 211 | 
            +
                # @see https://www.w3.org/TR/xpath-functions/#func-round
         | 
| 197 212 | 
             
                def round
         | 
| 198 213 | 
             
                  raise NotImplementedError
         | 
| 199 214 | 
             
                end
         | 
| @@ -201,10 +216,13 @@ module RDF; class Literal | |
| 201 216 | 
             
                ##
         | 
| 202 217 | 
             
                # Returns the smallest integer greater than or equal to `self`.
         | 
| 203 218 | 
             
                #
         | 
| 219 | 
            +
                # From the XQuery function [fn:ceil](https://www.w3.org/TR/xpath-functions/#func-ceil).
         | 
| 220 | 
            +
                #
         | 
| 204 221 | 
             
                # @example
         | 
| 205 222 | 
             
                #   RDF::Literal(1).ceil            #=> RDF::Literal(1)
         | 
| 206 223 | 
             
                #
         | 
| 207 224 | 
             
                # @return [RDF::Literal]
         | 
| 225 | 
            +
                # @see https://www.w3.org/TR/xpath-functions/#func-ceil
         | 
| 208 226 | 
             
                def ceil
         | 
| 209 227 | 
             
                  self
         | 
| 210 228 | 
             
                end
         | 
| @@ -212,14 +230,146 @@ module RDF; class Literal | |
| 212 230 | 
             
                ##
         | 
| 213 231 | 
             
                # Returns the largest integer less than or equal to `self`.
         | 
| 214 232 | 
             
                #
         | 
| 233 | 
            +
                # From the XQuery function [fn:floor](https://www.w3.org/TR/xpath-functions/#func-floor).
         | 
| 234 | 
            +
                #
         | 
| 215 235 | 
             
                # @example
         | 
| 216 236 | 
             
                #   RDF::Literal(1).floor            #=> RDF::Literal(1)
         | 
| 217 237 | 
             
                #
         | 
| 218 238 | 
             
                # @return [RDF::Literal]
         | 
| 239 | 
            +
                # @see https://www.w3.org/TR/xpath-functions/#func-floor
         | 
| 219 240 | 
             
                def floor
         | 
| 220 241 | 
             
                  self
         | 
| 221 242 | 
             
                end
         | 
| 222 243 |  | 
| 244 | 
            +
                ##
         | 
| 245 | 
            +
                # Returns the value of `e`<sup>`x`</sup>.
         | 
| 246 | 
            +
                #
         | 
| 247 | 
            +
                # @return [Double]
         | 
| 248 | 
            +
                # @see https://www.w3.org/TR/xpath-functions/#func-math-exp
         | 
| 249 | 
            +
                def exp
         | 
| 250 | 
            +
                  Double.new(Math.exp(self.to_f))
         | 
| 251 | 
            +
                end
         | 
| 252 | 
            +
             | 
| 253 | 
            +
                ##
         | 
| 254 | 
            +
                # Returns the value of `10`<sup>`x`</sup>.
         | 
| 255 | 
            +
                #
         | 
| 256 | 
            +
                # @return [Double]
         | 
| 257 | 
            +
                # @see https://www.w3.org/TR/xpath-functions/#func-math-exp10
         | 
| 258 | 
            +
                def exp10
         | 
| 259 | 
            +
                  Double.new(10**self.to_f)
         | 
| 260 | 
            +
                end
         | 
| 261 | 
            +
             | 
| 262 | 
            +
                ##
         | 
| 263 | 
            +
                # Returns the natural logarithm of the argument.
         | 
| 264 | 
            +
                #
         | 
| 265 | 
            +
                # @return [Double]
         | 
| 266 | 
            +
                # @see https://www.w3.org/TR/xpath-functions/#func-math-log
         | 
| 267 | 
            +
                def log
         | 
| 268 | 
            +
                  Double.new(Math.log(self.to_f))
         | 
| 269 | 
            +
                rescue Math::DomainError
         | 
| 270 | 
            +
                  Double.new(::Float::NAN)
         | 
| 271 | 
            +
                end
         | 
| 272 | 
            +
             | 
| 273 | 
            +
                ##
         | 
| 274 | 
            +
                # Returns the base-ten logarithm of the argument.
         | 
| 275 | 
            +
                #
         | 
| 276 | 
            +
                # @return [Double]
         | 
| 277 | 
            +
                # @see https://www.w3.org/TR/xpath-functions/#func-math-log10
         | 
| 278 | 
            +
                def log10
         | 
| 279 | 
            +
                  Double.new(Math.log10(self.to_f))
         | 
| 280 | 
            +
                rescue Math::DomainError
         | 
| 281 | 
            +
                  Double.new(::Float::NAN)
         | 
| 282 | 
            +
                end
         | 
| 283 | 
            +
             | 
| 284 | 
            +
                ##
         | 
| 285 | 
            +
                # Returns the non-negative square root of the argument.
         | 
| 286 | 
            +
                #
         | 
| 287 | 
            +
                # @return [Double]
         | 
| 288 | 
            +
                # @see https://www.w3.org/TR/xpath-functions/#func-math-sqrt
         | 
| 289 | 
            +
                def sqrt
         | 
| 290 | 
            +
                  Double.new(Math.sqrt(self.to_f))
         | 
| 291 | 
            +
                rescue Math::DomainError
         | 
| 292 | 
            +
                  Double.new(::Float::NAN)
         | 
| 293 | 
            +
                end
         | 
| 294 | 
            +
             | 
| 295 | 
            +
                ##
         | 
| 296 | 
            +
                # Returns the sine of the argument. The argument is an angle in radians.
         | 
| 297 | 
            +
                #
         | 
| 298 | 
            +
                # @return [Double]
         | 
| 299 | 
            +
                # @see https://www.w3.org/TR/xpath-functions/#func-math-sin
         | 
| 300 | 
            +
                def sin
         | 
| 301 | 
            +
                  Double.new(Math.sin(self.to_f))
         | 
| 302 | 
            +
                rescue Math::DomainError
         | 
| 303 | 
            +
                  Double.new(::Float::NAN)
         | 
| 304 | 
            +
                end
         | 
| 305 | 
            +
             | 
| 306 | 
            +
                ##
         | 
| 307 | 
            +
                # Returns the cosine of the argument. The argument is an angle in radians.
         | 
| 308 | 
            +
                #
         | 
| 309 | 
            +
                # @return [Double]
         | 
| 310 | 
            +
                # @see https://www.w3.org/TR/xpath-functions/#func-math-cos
         | 
| 311 | 
            +
                def cos
         | 
| 312 | 
            +
                  Double.new(Math.cos(self.to_f))
         | 
| 313 | 
            +
                rescue Math::DomainError
         | 
| 314 | 
            +
                  Double.new(::Float::NAN)
         | 
| 315 | 
            +
                end
         | 
| 316 | 
            +
             | 
| 317 | 
            +
                ##
         | 
| 318 | 
            +
                # Returns the tangent of the argument. The argument is an angle in radians.
         | 
| 319 | 
            +
                #
         | 
| 320 | 
            +
                # @return [Double]
         | 
| 321 | 
            +
                # @see https://www.w3.org/TR/xpath-functions/#func-math-tan
         | 
| 322 | 
            +
                def tan
         | 
| 323 | 
            +
                  Double.new(Math.tan(self.to_f))
         | 
| 324 | 
            +
                rescue Math::DomainError
         | 
| 325 | 
            +
                  Double.new(::Float::NAN)
         | 
| 326 | 
            +
                end
         | 
| 327 | 
            +
             | 
| 328 | 
            +
                ##
         | 
| 329 | 
            +
                # Returns the arc sine of the argument.
         | 
| 330 | 
            +
                #
         | 
| 331 | 
            +
                # @return [Double]
         | 
| 332 | 
            +
                # @see https://www.w3.org/TR/xpath-functions/#func-math-asin
         | 
| 333 | 
            +
                def asin
         | 
| 334 | 
            +
                  Double.new(Math.asin(self.to_f))
         | 
| 335 | 
            +
                rescue Math::DomainError
         | 
| 336 | 
            +
                  Double.new(::Float::NAN)
         | 
| 337 | 
            +
                end
         | 
| 338 | 
            +
             | 
| 339 | 
            +
                ##
         | 
| 340 | 
            +
                # Returns the arc cosine of the argument.
         | 
| 341 | 
            +
                #
         | 
| 342 | 
            +
                # @return [Double]
         | 
| 343 | 
            +
                # @see https://www.w3.org/TR/xpath-functions/#func-math-acos
         | 
| 344 | 
            +
                def acos
         | 
| 345 | 
            +
                  Double.new(Math.acos(self.to_f))
         | 
| 346 | 
            +
                rescue Math::DomainError
         | 
| 347 | 
            +
                  Double.new(::Float::NAN)
         | 
| 348 | 
            +
                end
         | 
| 349 | 
            +
             | 
| 350 | 
            +
                ##
         | 
| 351 | 
            +
                # Returns the arc tangent of the argument.
         | 
| 352 | 
            +
                #
         | 
| 353 | 
            +
                # @return [Double]
         | 
| 354 | 
            +
                # @see https://www.w3.org/TR/xpath-functions/#func-math-atan
         | 
| 355 | 
            +
                def atan
         | 
| 356 | 
            +
                  Double.new(Math.atan(self.to_f))
         | 
| 357 | 
            +
                rescue Math::DomainError
         | 
| 358 | 
            +
                  Double.new(::Float::NAN)
         | 
| 359 | 
            +
                end
         | 
| 360 | 
            +
             | 
| 361 | 
            +
                ##
         | 
| 362 | 
            +
                # Returns the angle in radians subtended at the origin by the point on a plane with coordinates (x, y) and the positive x-axis.
         | 
| 363 | 
            +
                #
         | 
| 364 | 
            +
                # @param [#to_f] arg
         | 
| 365 | 
            +
                # @return [Double]
         | 
| 366 | 
            +
                # @see https://www.w3.org/TR/xpath-functions/#func-math-atan2
         | 
| 367 | 
            +
                def atan2(arg)
         | 
| 368 | 
            +
                  Double.new(Math.atan2(self.to_f, arg.to_f))
         | 
| 369 | 
            +
                rescue Math::DomainError
         | 
| 370 | 
            +
                  Double.new(::Float::NAN)
         | 
| 371 | 
            +
                end
         | 
| 372 | 
            +
             | 
| 223 373 | 
             
                ##
         | 
| 224 374 | 
             
                # Returns the value as an integer.
         | 
| 225 375 | 
             
                #
         | 
| @@ -0,0 +1,310 @@ | |
| 1 | 
            +
            module RDF; class Literal
         | 
| 2 | 
            +
              ##
         | 
| 3 | 
            +
              # Shared methods and class ancestry for date, time, and dateTime literal classes.
         | 
| 4 | 
            +
              #
         | 
| 5 | 
            +
              # @since 3.1
         | 
| 6 | 
            +
              class Temporal < Literal
         | 
| 7 | 
            +
                # Matches either -10:00 or -P1H0M forms
         | 
| 8 | 
            +
                ZONE_GRAMMAR  = %r(\A
         | 
| 9 | 
            +
                   (?:(?<si>[+-])(?<hr>\d{2}):(?:(?<mi>\d{2}))?)
         | 
| 10 | 
            +
                  |(?:(?<si>-)?PT(?<hr>\d{1,2})H(?:(?<mi>\d{1,2})M)?)
         | 
| 11 | 
            +
                \z)x.freeze
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                ##
         | 
| 14 | 
            +
                # Compares this literal to `other` for sorting purposes.
         | 
| 15 | 
            +
                #
         | 
| 16 | 
            +
                # @param  [Object] other
         | 
| 17 | 
            +
                # @return [Integer] `-1`, `0`, or `1`
         | 
| 18 | 
            +
                def <=>(other)
         | 
| 19 | 
            +
                  # If lexically invalid, use regular literal testing
         | 
| 20 | 
            +
                  return super unless self.valid? && (!other.respond_to?(:valid?) || other.valid?)
         | 
| 21 | 
            +
                  return super unless other.is_a?(self.class)
         | 
| 22 | 
            +
                  @object <=> other.object
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                ##
         | 
| 26 | 
            +
                # Returns `true` if this literal is equal to `other`.
         | 
| 27 | 
            +
                #
         | 
| 28 | 
            +
                # @param  [Object] other
         | 
| 29 | 
            +
                # @return [Boolean] `true` or `false`
         | 
| 30 | 
            +
                # @since  0.3.0
         | 
| 31 | 
            +
                def ==(other)
         | 
| 32 | 
            +
                  # If lexically invalid, use regular literal testing
         | 
| 33 | 
            +
                  return super unless self.valid? && (!other.respond_to?(:valid?) || other.valid?)
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  case other
         | 
| 36 | 
            +
                  when self.class
         | 
| 37 | 
            +
                    self.object == other.object
         | 
| 38 | 
            +
                  when Literal::Temporal
         | 
| 39 | 
            +
                    false
         | 
| 40 | 
            +
                  else
         | 
| 41 | 
            +
                    super
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                ##
         | 
| 46 | 
            +
                # Converts this literal into its canonical lexical representation.
         | 
| 47 | 
            +
                # with date and time normalized to UTC.
         | 
| 48 | 
            +
                #
         | 
| 49 | 
            +
                # @return [RDF::Literal] `self`
         | 
| 50 | 
            +
                # @see    http://www.w3.org/TR/xmlschema11-2/#dateTime
         | 
| 51 | 
            +
                def canonicalize!
         | 
| 52 | 
            +
                  if self.valid? && @zone && @zone != '+00:00'
         | 
| 53 | 
            +
                    adjust_to_timezone!
         | 
| 54 | 
            +
                  else
         | 
| 55 | 
            +
                    @string = nil
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
                  self
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                ##
         | 
| 61 | 
            +
                # Returns the timezone part of arg as a simple literal. Returns the empty string if there is no timezone.
         | 
| 62 | 
            +
                #
         | 
| 63 | 
            +
                # @return [RDF::Literal]
         | 
| 64 | 
            +
                def tz
         | 
| 65 | 
            +
                  RDF::Literal(@zone == "+00:00" ? 'Z' : @zone)
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                ##
         | 
| 69 | 
            +
                # Does the literal representation include a timezone? Note that this is only possible if initialized using a string, or `:lexical` option.
         | 
| 70 | 
            +
                #
         | 
| 71 | 
            +
                # @return [Boolean]
         | 
| 72 | 
            +
                # @since 1.1.6
         | 
| 73 | 
            +
                def timezone?
         | 
| 74 | 
            +
                  # Can only know there's a timezone from the string represntation
         | 
| 75 | 
            +
                  md = to_s.match(self.class.const_get(:GRAMMAR))
         | 
| 76 | 
            +
                  md && !!md[2]
         | 
| 77 | 
            +
                end
         | 
| 78 | 
            +
                alias_method :tz?, :timezone?
         | 
| 79 | 
            +
                alias_method :has_tz?, :timezone?
         | 
| 80 | 
            +
                alias_method :has_timezone?, :timezone?
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                ##
         | 
| 83 | 
            +
                # Returns the timezone part of arg as an xsd:dayTimeDuration, or `nil`
         | 
| 84 | 
            +
                # if lexical form of literal does not include a timezone.
         | 
| 85 | 
            +
                #
         | 
| 86 | 
            +
                # From [fn:timezone-from-date](https://www.w3.org/TR/xpath-functions/#func-timezone-from-date).
         | 
| 87 | 
            +
                #
         | 
| 88 | 
            +
                # @return [RDF::Literal]
         | 
| 89 | 
            +
                # @see https://www.w3.org/TR/xpath-functions/#func-timezone-from-date
         | 
| 90 | 
            +
                def timezone
         | 
| 91 | 
            +
                  if @zone
         | 
| 92 | 
            +
                    md = @zone.match(ZONE_GRAMMAR)
         | 
| 93 | 
            +
                    si, hr, mi = md[:si], md[:hr].to_i, md[:mi].to_i
         | 
| 94 | 
            +
                    si = nil unless si == "-"
         | 
| 95 | 
            +
                    res = "#{si}PT#{hr}H#{"#{mi}M" if mi > 0}"
         | 
| 96 | 
            +
                    RDF::Literal(res, datatype: RDF::URI("http://www.w3.org/2001/XMLSchema#dayTimeDuration"))
         | 
| 97 | 
            +
                  end
         | 
| 98 | 
            +
                end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                ##
         | 
| 101 | 
            +
                # Returns `true` if the value adheres to the defined grammar of the
         | 
| 102 | 
            +
                # datatype.
         | 
| 103 | 
            +
                #
         | 
| 104 | 
            +
                # Special case for date and dateTime, for which '0000' is not a valid year
         | 
| 105 | 
            +
                #
         | 
| 106 | 
            +
                # @return [Boolean]
         | 
| 107 | 
            +
                # @since  0.2.1
         | 
| 108 | 
            +
                def valid?
         | 
| 109 | 
            +
                  super && object && value !~ %r(\A0000)
         | 
| 110 | 
            +
                end
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                ##
         | 
| 113 | 
            +
                # Does the literal representation include millisectonds?
         | 
| 114 | 
            +
                #
         | 
| 115 | 
            +
                # @return [Boolean]
         | 
| 116 | 
            +
                # @since 1.1.6
         | 
| 117 | 
            +
                def milliseconds?
         | 
| 118 | 
            +
                  object.strftime("%L").to_i > 0
         | 
| 119 | 
            +
                end
         | 
| 120 | 
            +
                alias_method :has_milliseconds?, :milliseconds?
         | 
| 121 | 
            +
                alias_method :has_ms?, :milliseconds?
         | 
| 122 | 
            +
                alias_method :ms?, :milliseconds?
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                ##
         | 
| 125 | 
            +
                # Returns the `timezone` of the literal. If the
         | 
| 126 | 
            +
                ##
         | 
| 127 | 
            +
                # Returns the value as a string.
         | 
| 128 | 
            +
                #
         | 
| 129 | 
            +
                # @return [String]
         | 
| 130 | 
            +
                def to_s
         | 
| 131 | 
            +
                  @string || (@object.strftime(self.class.const_get(:FORMAT)).sub('.000', '') + self.tz)
         | 
| 132 | 
            +
                end
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                ##
         | 
| 135 | 
            +
                # Adjust the timezone.
         | 
| 136 | 
            +
                #
         | 
| 137 | 
            +
                # From [fn:adjust-dateTime-to-timezone](https://www.w3.org/TR/xpath-functions/#func-adjust-dateTime-to-timezone)
         | 
| 138 | 
            +
                #
         | 
| 139 | 
            +
                # @overload adjust_to_timezone!
         | 
| 140 | 
            +
                #   Adjusts the timezone to UTC.
         | 
| 141 | 
            +
                #
         | 
| 142 | 
            +
                #   @return [Temporal] `self`
         | 
| 143 | 
            +
                #   @raise [RangeError] if `zone < -14*60` or `zone > 14*60`
         | 
| 144 | 
            +
                # @overload adjust_to_timezone!(zone)
         | 
| 145 | 
            +
                #   If `zone` is nil, then the timzeone component is removed.
         | 
| 146 | 
            +
                #
         | 
| 147 | 
            +
                #   Otherwise, the timezone is set based on the difference between the current timezone offset (if any) and `zone`.
         | 
| 148 | 
            +
                #
         | 
| 149 | 
            +
                #   @param [String] zone (nil) In the form of {ZONE_GRAMMAR}.
         | 
| 150 | 
            +
                #   @return [Temporal] `self`
         | 
| 151 | 
            +
                #   @raise [RangeError] if `zone < -14*60` or `zone > 14*60`
         | 
| 152 | 
            +
                # @see https://www.w3.org/TR/xpath-functions/#func-adjust-dateTime-to-timezone
         | 
| 153 | 
            +
                def adjust_to_timezone!(*args)
         | 
| 154 | 
            +
                  zone = args.empty? ? '+00:00' : args.first
         | 
| 155 | 
            +
                  if zone.nil?
         | 
| 156 | 
            +
                    # Remove timezone component
         | 
| 157 | 
            +
                    @object = self.class.new(@object.strftime(self.class.const_get(:FORMAT))).object
         | 
| 158 | 
            +
                    @zone = nil
         | 
| 159 | 
            +
                  else
         | 
| 160 | 
            +
                    md = zone.match(ZONE_GRAMMAR)
         | 
| 161 | 
            +
                    raise ArgumentError,
         | 
| 162 | 
            +
                          "expected #{zone.inspect} to be a xsd:dayTimeDuration or +/-HH:MM" unless md
         | 
| 163 | 
            +
             | 
| 164 | 
            +
                    # Adjust to zone
         | 
| 165 | 
            +
                    si, hr, mi = md[:si], md[:hr], md[:mi]
         | 
| 166 | 
            +
                    si ||= '+'
         | 
| 167 | 
            +
                    offset = hr.to_i * 60 + mi.to_i
         | 
| 168 | 
            +
                    raise ArgumentError,
         | 
| 169 | 
            +
                          "Zone adjustment of #{zone} out of range" if
         | 
| 170 | 
            +
                          md.nil? || offset > 14*60
         | 
| 171 | 
            +
             | 
| 172 | 
            +
                    new_zone = "%s%.2d:%.2d" % [si, hr.to_i, mi.to_i]
         | 
| 173 | 
            +
                    dt = @zone.nil? ? @object : @object.new_offset(new_zone)
         | 
| 174 | 
            +
                    @object = self.class.new(dt.strftime(self.class.const_get(:FORMAT) + new_zone)).object
         | 
| 175 | 
            +
                    @zone = new_zone
         | 
| 176 | 
            +
                  end
         | 
| 177 | 
            +
                  @string = nil
         | 
| 178 | 
            +
                  self
         | 
| 179 | 
            +
                end
         | 
| 180 | 
            +
             | 
| 181 | 
            +
                ##
         | 
| 182 | 
            +
                # Functional version of `#adjust_to_timezone!`.
         | 
| 183 | 
            +
                #
         | 
| 184 | 
            +
                # @overload adjust_to_timezone
         | 
| 185 | 
            +
                #   @param (see #adjust_to_timezone!)
         | 
| 186 | 
            +
                #   @return [DateTime]
         | 
| 187 | 
            +
                #   @raise (see #adjust_to_timezone!)
         | 
| 188 | 
            +
                # @overload adjust_to_timezone(zone) (see #adjust_to_timezone!)
         | 
| 189 | 
            +
                #   @return [DateTime]
         | 
| 190 | 
            +
                #   @raise (see #adjust_to_timezone!)
         | 
| 191 | 
            +
                def adjust_to_timezone(*args)
         | 
| 192 | 
            +
                  self.dup.adjust_to_timezone!(*args)
         | 
| 193 | 
            +
                end
         | 
| 194 | 
            +
             | 
| 195 | 
            +
                ##
         | 
| 196 | 
            +
                # Add a Duration to a Temporal.
         | 
| 197 | 
            +
                #
         | 
| 198 | 
            +
                # For YearMonthDuration, turns duration into months and adds to internal DateTime object.
         | 
| 199 | 
            +
                #
         | 
| 200 | 
            +
                # For DayTimeDuration, turns duration into rational days, and adds to internal DateTime object.
         | 
| 201 | 
            +
                #
         | 
| 202 | 
            +
                # @note This depends on the parameter responding to `#to_i` or `#to_r`, which for Duration types, is implemented in the rdf-xsd gem.
         | 
| 203 | 
            +
                #
         | 
| 204 | 
            +
                # @param [YearMonthDuration, DayTimeDuration] other
         | 
| 205 | 
            +
                # @return [Temporal]
         | 
| 206 | 
            +
                # @see https://www.w3.org/TR/xpath-functions/#func-add-yearMonthDuration-to-dateTime
         | 
| 207 | 
            +
                # @see https://www.w3.org/TR/xpath-functions/#func-add-dayTimeDuration-to-dateTime
         | 
| 208 | 
            +
                def +(other)
         | 
| 209 | 
            +
                  new_dt = case other
         | 
| 210 | 
            +
                  when YearMonthDuration
         | 
| 211 | 
            +
                    @object >> other.to_i
         | 
| 212 | 
            +
                  when DayTimeDuration
         | 
| 213 | 
            +
                    @object + other.to_r
         | 
| 214 | 
            +
                  else
         | 
| 215 | 
            +
                    return super
         | 
| 216 | 
            +
                  end
         | 
| 217 | 
            +
             | 
| 218 | 
            +
                  dt = new_dt.strftime(self.class.const_get(:FORMAT)) + tz
         | 
| 219 | 
            +
                  self.class.new(dt)
         | 
| 220 | 
            +
                rescue NoMethodError => e
         | 
| 221 | 
            +
                  raise "Consider including the rdf-xsd class for method implementaions: #{e.message}"
         | 
| 222 | 
            +
                end
         | 
| 223 | 
            +
             | 
| 224 | 
            +
                ##
         | 
| 225 | 
            +
                # Subtract times or durations from a temporal.
         | 
| 226 | 
            +
                #
         | 
| 227 | 
            +
                # @overload +(other)
         | 
| 228 | 
            +
                #   For YearMonthDuration, turns duration into months and subtracts from internal DateTime object resulting in a new {Temporal} object.
         | 
| 229 | 
            +
                #
         | 
| 230 | 
            +
                #   For DayTimeDuration, turns duration into rational days, and subtracts from internal DateTime object resulting in a new {Temporal} object.
         | 
| 231 | 
            +
                #
         | 
| 232 | 
            +
                #   For Temporal, subtracts the two moments resulting in a `xsd:dayTimeDuration`.
         | 
| 233 | 
            +
                #
         | 
| 234 | 
            +
                #   @param [YearMonthDuration, DayTimeDurationm, Temporal] other
         | 
| 235 | 
            +
                #   @return [Temporal, DayTimeDuration]
         | 
| 236 | 
            +
                #   @note This depends on the parameter responding to `#to_i` or `#to_r`, which for Duration types, is implemented in the rdf-xsd gem.
         | 
| 237 | 
            +
                #   @see https://www.w3.org/TR/xpath-functions/#func-subtract-yearMonthDuration-from-dateTime
         | 
| 238 | 
            +
                #   @see https://www.w3.org/TR/xpath-functions/#func-subtract-dayTimeDuration-from-dateTime
         | 
| 239 | 
            +
                #   @see https://www.w3.org/TR/xpath-functions/#func-subtract-dateTimes
         | 
| 240 | 
            +
                def -(other)
         | 
| 241 | 
            +
                  new_dt = case other
         | 
| 242 | 
            +
                  when YearMonthDuration
         | 
| 243 | 
            +
                    @object << other.to_i
         | 
| 244 | 
            +
                  when DayTimeDuration
         | 
| 245 | 
            +
                    @object - other.to_r
         | 
| 246 | 
            +
                  when Temporal
         | 
| 247 | 
            +
                    @object - other.object
         | 
| 248 | 
            +
                  else
         | 
| 249 | 
            +
                    return super
         | 
| 250 | 
            +
                  end
         | 
| 251 | 
            +
             | 
| 252 | 
            +
                  if new_dt.is_a?(Rational)
         | 
| 253 | 
            +
                    RDF::Literal(new_dt, datatype: RDF::XSD.dayTimeDuration)
         | 
| 254 | 
            +
                  else
         | 
| 255 | 
            +
                    dt = new_dt.strftime(self.class.const_get(:FORMAT)) + tz
         | 
| 256 | 
            +
                    self.class.new(dt)
         | 
| 257 | 
            +
                  end
         | 
| 258 | 
            +
                rescue NoMethodError => e
         | 
| 259 | 
            +
                  raise "Consider including the rdf-xsd class for method implementaions: #{e.message}"
         | 
| 260 | 
            +
                end
         | 
| 261 | 
            +
             | 
| 262 | 
            +
                # Years
         | 
| 263 | 
            +
                #
         | 
| 264 | 
            +
                # From the XQuery function [fn:year-from-dateTime](https://www.w3.org/TR/xpath-functions/#func-year-from-dateTime).
         | 
| 265 | 
            +
                #
         | 
| 266 | 
            +
                # @return [Integer]
         | 
| 267 | 
            +
                # @see https://www.w3.org/TR/xpath-functions/#func-year-from-dateTime
         | 
| 268 | 
            +
                def year; Integer.new(object.year); end
         | 
| 269 | 
            +
             | 
| 270 | 
            +
                # Months
         | 
| 271 | 
            +
                #
         | 
| 272 | 
            +
                # From the XQuery function [fn:month-from-dateTime](https://www.w3.org/TR/xpath-functions/#func-month-from-dateTime).
         | 
| 273 | 
            +
                #
         | 
| 274 | 
            +
                # @return [Integer]
         | 
| 275 | 
            +
                # @see https://www.w3.org/TR/xpath-functions/#func-month-from-dateTime
         | 
| 276 | 
            +
                def month; Integer.new(object.month); end
         | 
| 277 | 
            +
             | 
| 278 | 
            +
                # Days
         | 
| 279 | 
            +
                #
         | 
| 280 | 
            +
                # From the XQuery function [fn:day-from-dateTime](https://www.w3.org/TR/xpath-functions/#func-day-from-dateTime).
         | 
| 281 | 
            +
                #
         | 
| 282 | 
            +
                # @return [Integer]
         | 
| 283 | 
            +
                # @see https://www.w3.org/TR/xpath-functions/#func-day-from-dateTime
         | 
| 284 | 
            +
                def day; Integer.new(object.day); end
         | 
| 285 | 
            +
             | 
| 286 | 
            +
                # Hours
         | 
| 287 | 
            +
                #
         | 
| 288 | 
            +
                # From the XQuery function [fn:hours-from-dateTime](https://www.w3.org/TR/xpath-functions/#func-hours-from-dateTime).
         | 
| 289 | 
            +
                #
         | 
| 290 | 
            +
                # @return [Integer]
         | 
| 291 | 
            +
                # @see https://www.w3.org/TR/xpath-functions/#func-hours-from-dateTime
         | 
| 292 | 
            +
                def hours; Integer.new(object.hour); end
         | 
| 293 | 
            +
             | 
| 294 | 
            +
                # Minutes
         | 
| 295 | 
            +
                #
         | 
| 296 | 
            +
                # From the XQuery function [fn:minutes-from-dateTime](https://www.w3.org/TR/xpath-functions/#func-minutes-from-dateTime).
         | 
| 297 | 
            +
                #
         | 
| 298 | 
            +
                # @return [Integer]
         | 
| 299 | 
            +
                # @see https://www.w3.org/TR/xpath-functions/#func-minutes-from-dateTime
         | 
| 300 | 
            +
                def minutes; Integer.new(object.min); end
         | 
| 301 | 
            +
             | 
| 302 | 
            +
                # Seconds
         | 
| 303 | 
            +
                #
         | 
| 304 | 
            +
                # From the XQuery function [fn:seconds-from-dateTime](https://www.w3.org/TR/xpath-functions/#func-seconds-from-dateTime).
         | 
| 305 | 
            +
                #
         | 
| 306 | 
            +
                # @return [Decimal]
         | 
| 307 | 
            +
                # @see https://www.w3.org/TR/xpath-functions/#func-seconds-from-dateTime
         | 
| 308 | 
            +
                def seconds; Decimal.new(object.strftime("%S.%L")); end
         | 
| 309 | 
            +
              end # Temporal
         | 
| 310 | 
            +
            end; end # RDF::Literal
         | 
| @@ -9,91 +9,41 @@ module RDF; class Literal | |
| 9 9 | 
             
              #
         | 
| 10 10 | 
             
              # @see   http://www.w3.org/TR/xmlschema11-2/#time
         | 
| 11 11 | 
             
              # @since 0.2.1
         | 
| 12 | 
            -
              class Time <  | 
| 12 | 
            +
              class Time < Temporal
         | 
| 13 13 | 
             
                DATATYPE = RDF::URI("http://www.w3.org/2001/XMLSchema#time")
         | 
| 14 14 | 
             
                GRAMMAR  = %r(\A(\d{2}:\d{2}:\d{2}(?:\.\d+)?)((?:[\+\-]\d{2}:\d{2})|UTC|GMT|Z)?\Z).freeze
         | 
| 15 | 
            -
                FORMAT   = '%H:%M:%S.%L | 
| 15 | 
            +
                FORMAT   = '%H:%M:%S.%L'.freeze
         | 
| 16 16 |  | 
| 17 17 | 
             
                ##
         | 
| 18 | 
            +
                # Internally, a `DateTime` is represented using a native `::DateTime`. 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.
         | 
| 19 | 
            +
                #
         | 
| 18 20 | 
             
                # @param  [String, DateTime, #to_datetime] value
         | 
| 19 21 | 
             
                # @param  (see Literal#initialize)
         | 
| 20 22 | 
             
                def initialize(value, datatype: nil, lexical: nil, **options)
         | 
| 21 23 | 
             
                  @datatype = RDF::URI(datatype || self.class.const_get(:DATATYPE))
         | 
| 22 24 | 
             
                  @string   = lexical || (value if value.is_a?(String))
         | 
| 23 25 | 
             
                  @object   = case
         | 
| 24 | 
            -
                    when value. | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 28 | 
            -
             | 
| 29 | 
            -
             | 
| 30 | 
            -
                ##
         | 
| 31 | 
            -
                # Converts this literal into its canonical lexical representation.
         | 
| 32 | 
            -
                #
         | 
| 33 | 
            -
                # §3.2.8.2 Canonical representation
         | 
| 34 | 
            -
                #
         | 
| 35 | 
            -
                # The canonical representation for time is defined by prohibiting
         | 
| 36 | 
            -
                # certain options from the Lexical representation (§3.2.8.1).
         | 
| 37 | 
            -
                # Specifically, either the time zone must be omitted or, if present, the
         | 
| 38 | 
            -
                # time zone must be Coordinated Universal Time (UTC) indicated by a "Z".
         | 
| 39 | 
            -
                # Additionally, the canonical representation for midnight is 00:00:00.
         | 
| 40 | 
            -
                #
         | 
| 41 | 
            -
                # @return [RDF::Literal] `self`
         | 
| 42 | 
            -
                # @see    http://www.w3.org/TR/xmlschema11-2/#time
         | 
| 43 | 
            -
                def canonicalize!
         | 
| 44 | 
            -
                  if self.valid?
         | 
| 45 | 
            -
                    @string = if timezone?
         | 
| 46 | 
            -
                      @object.new_offset.new_offset.strftime(FORMAT[0..-4] + 'Z').sub('.000', '')
         | 
| 26 | 
            +
                    when value.respond_to?(:to_datetime)
         | 
| 27 | 
            +
                      dt = value.to_datetime
         | 
| 28 | 
            +
                      @zone = dt.zone
         | 
| 29 | 
            +
                      # Normalize to 1972-12-31 dateTime base
         | 
| 30 | 
            +
                      hms = dt.strftime(FORMAT)
         | 
| 31 | 
            +
                      ::DateTime.parse("1972-12-31T#{hms}#{@zone}")
         | 
| 47 32 | 
             
                    else
         | 
| 48 | 
            -
                       | 
| 49 | 
            -
             | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 52 | 
            -
             | 
| 53 | 
            -
             | 
| 54 | 
            -
             | 
| 55 | 
            -
             | 
| 56 | 
            -
             | 
| 57 | 
            -
             | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 61 | 
            -
                   | 
| 62 | 
            -
                  RDF::Literal(zone)
         | 
| 63 | 
            -
                end
         | 
| 64 | 
            -
             | 
| 65 | 
            -
                ##
         | 
| 66 | 
            -
                # Returns `true` if the value adheres to the defined grammar of the
         | 
| 67 | 
            -
                # datatype.
         | 
| 68 | 
            -
                #
         | 
| 69 | 
            -
                # Special case for date and dateTime, for which '0000' is not a valid year
         | 
| 70 | 
            -
                #
         | 
| 71 | 
            -
                # @return [Boolean]
         | 
| 72 | 
            -
                # @since  0.2.1
         | 
| 73 | 
            -
                def valid?
         | 
| 74 | 
            -
                  super && !object.nil?
         | 
| 75 | 
            -
                end
         | 
| 76 | 
            -
             | 
| 77 | 
            -
                ##
         | 
| 78 | 
            -
                # Does the literal representation include a timezone? Note that this is only possible if initialized using a string, or `:lexical` option.
         | 
| 79 | 
            -
                #
         | 
| 80 | 
            -
                # @return [Boolean]
         | 
| 81 | 
            -
                # @since 1.1.6
         | 
| 82 | 
            -
                def timezone?
         | 
| 83 | 
            -
                  md = self.to_s.match(GRAMMAR)
         | 
| 84 | 
            -
                  md && !!md[2]
         | 
| 85 | 
            -
                end
         | 
| 86 | 
            -
                alias_method :tz?, :timezone?
         | 
| 87 | 
            -
                alias_method :has_tz?, :timezone?
         | 
| 88 | 
            -
                alias_method :has_timezone?, :timezone?
         | 
| 89 | 
            -
             | 
| 90 | 
            -
                ##
         | 
| 91 | 
            -
                # Returns the value as a string.
         | 
| 92 | 
            -
                # Does not normalize timezone
         | 
| 93 | 
            -
                #
         | 
| 94 | 
            -
                # @return [String]
         | 
| 95 | 
            -
                def to_s
         | 
| 96 | 
            -
                  @string || @object.strftime(FORMAT).sub("+00:00", 'Z').sub('.000', '')
         | 
| 33 | 
            +
                      md = value.to_s.match(GRAMMAR)
         | 
| 34 | 
            +
                      _, tm, tz = Array(md)
         | 
| 35 | 
            +
                      if tz
         | 
| 36 | 
            +
                        @zone = tz == 'Z' ? '+00:00' : tz
         | 
| 37 | 
            +
                      else
         | 
| 38 | 
            +
                        @zone = nil # No timezone
         | 
| 39 | 
            +
                      end
         | 
| 40 | 
            +
                      # Normalize 24:00:00 to 00:00:00
         | 
| 41 | 
            +
                      hr, mi, se = tm.split(':')
         | 
| 42 | 
            +
                      hr = "%.2i" % (hr.to_i % 24) if hr.to_i > 23
         | 
| 43 | 
            +
                      value = "#{hr}:#{mi}:#{se}"
         | 
| 44 | 
            +
                      # Normalize to 1972-12-31 dateTime base
         | 
| 45 | 
            +
                      ::DateTime.parse("1972-12-31T#{hr}:#{mi}:#{se}#{@zone}")
         | 
| 46 | 
            +
                  end rescue ::DateTime.new
         | 
| 97 47 | 
             
                end
         | 
| 98 48 |  | 
| 99 49 | 
             
                ##
         | 
| @@ -104,32 +54,10 @@ module RDF; class Literal | |
| 104 54 | 
             
                def humanize(lang = :en)
         | 
| 105 55 | 
             
                  t = object.strftime("%r")
         | 
| 106 56 | 
             
                  if timezone?
         | 
| 107 | 
            -
                     | 
| 108 | 
            -
             | 
| 109 | 
            -
                    else
         | 
| 110 | 
            -
                      " #{self.tz}"
         | 
| 111 | 
            -
                    end
         | 
| 57 | 
            +
                    z = @zone == '+00:00' ? "UTC" : @zone
         | 
| 58 | 
            +
                    t += " #{z}"
         | 
| 112 59 | 
             
                  end
         | 
| 113 60 | 
             
                  t
         | 
| 114 61 | 
             
                end
         | 
| 115 | 
            -
             | 
| 116 | 
            -
                ##
         | 
| 117 | 
            -
                # Equal compares as Time objects
         | 
| 118 | 
            -
                def ==(other)
         | 
| 119 | 
            -
                  # If lexically invalid, use regular literal testing
         | 
| 120 | 
            -
                  return super unless self.valid?
         | 
| 121 | 
            -
             | 
| 122 | 
            -
                  case other
         | 
| 123 | 
            -
                  when Literal::Time
         | 
| 124 | 
            -
                    return super unless other.valid?
         | 
| 125 | 
            -
                    # Compare as strings, as time includes a date portion, and adjusting for UTC
         | 
| 126 | 
            -
                    # can create a mismatch in the date portion.
         | 
| 127 | 
            -
                    self.object.new_offset.strftime('%H%M%S.%L') == other.object.new_offset.strftime('%H%M%S.%L')
         | 
| 128 | 
            -
                  when Literal::DateTime, Literal::Date
         | 
| 129 | 
            -
                    false
         | 
| 130 | 
            -
                  else
         | 
| 131 | 
            -
                    super
         | 
| 132 | 
            -
                  end
         | 
| 133 | 
            -
                end
         | 
| 134 62 | 
             
              end # Time
         | 
| 135 63 | 
             
            end; end # RDF::Literal
         |