Packages
timex
3.1.13
3.7.13
3.7.12
3.7.11
3.7.9
3.7.8
3.7.7
3.7.6
3.7.5
3.7.3
3.7.2
3.7.1
3.7.0
3.6.4
3.6.3
3.6.2
retired
3.6.1
3.6.0
3.5.0
3.4.2
3.4.1
3.3.0
3.2.2
3.2.1
3.2.0
3.1.25
retired
3.1.24
3.1.23
3.1.22
3.1.21
3.1.20
3.1.19
3.1.18
3.1.17
3.1.16
3.1.15
3.1.13
3.1.12
3.1.11
3.1.10
3.1.9
3.1.8
3.1.7
3.1.6
3.1.5
3.1.4
3.1.3
3.1.2
3.1.1
3.1.0
3.0.8
3.0.7
3.0.6
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
2.2.1
2.1.6
2.1.5
2.1.4
2.1.3
2.1.2
2.1.1
2.1.0
2.0.0
1.0.2
1.0.1
1.0.0
1.0.0-rc4
1.0.0-rc3
1.0.0-rc2
1.0.0-rc1
1.0.0-pre
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.2
0.18.1
0.18.0
0.17.0
0.16.2
0.16.1
0.16.0
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.13.5
0.13.4
0.13.3
0.13.2
0.13.1
0.13.0
0.12.9
0.12.8
0.12.7
0.12.6
0.12.5
0.12.4
0.12.3
0.12.2
0.12.1
0.12.0
0.11.0
0.10.2
0.10.1
0.10.0
0.9.0
0.8.0
0.7.1
0.6.0
0.5.0
0.4.8
0.4.7
0.4.6
Timex is a rich, comprehensive Date/Time library for Elixir projects, with full timezone support via the :tzdata package. If you need to manipulate dates, times, datetimes, timestamps, etc., then Timex is for you!
Current section
Files
Jump to
Current section
Files
lib/format/datetime/formatter.ex
defmodule Timex.Format.DateTime.Formatter do
@moduledoc """
This module defines the behaviour for custom DateTime formatters.
"""
alias Timex.{Timezone, Translator, Types}
alias Timex.Translator
alias Timex.Format.FormatError
alias Timex.Format.DateTime.Formatters.{Default, Strftime, Relative}
alias Timex.Parse.DateTime.Tokenizers.Directive
@callback tokenize(format_string :: String.t)
:: {:ok, [Directive.t]} | {:error, term}
@callback format(date :: Types.calendar_types, format_string :: String.t)
:: {:ok, String.t} | {:error, term}
@callback format!(date :: Types.calendar_types, format_string :: String.t)
:: String.t | no_return
@callback lformat(date :: Types.calendar_types, format_string :: String.t, locale :: String.t)
:: {:ok, String.t} | {:error, term}
@callback lformat!(date :: Types.calendar_types, format_string :: String.t, locale :: String.t)
:: String.t | no_return
@doc false
defmacro __using__(_opts) do
quote do
@behaviour Timex.Format.DateTime.Formatter
alias Timex.Parse.DateTime.Tokenizers.Directive
import Timex.Format.DateTime.Formatter, only: [format_token: 5, format_token: 6]
end
end
@doc """
Formats a Date, DateTime, or NaiveDateTime as a string, using the provided format string,
locale, and formatter. If the locale does not have translations, "en" will be used by
default. If a formatter is not provided, the formatter used is `Timex.Format.DateTime.Formatters.DefaultFormatter`
If an error is encountered during formatting, `lformat!` will raise
"""
@spec lformat!(Types.calendar_types, String.t, String.t, atom | nil) :: String.t | no_return
def lformat!(date, format_string, locale, formatter \\ Default)
def lformat!({:error, _} = err, _format_string, _locale, _formatter),
do: err
def lformat!(datetime, format_string, locale, :strftime),
do: lformat!(datetime, format_string, locale, Strftime)
def lformat!(datetime, format_string, locale, :relative),
do: lformat!(datetime, format_string, locale, Relative)
def lformat!(datetime, format_string, locale, formatter)
when is_tuple(datetime),
do: lformat!(Timex.to_naive_datetime(datetime), format_string, locale, formatter)
def lformat!(date, format_string, locale, formatter)
when is_binary(format_string) and is_binary(locale) and is_atom(formatter)
do
case formatter.lformat(date, format_string, locale) do
{:ok, result} -> result
{:error, reason} -> raise FormatError, message: reason
end
end
def lformat!(a,b,c,d),
do: raise "invalid argument(s) to lformat!/4: #{inspect a}, #{inspect b}, #{inspect c}, #{inspect d}"
@doc """
Formats a Date, DateTime, or NaiveDateTime as a string, using the provided format string,
locale, and formatter. If the locale provided does not have translations, "en" is used by
default. If a formatter is not provided, the formatter used is `Timex.Format.DateTime.Formatters.DefaultFormatter`
"""
@spec lformat(Types.calendar_types, String.t, String.t, atom | nil) :: {:ok, String.t} | {:error, term}
def lformat(date, format_string, locale, formatter \\ Default)
def lformat({:error, _} = err, _format_string, _locale, _formatter),
do: err
def lformat(datetime, format_string, locale, :strftime),
do: lformat(datetime, format_string, locale, Strftime)
def lformat(datetime, format_string, locale, :relative),
do: lformat(datetime, format_string, locale, Relative)
def lformat(date, format_string, locale, formatter)
when is_binary(format_string) and is_binary(locale) and is_atom(formatter) do
try do
{:ok, lformat!(date, format_string, locale, formatter)}
catch
_type, %FormatError{:message => msg} ->
{:error, msg}
_type, %{:message => msg} ->
{:error, msg}
_type, reason ->
{:error, reason}
end
end
def lformat(_, _, _, _),
do: {:error, :badarg}
@doc """
Formats a Date, DateTime, or NaiveDateTime as a string, using the provided format
string and formatter. If a formatter is not provided, the formatter
used is `Timex.Format.DateTime.Formatters.DefaultFormatter`.
Formatting will use the configured default locale, "en" if no other default is given.
If an error is encountered during formatting, `format!` will raise.
"""
@spec format!(Types.calendar_types, String.t, atom | nil) :: String.t | no_return
def format!(date, format_string, formatter \\ Default)
def format!(date, format_string, formatter),
do: lformat!(date, format_string, Translator.default_locale, formatter)
@doc """
Formats a Date, DateTime, or NaiveDateTime as a string, using the provided format
string and formatter. If a formatter is not provided, the formatter
used is `Timex.Format.DateTime.Formatters.DefaultFormatter`.
Formatting will use the configured default locale, "en" if no other default is given.
"""
@spec format(Types.calendar_types, String.t, atom | nil) :: {:ok, String.t} | {:error, term}
def format(date, format_string, formatter \\ Default)
def format(datetime, format_string, :strftime),
do: lformat(datetime, format_string, Translator.default_locale, Strftime)
def format(datetime, format_string, :relative),
do: lformat(datetime, format_string, Translator.default_locale, Relative)
def format(datetime, format_string, formatter),
do: lformat(datetime, format_string, Translator.default_locale, formatter)
@doc """
Validates the provided format string, using the provided formatter,
or if none is provided, the default formatter. Returns `:ok` when valid,
or `{:error, reason}` if not valid.
"""
@spec validate(String.t, atom | nil) :: :ok | {:error, term}
def validate(format_string, formatter \\ Default)
def validate(format_string, formatter) when is_binary(format_string) and is_atom(formatter) do
try do
formatter = case formatter do
:strftime -> Strftime
:relative -> Relative
_ -> formatter
end
case formatter.tokenize(format_string) do
{:error, _} = error -> error
{:ok, []} -> {:error, "There were no formatting directives in the provided string."}
{:ok, directives} when is_list(directives)-> :ok
end
rescue
x -> {:error, x}
end
end
def validate(_, _), do: {:error, :badarg}
@doc """
Given a token (as found in `Timex.Parsers.Directive`), and a Date, DateTime, or NaiveDateTime struct,
produce a string representation of the token using values from the struct, using the default locale.
"""
@spec format_token(atom, Types.calendar_types, list(), list(), list()) :: String.t | {:error, term}
def format_token(token, date, modifiers, flags, width) do
format_token(Translator.default_locale, token, date, modifiers, flags, width)
end
@doc """
Given a token (as found in `Timex.Parsers.Directive`), and a Date, DateTime, or NaiveDateTime struct,
produce a string representation of the token using values from the struct.
"""
@spec format_token(String.t, atom, Types.calendar_types, list(), list(), list()) :: String.t | {:error, term}
def format_token(locale, token, date, modifiers, flags, width)
# Formats
def format_token(locale, :iso_date, date, modifiers, _flags, _width) do
flags = [padding: :zeroes]
year = format_token(locale, :year4, date, modifiers, flags, width_spec(4..4))
month = format_token(locale, :month, date, modifiers, flags, width_spec(2..2))
day = format_token(locale, :day, date, modifiers, flags, width_spec(2..2))
"#{year}-#{month}-#{day}"
end
def format_token(locale, :iso_time, date, modifiers, _flags, _width) do
flags = [padding: :zeroes]
hour = format_token(locale, :hour24, date, modifiers, flags, width_spec(2..2))
minute = format_token(locale, :min, date, modifiers, flags, width_spec(2..2))
sec = format_token(locale, :sec, date, modifiers, flags, width_spec(2..2))
ms = format_token(locale, :sec_fractional, date, modifiers, flags, width_spec(-1, nil))
"#{hour}:#{minute}:#{sec}#{ms}"
end
def format_token(locale, token, date, modifiers, _flags, _width)
when token in [:iso_8601_extended, :iso_8601_extended_z] do
date = case token do
:iso_8601_extended -> date
:iso_8601_extended_z -> Timezone.convert(date, "UTC")
end
flags = [padding: :zeroes]
year = format_token(locale, :year4, date, modifiers, flags, width_spec(4..4))
month = format_token(locale, :month, date, modifiers, flags, width_spec(2..2))
day = format_token(locale, :day, date, modifiers, flags, width_spec(2..2))
hour = format_token(locale, :hour24, date, modifiers, flags, width_spec(2..2))
min = format_token(locale, :min, date, modifiers, flags, width_spec(2..2))
sec = format_token(locale, :sec, date, modifiers, flags, width_spec(2..2))
ms = format_token(locale, :sec_fractional, date, modifiers, flags, width_spec(-1, nil))
case token do
:iso_8601_extended ->
tz = format_token(locale, :zoffs_colon, date, modifiers, flags, width_spec(-1, nil))
"#{year}-#{month}-#{day}T#{hour}:#{min}:#{sec}#{ms}#{tz}"
:iso_8601_extended_z ->
"#{year}-#{month}-#{day}T#{hour}:#{min}:#{sec}#{ms}Z"
end
end
def format_token(locale, token, date, modifiers, _flags, _width)
when token in [:iso_8601_basic, :iso_8601_basic_z] do
date = case token do
:iso_8601_basic -> date
:iso_8601_basic_z -> Timezone.convert(date, "UTC")
end
flags = [padding: :zeroes]
year = format_token(locale, :year4, date, modifiers, flags, width_spec(4..4))
month = format_token(locale, :month, date, modifiers, flags, width_spec(2..2))
day = format_token(locale, :day, date, modifiers, flags, width_spec(2..2))
hour = format_token(locale, :hour24, date, modifiers, flags, width_spec(2..2))
min = format_token(locale, :min, date, modifiers, flags, width_spec(2..2))
sec = format_token(locale, :sec, date, modifiers, flags, width_spec(2..2))
ms = format_token(locale, :sec_fractional, date, modifiers, flags, width_spec(-1, nil))
case token do
:iso_8601_basic ->
tz = format_token(locale, :zoffs, date, modifiers, flags, width_spec(-1, nil))
"#{year}#{month}#{day}T#{hour}#{min}#{sec}#{ms}#{tz}"
:iso_8601_basic_z ->
"#{year}#{month}#{day}T#{hour}#{min}#{sec}#{ms}Z"
end
end
def format_token(locale, token, date, modifiers, _flags, _width)
when token in [:rfc_822, :rfc_822z] do
# Mon, 05 Jun 14 23:20:59 +0200
date = case token do
:rfc_822 -> date
:rfc_822z -> Timezone.convert(date, "UTC")
end
flags = [padding: :zeroes]
year = format_token(locale, :year2, date, modifiers, flags, width_spec(2..2))
month = format_token(locale, :mshort, date, modifiers, flags, width_spec(-1, nil))
day = format_token(locale, :day, date, modifiers, flags, width_spec(2..2))
hour = format_token(locale, :hour24, date, modifiers, flags, width_spec(2..2))
min = format_token(locale, :min, date, modifiers, flags, width_spec(2..2))
sec = format_token(locale, :sec, date, modifiers, flags, width_spec(2..2))
wday = format_token(locale, :wdshort, date, modifiers, flags, width_spec(-1, nil))
case token do
:rfc_822 ->
tz = format_token(locale, :zoffs, date, modifiers, flags, width_spec(-1, nil))
"#{wday}, #{day} #{month} #{year} #{hour}:#{min}:#{sec} #{tz}"
:rfc_822z ->
"#{wday}, #{day} #{month} #{year} #{hour}:#{min}:#{sec} Z"
end
end
def format_token(locale, token, date, modifiers, _flags, _width)
when token in [:rfc_1123, :rfc_1123z] do
# `Tue, 05 Mar 2013 23:25:19 GMT`
date = case token do
:rfc_1123 -> date
:rfc_1123z -> Timezone.convert(date, "UTC")
end
flags = [padding: :zeroes]
year = format_token(locale, :year4, date, modifiers, flags, width_spec(4..4))
month = format_token(locale, :mshort, date, modifiers, flags, width_spec(-1, nil))
day = format_token(locale, :day, date, modifiers, flags, width_spec(2..2))
hour = format_token(locale, :hour24, date, modifiers, flags, width_spec(2..2))
min = format_token(locale, :min, date, modifiers, flags, width_spec(2..2))
sec = format_token(locale, :sec, date, modifiers, flags, width_spec(2..2))
wday = format_token(locale, :wdshort, date, modifiers, flags, width_spec(-1, nil))
case token do
:rfc_1123 ->
tz = format_token(locale, :zoffs, date, modifiers, flags, width_spec(-1, nil))
"#{wday}, #{day} #{month} #{year} #{hour}:#{min}:#{sec} #{tz}"
:rfc_1123z ->
"#{wday}, #{day} #{month} #{year} #{hour}:#{min}:#{sec} Z"
end
end
def format_token(locale, token, date, modifiers, _flags, _width)
when token in [:rfc_3339, :rfc_3339z] do
# `2013-03-05T23:25:19+02:00`
date = case token do
:rfc_3339 -> date
:rfc_3339z -> Timezone.convert(date, "UTC")
end
flags = [padding: :zeroes]
year = format_token(locale, :year4, date, modifiers, flags, width_spec(4..4))
month = format_token(locale, :month, date, modifiers, flags, width_spec(2..2))
day = format_token(locale, :day, date, modifiers, flags, width_spec(2..2))
hour = format_token(locale, :hour24, date, modifiers, flags, width_spec(2..2))
min = format_token(locale, :min, date, modifiers, flags, width_spec(2..2))
sec = format_token(locale, :sec, date, modifiers, flags, width_spec(2..2))
ms = format_token(locale, :sec_fractional, date, modifiers, flags, width_spec(-1, nil))
case token do
:rfc_3339 ->
tz = format_token(locale, :zoffs_colon, date, modifiers, flags, width_spec(-1, nil))
"#{year}-#{month}-#{day}T#{hour}:#{min}:#{sec}#{ms}#{tz}"
:rfc_3339z ->
"#{year}-#{month}-#{day}T#{hour}:#{min}:#{sec}#{ms}Z"
end
end
def format_token(locale, :unix, date, modifiers, _flags, _width) do
# Tue Mar 5 23:25:19 PST 2013`
flags = [padding: :zeroes]
year = format_token(locale, :year4, date, modifiers, [padding: :spaces], width_spec(4..4))
month = format_token(locale, :mshort, date, modifiers, flags, width_spec(-1, nil))
day = format_token(locale, :day, date, modifiers, [padding: :spaces], width_spec(2..2))
hour = format_token(locale, :hour24, date, modifiers, [padding: :zeroes], width_spec(2..2))
min = format_token(locale, :min, date, modifiers, [padding: :zeroes], width_spec(2..2))
sec = format_token(locale, :sec, date, modifiers, [padding: :zeroes], width_spec(2..2))
wday = format_token(locale, :wdshort, date, modifiers, flags, width_spec(-1, nil))
tz = format_token(locale, :zabbr, date, modifiers, flags, width_spec(-1, nil))
"#{wday} #{month} #{day} #{hour}:#{min}:#{sec} #{tz} #{year}"
end
def format_token(locale, :ansic, date, modifiers, flags, _width) do
# Tue Mar 5 23:25:19 2013`
year = format_token(locale, :year4, date, modifiers, [padding: :spaces], width_spec(4..4))
month = format_token(locale, :mshort, date, modifiers, flags, width_spec(-1, nil))
day = format_token(locale, :day, date, modifiers, [padding: :spaces], width_spec(2..2))
hour = format_token(locale, :hour24, date, modifiers, [padding: :zeroes], width_spec(2..2))
min = format_token(locale, :min, date, modifiers, [padding: :zeroes], width_spec(2..2))
sec = format_token(locale, :sec, date, modifiers, [padding: :zeroes], width_spec(2..2))
wday = format_token(locale, :wdshort, date, modifiers, flags, width_spec(-1, nil))
"#{wday} #{month} #{day} #{hour}:#{min}:#{sec} #{year}"
end
def format_token(locale, :asn1_utc_time, date, modifiers, _flags, _width) do
# `130305232519Z`
date = Timezone.convert(date, "UTC")
flags = [padding: :zeroes]
year = format_token(locale, :year2, date, modifiers, flags, width_spec(2..2))
month = format_token(locale, :month, date, modifiers, flags, width_spec(2..2))
day = format_token(locale, :day, date, modifiers, flags, width_spec(2..2))
hour = format_token(locale, :hour24, date, modifiers, flags, width_spec(2..2))
min = format_token(locale, :min, date, modifiers, flags, width_spec(2..2))
sec = format_token(locale, :sec, date, modifiers, flags, width_spec(2..2))
"#{year}#{month}#{day}#{hour}#{min}#{sec}Z"
end
def format_token(locale, :asn1_generalized_time, date, modifiers, _flags, _width) do
# `130305232519`
flags = [padding: :zeroes]
year = format_token(locale, :year4, date, modifiers, flags, width_spec(4..4))
month = format_token(locale, :month, date, modifiers, flags, width_spec(2..2))
day = format_token(locale, :day, date, modifiers, flags, width_spec(2..2))
hour = format_token(locale, :hour24, date, modifiers, flags, width_spec(2..2))
min = format_token(locale, :min, date, modifiers, flags, width_spec(2..2))
sec = format_token(locale, :sec, date, modifiers, flags, width_spec(2..2))
"#{year}#{month}#{day}#{hour}#{min}#{sec}"
end
def format_token(locale, :asn1_generalized_time_z, date, modifiers, flags, width) do
# `130305232519Z`
date = Timezone.convert(date, "UTC")
base = format_token(locale, :asn1_generalized_time, date, modifiers, flags, width)
base <> "Z"
end
def format_token(locale, :asn1_generalized_time_tz, date, modifiers, flags, width) do
# `130305232519-0500`
offset = format_token(locale, :zoffs, date, modifiers, flags, width)
base = format_token(locale, :asn1_generalized_time, date, modifiers, flags, width)
base <> offset
end
def format_token(locale, :kitchen, date, modifiers, _flags, _width) do
# `3:25PM`
hour = format_token(locale, :hour12, date, modifiers, [], width_spec(2..2))
min = format_token(locale, :min, date, modifiers, [padding: :zeroes], width_spec(2..2))
ampm = format_token(locale, :AM, date, modifiers, [], width_spec(-1, nil))
"#{hour}:#{min}#{ampm}"
end
def format_token(locale, :slashed, date, modifiers, _flags, _width) do
# `04/12/1987`
flags = [padding: :zeroes]
year = format_token(locale, :year2, date, modifiers, flags, width_spec(2..2))
month = format_token(locale, :month, date, modifiers, flags, width_spec(2..2))
day = format_token(locale, :day, date, modifiers, flags, width_spec(2..2))
"#{month}/#{day}/#{year}"
end
def format_token(locale, token, date, modifiers, _flags, _width)
when token in [:strftime_iso_clock, :strftime_iso_clock_full] do
# `23:30:05`
flags = [padding: :zeroes]
hour = format_token(locale, :hour24, date, modifiers, flags, width_spec(2..2))
min = format_token(locale, :min, date, modifiers, flags, width_spec(2..2))
case token do
:strftime_iso_clock -> "#{hour}:#{min}"
:strftime_iso_clock_full ->
sec = format_token(locale, :sec, date, modifiers, flags, width_spec(2..2))
"#{hour}:#{min}:#{sec}"
end
end
def format_token(locale, :strftime_kitchen, date, modifiers, _flags, _width) do
# `04:30:01 PM`
hour = format_token(locale, :hour12, date, modifiers, [padding: :zeroes], width_spec(2..2))
min = format_token(locale, :min, date, modifiers, [padding: :zeroes], width_spec(2..2))
sec = format_token(locale, :sec, date, modifiers, [padding: :zeroes], width_spec(2..2))
ampm = format_token(locale, :AM, date, modifiers, [], width_spec(-1, nil))
"#{hour}:#{min}:#{sec} #{ampm}"
end
def format_token(locale, :strftime_iso_shortdate, date, modifiers, _flags, _width) do
# ` 5-Jan-2014`
flags = [padding: :zeroes]
year = format_token(locale, :year4, date, modifiers, flags, width_spec(4..4))
month = format_token(locale, :mshort, date, modifiers, flags, width_spec(-1, nil))
day = format_token(locale, :day, date, modifiers, [padding: :spaces], width_spec(2..2))
"#{day}-#{month}-#{year}"
end
def format_token(locale, :iso_week, date, modifiers, _flags, _width) do
# 2015-W04
flags = [padding: :zeroes]
year = format_token(locale, :year4, date, modifiers, flags, width_spec(4..4))
week = format_token(locale, :iso_weeknum, date, modifiers, flags, width_spec(2..2))
"#{year}-W#{week}"
end
def format_token(locale, :iso_weekday, date, modifiers, _flags, _width) do
# 2015-W04-1
flags = [padding: :zeroes]
year = format_token(locale, :year4, date, modifiers, flags, width_spec(4..4))
week = format_token(locale, :iso_weeknum, date, modifiers, flags, width_spec(2..2))
day = format_token(locale, :wday_mon, date, modifiers, flags, width_spec(1, 1))
"#{year}-W#{week}-#{day}"
end
def format_token(locale, :iso_ordinal, date, modifiers, _flags, _width) do
# 2015-180
flags = [padding: :zeroes]
year = format_token(locale, :year4, date, modifiers, flags, width_spec(4..4))
day = format_token(locale, :oday, date, modifiers, flags, width_spec(3..3))
"#{year}-#{day}"
end
# Years
def format_token(_locale, :year4, date, _modifiers, flags, width),
do: pad_numeric(date.year, flags, width)
def format_token(_locale, :year2, date, _modifiers, flags, width),
do: pad_numeric(rem(date.year, 100), flags, width)
def format_token(_locale, :century, date, _modifiers, flags, width),
do: pad_numeric(div(date.year, 100), flags, width)
def format_token(_locale, :iso_year4, date, _modifiers, flags, width) do
{iso_year, _} = Timex.iso_week(date)
pad_numeric(iso_year, flags, width)
end
def format_token(_locale, :iso_year2, date, _modifiers, flags, width) do
{iso_year, _} = Timex.iso_week(date)
pad_numeric(rem(iso_year, 100), flags, width)
end
# Months
def format_token(_locale, :month, date, _modifiers, flags, width),
do: pad_numeric(date.month, flags, width)
def format_token(locale, :mshort, date, _, _, _) do
months = Translator.get_months_abbreviated(locale)
Map.get(months, date.month)
end
def format_token(locale, :mfull, date, _, _, _) do
months = Translator.get_months(locale)
Map.get(months, date.month)
end
# Days
def format_token(_locale, :day, date, _modifiers, flags, width),
do: pad_numeric(date.day, flags, width)
def format_token(_locale, :oday, date, _modifiers, flags, width),
do: pad_numeric(Timex.day(date), flags, width)
# Weeks
def format_token(_locale, :iso_weeknum, date, _modifiers, flags, width) do
{_, week} = Timex.iso_week(date)
pad_numeric(week, flags, width)
end
def format_token(_locale, :week_mon, %{:year => year} = date, _modifiers, flags, width) do
{:ok, jan1} = Date.new(year,1,1)
Timex.Interval.new(from: jan1, until: Timex.shift(date, days: 1))
|> Enum.reduce(0, fn d, acc ->
case Timex.weekday(d) do
1 -> acc+1
_ -> acc
end
end)
|> pad_numeric(flags, width)
end
def format_token(_locale, :week_sun, %{:year => year} = date, _modifiers, flags, width) do
{:ok, jan1} = Date.new(year,1,1)
Timex.Interval.new(from: jan1, until: Timex.shift(date, days: 1))
|> Enum.reduce(0, fn d, acc ->
case Timex.weekday(d) do
7 -> acc+1
_ -> acc
end
end)
|> pad_numeric(flags, width)
end
def format_token(_locale, :wday_mon, date, _modifiers, flags, width),
do: pad_numeric(Timex.weekday(date), flags, width)
def format_token(_locale, :wday_sun, date, _modifiers, flags, width) do
# from 1..7 to 0..6
weekday = case Timex.weekday(date) do
7 -> 0
day -> day
end
pad_numeric(weekday, flags, width)
end
def format_token(locale, :wdshort, date, _modifiers, _flags, _width) do
day = Timex.weekday(date)
day_names = Translator.get_weekdays_abbreviated(locale)
Map.get(day_names, day)
end
def format_token(locale, :wdfull, date, _modifiers, _flags, _width) do
day = Timex.weekday(date)
day_names = Translator.get_weekdays(locale)
Map.get(day_names, day)
end
# Hours
def format_token(_locale, :hour24, %{:hour => hour}, _modifiers, flags, width),
do: pad_numeric(hour, flags, width)
def format_token(_locale, :hour24, _date, _modifiers, flags, width),
do: pad_numeric(0, flags, width)
def format_token(_locale, :hour12, %{:hour => hour}, _modifiers, flags, width) do
{h, _} = Timex.Time.to_12hour_clock(hour)
pad_numeric(h, flags, width)
end
def format_token(_locale, :hour12, _date, _modifiers, flags, width) do
{h, _} = Timex.Time.to_12hour_clock(0)
pad_numeric(h, flags, width)
end
def format_token(_locale, :min, %{:minute => min}, _modifiers, flags, width),
do: pad_numeric(min, flags, width)
def format_token(_locale, :min, _date, _modifiers, flags, width),
do: pad_numeric(0, flags, width)
def format_token(_locale, :sec, %{:second => sec}, _modifiers, flags, width),
do: pad_numeric(sec, flags, width)
def format_token(_locale, :sec, _date, _modifiers, flags, width),
do: pad_numeric(0, flags, width)
def format_token(_locale, :sec_fractional, %{microsecond: {us, precision}}, _modifiers, _flags, _width) when precision > 0 do
padded = pad_numeric(us, [padding: :zeroes], width_spec(6..6))
".#{:erlang.binary_part(padded, 0, precision)}"
end
def format_token(_locale, :sec_fractional, _date, _modifiers, _flags, _width),
do: ""
def format_token(_locale, :sec_epoch, date, _modifiers, flags, width) do
case get_in(flags, [:padding]) do
padding when padding in [:zeroes, :spaces] ->
{:error, {:formatter, "Invalid directive flag: Cannot pad seconds from epoch, as it is not a fixed width integer."}}
_ ->
pad_numeric(Timex.to_unix(date), flags, width)
end
end
def format_token(_locale, :us, %{microsecond: {us, precision}}, _modifiers, flags, width) do
p = cond do
width > precision -> precision
:else -> width
end
pad_numeric(us, flags, width_spec(p..p))
end
def format_token(_locale, :us, _date, _modifiers, flags, width) do
pad_numeric(0, flags, width)
end
def format_token(locale, :am, %{hour: hour}, _modifiers, _flags, _width) do
day_periods = Translator.get_day_periods(locale)
{_, am_pm} = Timex.Time.to_12hour_clock(hour)
Map.get(day_periods, am_pm)
end
def format_token(locale, :am, _date, _modifiers, _flags, _width) do
day_periods = Translator.get_day_periods(locale)
{_, am_pm} = Timex.Time.to_12hour_clock(0)
Map.get(day_periods, am_pm)
end
def format_token(locale, :AM, %{hour: hour}, _modifiers, _flags, _width) do
day_periods = Translator.get_day_periods(locale)
case Timex.Time.to_12hour_clock(hour) do
{_, :am} ->
Map.get(day_periods, :AM)
{_, :pm} ->
Map.get(day_periods, :PM)
end
end
def format_token(locale, :AM, _date, _modifiers, _flags, _width) do
day_periods = Translator.get_day_periods(locale)
case Timex.Time.to_12hour_clock(0) do
{_, :am} ->
Map.get(day_periods, :AM)
{_, :pm} ->
Map.get(day_periods, :PM)
end
end
# Timezones
def format_token(_locale, :zname, %{time_zone: tz}, _modifiers, _flags, _width),
do: tz
def format_token(_locale, :zname, _date, _modifiers, _flags, _width),
do: ""
def format_token(_locale, :zabbr, %{zone_abbr: abbr}, _modifiers, _flags, _width),
do: abbr
def format_token(_locale, :zabbr, _date, _modifiers, _flags, _width),
do: ""
def format_token(_locale, :zoffs, %{std_offset: std, utc_offset: utc}, _modifiers, flags, _width) do
case get_in(flags, [:padding]) do
padding when padding in [:spaces, :none] ->
{:error, {:formatter, "Invalid directive flag: Timezone offsets require 0-padding to remain unambiguous."}}
_ ->
total_offset = Timezone.total_offset(std, utc)
offset_hours = div(total_offset, 60 * 60)
offset_mins = div(rem(total_offset, 60 * 60), 60)
hour = pad_numeric(offset_hours, [padding: :zeroes], width_spec(2..2))
min = pad_numeric(offset_mins, [padding: :zeroes], width_spec(2..2))
cond do
(offset_hours + offset_mins) >= 0 -> "+#{hour}#{min}"
true -> "#{hour}#{min}"
end
end
end
def format_token(_locale, :zoffs, _date, _modifiers, _flags, _width),
do: ""
def format_token(locale, :zoffs_colon, date, modifiers, flags, width) do
case format_token(locale, :zoffs, date, modifiers, flags, width) do
{:error, _} = err -> err
"" -> ""
offset ->
[qualifier, <<hour::binary-size(2), min::binary-size(2)>>] = String.split(offset, "", [trim: true, parts: 2])
<<qualifier::binary, hour::binary, ?:, min::binary>>
end
end
def format_token(locale, :zoffs_sec, %{std_offset: std, utc_offset: utc} = date, modifiers, flags, width) do
case format_token(locale, :zoffs_colon, date, modifiers, flags, width) do
{:error,_} = err -> err
"" -> ""
offset ->
total_offset = Timezone.total_offset(std, utc)
offset_secs = rem(rem(total_offset, 60*60), 60)
"#{offset}:#{pad_numeric(offset_secs, [padding: :zeroes], width_spec(2..2))}"
end
end
def format_token(_locale, :zoffs_sec, _date, _modifiers, _flags, _width),
do: ""
def format_token(_locale, token, _, _, _, _) do
{:error, {:formatter, :unsupported_token, token}}
end
defp pad_numeric(number, flags, width) when is_integer(number), do: pad_numeric("#{number}", flags, width)
defp pad_numeric(number_str, [], _width), do: number_str
defp pad_numeric(<<?-, number_str::binary>>, flags, width) do
res = pad_numeric(number_str, flags, width)
<<?-, res::binary>>
end
defp pad_numeric(number_str, flags, [min: min_width, max: max_width]) do
case get_in(flags, [:padding]) do
pad_type when pad_type in [nil, :none] -> number_str
pad_type ->
len = String.length(number_str)
cond do
min_width == -1 && max_width == nil -> number_str
len < min_width && max_width == nil -> String.duplicate(pad_char(pad_type), min_width - len) <> number_str
max_width != nil && len < max_width -> String.duplicate(pad_char(pad_type), max_width - len) <> number_str
true -> number_str
end
end
end
defp pad_char(:zeroes), do: <<?0>>
defp pad_char(:spaces), do: <<32>>
defp width_spec(min..max), do: [min: min, max: max]
defp width_spec(min, max), do: [min: min, max: max]
end