Add various utility functions on RDF.Time and RDF.DateTime

This commit is contained in:
Marcel Otto 2018-09-11 03:12:15 +02:00
parent 1fa369197e
commit de25b5399b
5 changed files with 88 additions and 13 deletions

View file

@ -20,7 +20,7 @@ This project adheres to [Semantic Versioning](http://semver.org/) and
XSD spec
- the logical operators and the Effective Boolean Value (EBV) coercion algorithm
from the XPath and SPARQL specs on `RDF.Boolean`
- `RDF.DateTime.now/0`
- various functions on the `RDF.DateTime` and `RDF.Time` datatypes
- `RDF.Term.equal?/2` and `RDF.Term.equal_value?/2`
- `RDF.LangString.match_language?/2`
- possibility to configure an application-specific default base IRI; for now it

View file

@ -5,6 +5,8 @@ defmodule RDF.DateTime do
use RDF.Datatype, id: RDF.Datatype.NS.XSD.dateTime
import RDF.Literal.Guards
def now() do
new(DateTime.utc_now())
@ -59,20 +61,35 @@ defmodule RDF.DateTime do
end
def tz(%Literal{value: %NaiveDateTime{}}), do: ""
def tz(datetime_literal) do
if valid?(datetime_literal) do
lexical = lexical(datetime_literal)
case Regex.run(~r/([+-])(\d\d:\d\d)/, lexical) do
[_, sign, zone] ->
sign <> zone
_ ->
if String.ends_with?(lexical, "Z") do
"Z"
else
""
end
end
datetime_literal
|> lexical()
|> RDF.DateTimeUtils.tz()
end
end
@doc """
Converts a datetime literal to a canonical string, preserving the zone information.
"""
def canonical_lexical_with_zone(%Literal{datatype: datatype} = literal)
when is_xsd_datetime(datatype) do
case tz(literal) do
nil ->
nil
zone when zone in ["Z", "", "+00:00"] ->
canonical_lexical(literal.value)
zone ->
literal
|> lexical()
|> String.replace_trailing(zone, "Z")
|> DateTime.from_iso8601()
|> elem(1)
|> canonical_lexical()
|> String.replace_trailing("Z", zone)
end
end

View file

@ -0,0 +1,17 @@
defmodule RDF.DateTimeUtils do
@moduledoc false
def tz(string) do
case Regex.run(~r/([+-])(\d\d:\d\d)/, string) do
[_, sign, zone] ->
sign <> zone
_ ->
if String.ends_with?(string, "Z") do
"Z"
else
""
end
end
end
end

View file

@ -5,6 +5,8 @@ defmodule RDF.Time do
use RDF.Datatype, id: RDF.Datatype.NS.XSD.time
import RDF.Literal.Guards
@grammar ~r/\A(\d{2}:\d{2}:\d{2}(?:\.\d+)?)((?:[\+\-]\d{2}:\d{2})|UTC|GMT|Z)?\Z/
@tz_grammar ~r/\A(?:([\+\-])(\d{2}):(\d{2}))\Z/
@ -76,4 +78,35 @@ defmodule RDF.Time do
canonical_lexical(value) <> "Z"
end
def tz(time_literal) do
if valid?(time_literal) do
time_literal
|> lexical()
|> RDF.DateTimeUtils.tz()
end
end
@doc """
Converts a time literal to a canonical string, preserving the zone information.
"""
def canonical_lexical_with_zone(%Literal{datatype: datatype} = literal)
when is_xsd_time(datatype) do
case tz(literal) do
nil ->
nil
zone when zone in ["Z", "", "+00:00"] ->
canonical_lexical(literal.value)
zone ->
literal
|> lexical()
|> String.replace_trailing(zone, "")
|> Time.from_iso8601!()
|> canonical_lexical()
|> Kernel.<>(zone)
end
end
end

View file

@ -123,4 +123,12 @@ defmodule RDF.DateTimeTest do
end
end
test "canonical_lexical_with_zone/1" do
assert RDF.date_time(~N[2010-01-01T12:34:56]) |> DateTime.canonical_lexical_with_zone() == "2010-01-01T12:34:56"
assert RDF.date_time("2010-01-01T12:34:56") |> DateTime.canonical_lexical_with_zone() == "2010-01-01T12:34:56"
assert RDF.date_time("2010-01-01T00:00:00+00:00") |> DateTime.canonical_lexical_with_zone() == "2010-01-01T00:00:00Z"
assert RDF.date_time("2010-01-01T01:00:00+01:00") |> DateTime.canonical_lexical_with_zone() == "2010-01-01T01:00:00+01:00"
assert RDF.date_time("2010-01-01 01:00:00+01:00") |> DateTime.canonical_lexical_with_zone() == "2010-01-01T01:00:00+01:00"
end
end