core: RDF.Date datatype

- negative years not yet supported
This commit is contained in:
Marcel Otto 2017-05-01 18:06:53 +02:00
parent 354ead9d80
commit 8eb15b581b
3 changed files with 107 additions and 3 deletions

52
lib/rdf/datatypes/date.ex Normal file
View file

@ -0,0 +1,52 @@
defmodule RDF.Date do
use RDF.Datatype, id: RDF.Datatype.NS.XSD.date
@grammar ~r/\A(-?\d{4}-\d{2}-\d{2})((?:[\+\-]\d{2}:\d{2})|UTC|GMT|Z)?\Z/
def convert(%Date{} = value, %{tz: "+00:00"} = opts) do
{convert(value, Map.delete(opts, :tz)), "Z"}
end
def convert(%Date{} = value, %{tz: tz} = opts) do
{convert(value, Map.delete(opts, :tz)), tz}
end
# Special case for date and dateTime, for which 0 is not a valid year
def convert(%Date{year: 0} = value, opts), do: super(value, opts)
def convert(%Date{} = value, _), do: value
def convert(value, opts) when is_binary(value) do
case Regex.run(@grammar, value) do
[_, date] ->
date
|> do_convert
|> convert(opts)
[_, date, zone] ->
date
|> do_convert
|> convert(Map.put(opts, :tz, zone))
_ ->
super(value, opts)
end
end
def convert(value, opts), do: super(value, opts)
defp do_convert(value) do
case Date.from_iso8601(value) do
{:ok, date} -> date
_ -> nil
end
end
def canonical_lexical(%Date{} = value) do
Date.to_iso8601(value)
end
def canonical_lexical({%Date{} = value, zone}) do
canonical_lexical(value) <> zone
end
end

View file

@ -46,9 +46,7 @@ defmodule RDF.Literal do
def new(value) when is_integer(value), do: RDF.Integer.new(value)
def new(value) when is_float(value), do: RDF.Double.new(value)
# TODO:
def new(%Date{} = date), do: %RDF.Literal{value: date, datatype: XSD.date}
# def new(%Date{} = value), do: RDF.Date.new(value)
def new(%Date{} = value), do: RDF.Date.new(value)
def new(%Time{} = value), do: RDF.Time.new(value)
def new(%DateTime{} = value), do: RDF.DateTime.new(value)
def new(%NaiveDateTime{} = value), do: RDF.DateTime.new(value)

View file

@ -0,0 +1,54 @@
defmodule RDF.DateTest do
use RDF.Datatype.Test.Case, datatype: RDF.Date, id: RDF.NS.XSD.date,
valid: %{
# input => { value , lexical , canonicalized }
~D[2010-01-01] => { ~D[2010-01-01] , nil , "2010-01-01" },
"2010-01-01" => { ~D[2010-01-01] , nil , "2010-01-01" },
"2010-01-01Z" => { {~D[2010-01-01], "Z"} , nil , "2010-01-01Z" },
"2010-01-01+00:00" => { {~D[2010-01-01], "Z"} , "2010-01-01+00:00" , "2010-01-01Z" },
"2010-01-01-00:00" => { {~D[2010-01-01], "-00:00"} , nil , "2010-01-01-00:00" },
"2010-01-01+01:00" => { {~D[2010-01-01], "+01:00"} , nil , "2010-01-01+01:00" },
"2009-12-31-01:00" => { {~D[2009-12-31], "-01:00"} , nil , "2009-12-31-01:00" },
"2014-09-01-08:00" => { {~D[2014-09-01], "-08:00"} , nil , "2014-09-01-08:00" },
# TODO: DateTime doesn't support negative years (at least with the iso8601 conversion functions)
# "-2010-01-01Z" => { ~D[-2010-01-01] , nil , "-2010-01-01Z" },
},
invalid: ~w(
foo
+2010-01-01Z
2010-01-01TFOO
02010-01-01
2010-1-1
0000-01-01
2011-07
2011
) ++ [true, false, 2010, 3.14, "2010-01-01Z foo", "foo 2010-01-01Z"]
describe "equality" do
test "two literals are equal when they have the same datatype and lexical form" do
[
{ ~D[2010-01-01] , "2010-01-01" },
]
|> Enum.each(fn {l, r} ->
assert Date.new(l) == Date.new(r)
end)
end
test "two literals with same value but different lexical form are not equal" do
[
{ ~D[2010-01-01] , "2010-01-01Z" },
{ ~D[2010-01-01] , "2010-01-01+00:00" },
{ "2010-01-01" , "00:00:00Z" },
{ "2010-01-01+00:00" , "00:00:00Z" },
{ "2010-01-01-00:00" , "00:00:00Z" },
{ "2010-01-01+00:00" , "00:00:00" },
{ "2010-01-01-00:00" , "00:00:00" },
]
|> Enum.each(fn {l, r} ->
assert Date.new(l) != Date.new(r)
end)
end
end
end