From b74c5b64c7f9181b688156bd0cb5e0c494f3a94f Mon Sep 17 00:00:00 2001 From: Evgeny Golomidov Date: Wed, 29 Aug 2018 14:47:40 -0400 Subject: [PATCH] added new directive: day with suffix --- lib/format/datetime/formatter.ex | 11 +++++++++++ lib/format/datetime/formatters/strftime.ex | 5 +++-- lib/parse/datetime/parsers.ex | 6 ++++++ lib/parse/datetime/tokenizers/directive.ex | 2 +- lib/parse/datetime/tokenizers/strftime.ex | 2 ++ test/format_strftime_test.exs | 8 ++++++++ 6 files changed, 31 insertions(+), 3 deletions(-) diff --git a/lib/format/datetime/formatter.ex b/lib/format/datetime/formatter.ex index 3e23a4cb..80d4424b 100644 --- a/lib/format/datetime/formatter.ex +++ b/lib/format/datetime/formatter.ex @@ -476,6 +476,17 @@ defmodule Timex.Format.DateTime.Formatter do do: pad_numeric(date.day, flags, width) def format_token(_locale, :oday, date, _modifiers, flags, width), do: pad_numeric(Timex.day(date), flags, width) + def format_token(_locale, :dsuff, %{:day => day}, _modifiers, _flags, _width) do + remainder = day |> rem(10) + suffix = + cond do + remainder == 1 && day != 11 -> "st" + remainder == 2 && day != 12 -> "nd" + remainder == 3 && day != 13 -> "rd" + true -> "th" + end + "#{day}" <> suffix + end # Weeks def format_token(_locale, :iso_weeknum, date, _modifiers, flags, width) do {_, week} = Timex.iso_week(date) diff --git a/lib/format/datetime/formatters/strftime.ex b/lib/format/datetime/formatters/strftime.ex index d4a0b2ff..49dccbc2 100644 --- a/lib/format/datetime/formatters/strftime.ex +++ b/lib/format/datetime/formatters/strftime.ex @@ -22,8 +22,8 @@ defmodule Timex.Format.DateTime.Formatters.Strftime do * `-` - don't pad numerical results (overrides default padding if any) * `0` - use zeros for padding * `_` - use spaces for padding - * `:`, `::` - used only in combination with `%z`; see description of `%:z` - and `%::z` below + * `:`, `::` - used only in combination with `%z` and `%d`; see description of `%:d`, + `%:z` and `%::z` below `` is a non-negative decimal number specifying the minimum field width. @@ -59,6 +59,7 @@ defmodule Timex.Format.DateTime.Formatters.Strftime do * `%w` - weekday, Sunday first (0..6) * `%a` - abbreviated weekday name (Mon..Sun, no padding) * `%A` - full weekday name (Monday..Sunday, no padding) + * `%:d` - combined day number with english ordinal suffix for the day of the month (1st, 2nd, 3rd, 4th..30th) ### Weeks diff --git a/lib/parse/datetime/parsers.ex b/lib/parse/datetime/parsers.ex index ab2e049a..18c8a923 100644 --- a/lib/parse/datetime/parsers.ex +++ b/lib/parse/datetime/parsers.ex @@ -76,6 +76,12 @@ defmodule Timex.Parse.DateTime.Parsers do |> label("month abbreviation") end + def day_suffix(opts \\ []) do + Helpers.integer(opts) + |> satisfy(fn day -> day >= 1 && day <= 31 end) + |> map(fn n -> [day_suffix: n] end) + |> label("english ordinal suffix for the day of the month, 2 characters") + end def day_of_month(opts \\ []) do Helpers.integer(opts) |> satisfy(fn day -> day >= 1 && day <= 31 end) diff --git a/lib/parse/datetime/tokenizers/directive.ex b/lib/parse/datetime/tokenizers/directive.ex index eef16b08..69e572e5 100644 --- a/lib/parse/datetime/tokenizers/directive.ex +++ b/lib/parse/datetime/tokenizers/directive.ex @@ -42,7 +42,7 @@ defmodule Timex.Parse.DateTime.Tokenizers.Directive do ] @mapped_types [iso_year4: :year4, iso_year2: :year2, month: :month2, mshort: :month_short, mfull: :month_full, - day: :day_of_month, oday: :day_of_year, + day: :day_of_month, oday: :day_of_year, dsuff: :day_suffix, iso_weeknum: :week_of_year, week_mon: :week_of_year, week_sun: :week_of_year_sun, wday_mon: :weekday, wday_sun: :weekday, wdshort: :weekday_short, wdfull: :weekday_full, min: :minute, sec: :second, sec_fractional: :second_fractional, sec_epoch: :seconds_epoch, diff --git a/lib/parse/datetime/tokenizers/strftime.ex b/lib/parse/datetime/tokenizers/strftime.ex index 9501c06a..6c7b1727 100644 --- a/lib/parse/datetime/tokenizers/strftime.ex +++ b/lib/parse/datetime/tokenizers/strftime.ex @@ -51,6 +51,7 @@ defmodule Timex.Parse.DateTime.Tokenizers.Strftime do # Compound "D", "F", "R", "r", "T", "v" ]), + string(":d"), string(":z"), string("::z") ]) @@ -88,6 +89,7 @@ defmodule Timex.Parse.DateTime.Tokenizers.Strftime do "d" -> force_width(2, :day, directive, opts) "e" -> force_width(2, :day, directive, Keyword.merge(opts, flags: Keyword.merge([padding: :spaces], get_in(opts, [:flags])))) "j" -> force_width(3, :oday, directive, opts) + ":d" -> Directive.get(:dsuff, directive, opts) # Weeks "V" -> force_width(2, :iso_weeknum, directive, opts) "W" -> force_width(2, :week_mon, directive, opts) diff --git a/test/format_strftime_test.exs b/test/format_strftime_test.exs index 98777566..5175116d 100644 --- a/test/format_strftime_test.exs +++ b/test/format_strftime_test.exs @@ -96,6 +96,14 @@ defmodule DateFormatTest.FormatStrftime do assert { :ok, " 1" } = format(@jan12015, "%_d") end + test "format %:d" do + assert { :ok, "1st" } = format(@jan12015, "%:d") + assert { :ok, "2nd" } = format(Timex.to_datetime({{2015,1,2}, {0,0,0}}), "%:d") + assert { :ok, "3rd" } = format(Timex.to_datetime({{2015,1,3}, {0,0,0}}), "%:d") + assert { :ok, "4th" } = format(Timex.to_datetime({{2015,1,4}, {0,0,0}}), "%:d") + assert { :ok, "5th" } = format(Timex.to_datetime({{2015,1,5}, {0,0,0}}), "%:d") + end + test "format %e" do assert { :ok, " 1" } = format(@jan12015, "%e") assert { :ok, "1" } = format(@jan12015, "%-e")