From 7c28315f2a86e2b15bd5a1d52c7d774e738f7315 Mon Sep 17 00:00:00 2001 From: Kip Cole Date: Mon, 8 Jul 2024 08:05:29 +1000 Subject: [PATCH] Improve format option consistency checking --- lib/cldr/date_time.ex | 86 ++++++++++++++++++++++++---------- test/cldr_dates_times_test.exs | 29 ++++++++++++ 2 files changed, 89 insertions(+), 26 deletions(-) diff --git a/lib/cldr/date_time.ex b/lib/cldr/date_time.ex index 3772208..dbda5c8 100644 --- a/lib/cldr/date_time.ex +++ b/lib/cldr/date_time.ex @@ -196,18 +196,14 @@ defmodule Cldr.DateTime do def to_string(%{} = datetime, backend, options) when is_atom(backend) and is_list(options) and has_date_and_time(datetime) do - options = normalize_options(datetime, backend, options) format_backend = Module.concat(backend, DateTime.Formatter) - format = options.format - calendar = Map.get(datetime, :calendar, Cldr.Calendar.Gregorian) - datetime = Map.put_new(datetime, :calendar, calendar) - - with {:ok, locale} <- Cldr.validate_locale(options.locale, backend), - {:ok, cldr_calendar} <- Cldr.DateTime.type_from_calendar(calendar), + with {:ok, datetime, options} <- normalize_options(datetime, backend, options), + {:ok, locale} <- Cldr.validate_locale(options.locale, backend), + {:ok, cldr_calendar} <- Cldr.DateTime.type_from_calendar(datetime.calendar), {:ok, _} <- Cldr.Number.validate_number_system(locale, options.number_system, backend), {:ok, format, options} <- - find_format(datetime, format, locale, cldr_calendar, backend, options), + find_format(datetime, options.format, locale, cldr_calendar, backend, options), {:ok, format} <- apply_unicode_or_ascii_preference(format, options.prefer), {:ok, format_string} <- resolve_plural_format(format, datetime, backend, options) do format_backend.format(datetime, format_string, locale, options) @@ -325,23 +321,32 @@ defmodule Cldr.DateTime do {locale, _backend} = Cldr.locale_and_backend_from(nil, backend) number_system = Cldr.Number.System.number_system_from_locale(locale, backend) + calendar = Map.get(datetime, :calendar, Cldr.Calendar.Gregorian) + datetime = Map.put_new(datetime, :calendar, calendar) + {format, date_format, time_format} = formats_from_options(datetime, nil, nil, nil, @default_format_type) - %{ - locale: locale, - number_system: number_system, - format: format, - date_format: date_format, - time_format: time_format, - style: @default_style, - prefer: @default_prefer - } + options = + %{ + locale: locale, + number_system: number_system, + format: format, + date_format: date_format, + time_format: time_format, + style: @default_style, + prefer: @default_prefer + } + + {:ok, datetime, options} end defp normalize_options(datetime, backend, options) do {locale, backend} = Cldr.locale_and_backend_from(options[:locale], backend) + calendar = Map.get(datetime, :calendar, Cldr.Calendar.Gregorian) + datetime = Map.put_new(datetime, :calendar, calendar) + style = options[:style] || @default_style prefer = options[:prefer] || @default_prefer @@ -355,15 +360,44 @@ defmodule Cldr.DateTime do {format, date_format, time_format} = formats_from_options(datetime, format, date_format, time_format, @default_format_type) - options - |> Map.new() - |> Map.put(:locale, locale) - |> Map.put(:format, format) - |> Map.put(:date_format, date_format) - |> Map.put(:time_format, time_format) - |> Map.put(:style, style) - |> Map.put(:prefer, prefer) - |> Map.put(:number_system, number_system) + with :ok <- validate_formats_consistent(format, date_format, time_format) do + options = + options + |> Map.new() + |> Map.put(:locale, locale) + |> Map.put(:format, format) + |> Map.put(:date_format, date_format) + |> Map.put(:time_format, time_format) + |> Map.put(:style, style) + |> Map.put(:prefer, prefer) + |> Map.put(:number_system, number_system) + + {:ok, datetime, options} + end + end + + defp validate_formats_consistent(format, nil = _date_format, nil = _time_format) + when is_atom(format) or is_binary(format) do + :ok + end + + defp validate_formats_consistent(nil, date_format, time_format) + when not is_nil(date_format) and not is_nil(time_format) do + :ok + end + + defp validate_formats_consistent(format, date_format, time_format) + when format in @format_types and date_format in @format_types and time_format in @format_types do + :ok + end + + defp validate_formats_consistent(format, date_format, time_format) + when is_atom(format) or is_binary(format) do + {:error, {Cldr.DateTime.InvalidFormat, + ":date_format and :time_format cannot be specified if :format is also specified as " <> + "a format id or a format string. Found [time_format: #{inspect time_format}, " <> + "date_format: #{inspect date_format}]" + }} end # Returns the CLDR calendar type for a calendar diff --git a/test/cldr_dates_times_test.exs b/test/cldr_dates_times_test.exs index 6da612a..c34698f 100644 --- a/test/cldr_dates_times_test.exs +++ b/test/cldr_dates_times_test.exs @@ -93,4 +93,33 @@ defmodule Cldr.DatesTimes.Test do {:error, {Cldr.DateTime.UnresolvedFormat, "No available format resolved for :hme"}} end + + test "Datetime formatting with standard formats" do + datetime = ~U[2024-07-07 21:36:00.440105Z] + + assert {:ok, "Jul 7, 2024, 9:36:00 PM"} = + Cldr.DateTime.to_string(datetime) + + assert {:ok, "7/7/24, 9:36:00 PM GMT"} = + Cldr.DateTime.to_string(datetime, date_format: :short, time_format: :full) + + assert {:ok, "7/7/24, 9:36:00 PM GMT"} = + Cldr.DateTime.to_string(datetime, format: :medium, date_format: :short, time_format: :full) + end + + test "Datetime format option consistency" do + datetime = ~U[2024-07-07 21:36:00.440105Z] + + assert Cldr.DateTime.to_string(datetime, format: "yyy", date_format: :short, time_format: :medium) + {:error, + {Cldr.DateTime.InvalidFormat, + ":date_format and :time_format cannot be specified if :format is also specified as a " <> + "format id or a format string. Found [time_format: :medium, date_format: :short]"}} + + assert Cldr.DateTime.to_string(datetime, format: :yMd, date_format: :short, time_format: :medium) + {:error, + {Cldr.DateTime.InvalidFormat, + ":date_format and :time_format cannot be specified if :format is also specified as a " <> + "format id or a format string. Found [time_format: :medium, date_format: :short]"}} + end end