Skip to content

Commit

Permalink
Support ISO8601 without timezone (#31)
Browse files Browse the repository at this point in the history
* Support ISO8601 without timezone

* Add time_zone_required option

* Change option name
  • Loading branch information
axelson authored Apr 22, 2024
1 parent 8f26b3a commit 19dbb54
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 4 deletions.
35 changes: 31 additions & 4 deletions lib/machete/matchers/iso8601_datetime_matcher.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ defmodule Machete.ISO8601DateTimeMatcher do

import Machete.DateTimeMatcher
import Machete.Mismatch
import Machete.NaiveDateTimeMatcher
import Machete.Operators

defstruct datetime_opts: nil
Expand All @@ -20,6 +21,7 @@ defmodule Machete.ISO8601DateTimeMatcher do
@type opts :: [
{:precision, 0..6},
{:time_zone, Calendar.time_zone() | :utc},
{:offset_required, boolean()},
{:exactly, DateTime.t()},
{:roughly, DateTime.t() | :now},
{:before, DateTime.t() | :now},
Expand All @@ -34,6 +36,7 @@ defmodule Machete.ISO8601DateTimeMatcher do
* `precision`: Requires the matched ISO8601 string to have the specified microsecond precision
* `time_zone`: Requires the matched ISO8601 string to have the specified time zone. The atom
`:utc` can be used to specify the "Etc/UTC" time zone
* `offset_required`: Requires the matched ISO8601 string to have a timezone. Defaults to true
* `exactly`: Requires the matched ISO8601 string to be exactly equal to the specified DateTime
* `roughly`: Requires the matched ISO8601 string to be within +/- 10 seconds of the specified
DateTime. This values must be specified as a DateTime. The atom `:now` can be used to use the
Expand Down Expand Up @@ -65,6 +68,9 @@ defmodule Machete.ISO8601DateTimeMatcher do
iex> assert DateTime.utc_now() |> DateTime.to_iso8601() ~> iso8601_datetime(roughly: :now)
true
iex> assert NaiveDateTime.utc_now() |> NaiveDateTime.to_iso8601() ~> iso8601_datetime(roughly: :now, offset_required: false)
true
iex> assert "2020-01-01T00:00:00.000000Z" ~> iso8601_datetime(roughly: ~U[2020-01-01 00:00:05.000000Z])
true
Expand All @@ -85,13 +91,34 @@ defmodule Machete.ISO8601DateTimeMatcher do

defimpl Machete.Matchable do
def mismatches(%@for{} = a, b) when is_binary(b) do
DateTime.from_iso8601(b)
|> case do
{:ok, datetime_b, 0} -> datetime_b ~>> datetime(a.datetime_opts)
_ -> mismatch("#{inspect(b)} is not a parseable ISO8601 datetime")
with nil <- matches_date_time(b, a.datetime_opts) do
matches_naive_date_time(b, a.datetime_opts)
end
end

def mismatches(%@for{}, b), do: mismatch("#{inspect(b)} is not a string")

defp matches_date_time(b, datetime_opts) do
case DateTime.from_iso8601(b) do
{:ok, datetime_b, 0} -> datetime_b ~>> datetime(datetime_opts)
_ -> nil
end
end

defp matches_naive_date_time(b, datetime_opts) do
{offset_required, datetime_opts} = Keyword.pop(datetime_opts, :offset_required, true)

case NaiveDateTime.from_iso8601(b) do
{:ok, naive_datetime_b} ->
if offset_required do
mismatch("#{inspect(b)} does not have a time zone offset")
else
naive_datetime_b ~>> naive_datetime(datetime_opts)
end

_ ->
mismatch("#{inspect(b)} is not a parseable ISO8601 datetime")
end
end
end
end
12 changes: 12 additions & 0 deletions test/machete/matchers/iso8601_datetime_matcher_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,18 @@ defmodule ISO8601DateTimeMatcherTest do
)
end

test "does not supports ISO8601 without timezones by default" do
assert "2020-01-01T00:00:00.000000"
~>> iso8601_datetime(roughly: ~N[2020-01-01 00:00:00])
~> mismatch("\"2020-01-01T00:00:00.000000\" does not have a time zone offset")
end

test "supports ISO8601 without timezones" do
assert "2020-01-01T00:00:00.000000"
~>> iso8601_datetime(roughly: ~N[2020-01-01 00:00:00], offset_required: false)
~> []
end

test "produces a useful mismatch for exactly mismatches" do
assert "2020-01-01T00:00:00.000000Z"
~>> iso8601_datetime(exactly: ~U[3000-01-01 00:00:00.000000Z])
Expand Down

0 comments on commit 19dbb54

Please sign in to comment.