elixir-cldr / cldr_calendars

Calendar functions for CLDR
Other
15 stars 6 forks source link

Not an issue but a question re time periods #11

Closed DaTrader closed 2 years ago

DaTrader commented 2 years ago

Hey Kip,

looking at the API over and over again, I can't seem to find a way to format a duration (a time period) regardless of when it starts or ends e.g.:

2 days 3 weeks 2 quarters

Can you help with this one?

Thanks,

Damir

kipcole9 commented 2 years ago

ex_cldr_units is (mostly) your friend here. These are considered as time units with the emphasis on unit which is why they occur in that particular library. Some examples:

iex> MyApp.Cldr.Unit.to_string Cldr.Unit.new!(2, :week)
{:ok, "2 weeks"}

iex> MyApp.Cldr.Unit.to_string Cldr.Unit.new!(2, :week), locale: "fr"
{:ok, "2 semaines"}

At present, CLDR knows about the following time units:

iex> Cldr.Unit.known_units_by_category |> Map.get(:duration)
[:century, :day, :decade, :hour, :microsecond, :millisecond, :minute, :month,
 :nanosecond, :second, :week, :year]

Which means that :quarter isn't a known unit unfortunately. I will submit a ticket to CLDR but I wouldn't expect a quick outcome.

As an aside, in ex_cldr_calendars there is a module Cldr.Calendar.Duration that will calculate the difference between two dates/date_times and it will also use Cldr.Unit.to_string/1 for formatting if ex_cldr_units is configured. For example:

iex> {:ok, duration} = Cldr.Calendar.Duration.new(~D[2020-10-19], ~D[2021-12-01])
{:ok,
 %Cldr.Calendar.Duration{
   day: 13,
   hour: 0,
   microsecond: 0,
   minute: 0,
   month: 1,
   second: 0,
   year: 1
 }}
iex> Cldr.Calendar.Duration.to_string(duration)                                  
{:ok, "1 year, 1 month, and 13 days"}
DaTrader commented 2 years ago

Thanks, Kip!

Just so you know that little by little your libraries are becoming invaluable to my app.

kipcole9 commented 2 years ago

Thanks for the encouragement, definitely helps with motivation to know the work is useful!

DaTrader commented 2 years ago

Kip, just to let you known I'm getting the following warning when compiling the deps (I'm still on Elixir 1.10.3).

warning: Date.to_gregorian_days/1 is undefined or private
  lib/cldr/calendar/behaviour.ex:6: Cldr.Calendar.Behaviour.__using__/1

And Merry Christmas.

kipcole9 commented 2 years ago

There is a a solution for days and weeks, but not quarters. It requires using the ex_cldr_units library.

iex> days = Cldr.Unit.new!(:day, 2)
#Cldr.Unit<:day, 2>
iex> MyApp.Cldr.Unit.to_string days
{:ok, "2 days"}

iex> weeks = Cldr.Unit.new!(:week, 2)
#Cldr.Unit<:week, 2>
iex> MyApp.Cldr.Unit.to_string weeks 
{:ok, "2 weeks"}
iex> MyApp.Cldr.Unit.to_string weeks, locale: "fr"
{:ok, "2 semaines"}
iex> MyApp.Cldr.Unit.to_string weeks, locale: "de"
{:ok, "2 Wochen"}
iex> MyApp.Cldr.Unit.to_string weeks, locale: "he"
{:ok, "שבועיים"}

I have opened this issue to add a quarter unit type on CLDR but that's not likely to happen quickly.

There may be a way to format quarters using ex_cldr_messages or perhaps gettext. I'll have to think on that a bit.