Skip to content

Commit fe372a8

Browse files
Add specs and improve guards to Calendar.ISO.parse_*/2 functions (#11329)
1 parent a6267a6 commit fe372a8

File tree

2 files changed

+65
-26
lines changed

2 files changed

+65
-26
lines changed

lib/elixir/lib/calendar/iso.ex

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ defmodule Calendar.ISO do
131131
unix_end = 315_569_519_999_999_999 - @unix_epoch * 1_000_000
132132
@unix_range_microseconds unix_start..unix_end
133133

134+
defguardp is_format(term) when term in [:basic, :extended]
135+
134136
@typedoc """
135137
"Before the Current Era" or "Before the Common Era" (BCE), for those years less than `1`.
136138
"""
@@ -157,6 +159,7 @@ defmodule Calendar.ISO do
157159
@type second :: 0..59
158160
@type weekday :: :monday | :tuesday | :wednesday | :thursday | :friday | :saturday | :sunday
159161
@type utc_offset :: integer
162+
@type format :: :basic | :extended
160163

161164
@typedoc """
162165
Microseconds with stored precision.
@@ -286,11 +289,15 @@ defmodule Calendar.ISO do
286289
287290
"""
288291
@doc since: "1.12.0"
289-
def parse_time("T" <> string, format),
290-
do: do_parse_time(string, format)
291-
292-
def parse_time(string, format) when is_binary(string),
293-
do: do_parse_time(string, format)
292+
@spec parse_time(String.t(), format) ::
293+
{:ok, {hour, minute, second, microsecond}}
294+
| {:error, atom}
295+
def parse_time(string, format) when is_binary(string) and is_format(format) do
296+
case string do
297+
"T" <> rest -> do_parse_time(rest, format)
298+
_ -> do_parse_time(string, format)
299+
end
300+
end
294301

295302
defp do_parse_time(<<unquote(match_basic_time), rest::binary>>, :basic)
296303
when unquote(guard_time) do
@@ -363,13 +370,19 @@ defmodule Calendar.ISO do
363370
364371
"""
365372
@doc since: "1.12.0"
366-
def parse_date("-" <> string, format),
373+
@spec parse_date(String.t(), format) ::
374+
{:ok, {year, month, day}}
375+
| {:error, atom}
376+
def parse_date(string, format) when is_binary(string) and is_format(format),
377+
do: parse_date_guarded(string, format)
378+
379+
defp parse_date_guarded("-" <> string, format),
367380
do: do_parse_date(string, -1, format)
368381

369-
def parse_date("+" <> string, format),
382+
defp parse_date_guarded("+" <> string, format),
370383
do: do_parse_date(string, 1, format)
371384

372-
def parse_date(string, format) when is_binary(string),
385+
defp parse_date_guarded(string, format),
373386
do: do_parse_date(string, 1, format)
374387

375388
defp do_parse_date(unquote(match_basic_date), multiplier, :basic) when unquote(guard_date) do
@@ -442,13 +455,19 @@ defmodule Calendar.ISO do
442455
443456
"""
444457
@doc since: "1.12.0"
445-
def parse_naive_datetime("-" <> string, format),
458+
@spec parse_naive_datetime(String.t(), format) ::
459+
{:ok, {year, month, day, hour, minute, second, microsecond}}
460+
| {:error, atom}
461+
def parse_naive_datetime(string, format) when is_binary(string) and is_format(format),
462+
do: parse_naive_datetime_guarded(string, format)
463+
464+
defp parse_naive_datetime_guarded("-" <> string, format),
446465
do: do_parse_naive_datetime(string, -1, format)
447466

448-
def parse_naive_datetime("+" <> string, format),
467+
defp parse_naive_datetime_guarded("+" <> string, format),
449468
do: do_parse_naive_datetime(string, 1, format)
450469

451-
def parse_naive_datetime(string, format) when is_binary(string),
470+
defp parse_naive_datetime_guarded(string, format),
452471
do: do_parse_naive_datetime(string, 1, format)
453472

454473
defp do_parse_naive_datetime(
@@ -540,13 +559,19 @@ defmodule Calendar.ISO do
540559
541560
"""
542561
@doc since: "1.12.0"
543-
def parse_utc_datetime("-" <> string, format),
562+
@spec parse_utc_datetime(String.t(), format) ::
563+
{:ok, {year, month, day, hour, minute, second, microsecond}, utc_offset}
564+
| {:error, atom}
565+
def parse_utc_datetime(string, format) when is_binary(string) and is_format(format),
566+
do: parse_utc_datetime_guarded(string, format)
567+
568+
defp parse_utc_datetime_guarded("-" <> string, format),
544569
do: do_parse_utc_datetime(string, -1, format)
545570

546-
def parse_utc_datetime("+" <> string, format),
571+
defp parse_utc_datetime_guarded("+" <> string, format),
547572
do: do_parse_utc_datetime(string, 1, format)
548573

549-
def parse_utc_datetime(string, format) when is_binary(string),
574+
defp parse_utc_datetime_guarded(string, format),
550575
do: do_parse_utc_datetime(string, 1, format)
551576

552577
defp do_parse_utc_datetime(

lib/elixir/test/elixir/calendar/iso_test.exs

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,13 @@ defmodule Calendar.ISOTest do
148148
end
149149

150150
test "errors on other format names" do
151-
assert Calendar.ISO.parse_date("20150123", :other) == {:error, :invalid_format}
152-
assert Calendar.ISO.parse_date("2015-01-23", :other) == {:error, :invalid_format}
151+
assert_raise FunctionClauseError, fn ->
152+
Calendar.ISO.parse_date("20150123", :other)
153+
end
154+
155+
assert_raise FunctionClauseError, fn ->
156+
Calendar.ISO.parse_date("2015-01-23", :other)
157+
end
153158
end
154159
end
155160

@@ -226,8 +231,13 @@ defmodule Calendar.ISOTest do
226231
end
227232

228233
test "errors on other format names" do
229-
assert Calendar.ISO.parse_time("235007", :other) == {:error, :invalid_format}
230-
assert Calendar.ISO.parse_time("23:50:07", :other) == {:error, :invalid_format}
234+
assert_raise FunctionClauseError, fn ->
235+
Calendar.ISO.parse_time("235007", :other)
236+
end
237+
238+
assert_raise FunctionClauseError, fn ->
239+
Calendar.ISO.parse_time("23:50:07", :other)
240+
end
231241
end
232242
end
233243

@@ -308,11 +318,13 @@ defmodule Calendar.ISOTest do
308318
end
309319

310320
test "errors on other format names" do
311-
assert Calendar.ISO.parse_naive_datetime("20150123 235007.123", :other) ==
312-
{:error, :invalid_format}
321+
assert_raise FunctionClauseError, fn ->
322+
Calendar.ISO.parse_naive_datetime("20150123 235007.123", :other)
323+
end
313324

314-
assert Calendar.ISO.parse_naive_datetime("2015-01-23 23:50:07.123", :other) ==
315-
{:error, :invalid_format}
325+
assert_raise FunctionClauseError, fn ->
326+
Calendar.ISO.parse_naive_datetime("2015-01-23 23:50:07.123", :other)
327+
end
316328
end
317329
end
318330

@@ -393,11 +405,13 @@ defmodule Calendar.ISOTest do
393405
end
394406

395407
test "errors on other format names" do
396-
assert Calendar.ISO.parse_naive_datetime("20150123 235007.123Z", :other) ==
397-
{:error, :invalid_format}
408+
assert_raise FunctionClauseError, fn ->
409+
Calendar.ISO.parse_naive_datetime("20150123 235007.123Z", :other)
410+
end
398411

399-
assert Calendar.ISO.parse_naive_datetime("2015-01-23 23:50:07.123Z", :other) ==
400-
{:error, :invalid_format}
412+
assert_raise FunctionClauseError, fn ->
413+
Calendar.ISO.parse_naive_datetime("2015-01-23 23:50:07.123Z", :other)
414+
end
401415
end
402416

403417
test "errors on mixed basic and extended formats" do

0 commit comments

Comments
 (0)