A picker writes its dates in a locale, and can show them in a calendar
system other than the Gregorian. The value does not change with either:
every picker posts ISO 8601 dates (2027-03-15), and the server reads
them as Date values.
<.date_picker field={@form[:due]} label_id="due-label" locale="ar-EG" />
<.date_picker field={@form[:due]} label_id="due-label" locale="ja-JP" calendar="japanese" />Locales
The locale attribute is a BCP 47 tag, such as "en-GB", "pt-PT" or
"ar-EG". Without it, the picker takes config :koyomi, locale:, else
"en". The browser's Intl gives the picker, in that locale:
- the names of the months and the weekdays;
- the digits of the numbers (Arabic-Indic digits in
ar-EG; a-u-nu-extension chooses others, as in"en-u-nu-thai"); - the order of the date segments and their separators (day, month, year
in
en-GB; month, day, year inen-US); - the 12 or 24-hour clock of the time segments;
- the dates written in full, such as the labels that a screen reader reads for each day.
The segments read any Unicode digits that a person types or pastes, and turn them into numbers. See Typing dates.
The week
The first day of the week and the weekend come from the locale's
region, as CLDR gives them. The picker asks the browser
(Intl.Locale.prototype.getWeekInfo), and uses a copy of the CLDR table
when the browser does not have it:
| Region | First day | Weekend |
|---|---|---|
most of Europe (en-GB, fr-FR, de-DE) | Monday | Saturday and Sunday |
en-US, ja-JP, pt-BR, pt-PT | Sunday | Saturday and Sunday |
ar-EG | Saturday | Friday and Saturday |
hi-IN | Sunday | Sunday |
A locale with no region ("en", "pt") gets the region that the browser
adds to it. A -u-fw- extension sets the first day ("en-US-u-fw-mon"),
and the first_day attribute replaces both, as an ISO weekday (1 is
Monday, 7 is Sunday):
<.date_picker field={@form[:due]} locale="en-US" first_day={1} />The week picker always starts its weeks on Monday, as ISO weeks do.
The calendar marks the weekend columns and days with the
koyomi-weekend class, for the theme to style. The weekend does not
disable a day: use the :disabled_weekdays rule for that (see
Rules).
Right to left
A locale written right to left (Arabic, Hebrew, Persian, Urdu and the
other right-to-left scripts) puts dir="rtl" on the picker and on its
calendar. The picker asks the browser for the direction
(Intl.Locale.prototype.getTextInfo), and else reads it from the
locale's script. In the calendar, the left arrow then moves to the next
day and the right arrow to the day before.
Calendar systems
The calendar attribute shows the dates in another calendar system:
<.date_picker field={@form[:due]} locale="he-IL" calendar="hebrew" />
<.date_range_picker field={@form[:stay]} locale="fa-IR" calendar="persian" />The calendar systems are:
| Calendar | Value |
|---|---|
| Gregorian | gregory |
| ISO 8601 | iso8601 |
| Thai Buddhist | buddhist |
| Japanese imperial | japanese |
| Republic of China (Minguo) | roc |
| Persian (Solar Hijri) | persian |
| Islamic | islamic, islamic-umalqura, islamic-civil, islamic-tbla |
| Hebrew | hebrew |
| Chinese | chinese |
| Korean (Dangi) | dangi |
| Indian national | indian |
| Ethiopian | ethiopic |
| Coptic | coptic |
Without the attribute, a -u-ca- extension of the locale chooses the
calendar ("th-TH-u-ca-buddhist"), else it is gregory. A value that is
not in the list, or a calendar that the browser cannot format, falls
back to gregory.
The calendar system changes what the person sees: the month grid, the names of the months, the year, and the dates written in full. A Hebrew month runs from its own first day to its last, and a Hebrew leap year has 13 months. The rules, the presets, the holidays and the posted value stay in ISO dates.
iso8601 is the Gregorian calendar; the picker writes its months as
gregory does. The Gregorian-like systems (gregory, iso8601,
buddhist, japanese, roc) have the Gregorian months, and differ in
their years. The others have months of their own, which the browser
computes, with Temporal when it has it.
The segments
The segments keep the locale's order, and take the numbers of the calendar:
gregory,iso8601andjapanese: the ISO numbers. Injapanese, the segments take2027for the year, while the calendar and the dates written in full show the era (令和9年).- The other calendars: their own numbers. In
buddhistthe year of 2027 is 2570; inhebrew, the month segment takes 1 to 13, and a day that the month does not have is not a date.
Month, year and week pickers
The month, year and week pickers work in Gregorian months and years: a
month picker posts 2027-03, which is not a month of the Hebrew
calendar. With a calendar that is not Gregorian-like, these pickers
use gregory. With buddhist, japanese or roc, they keep the
calendar, whose months are the Gregorian ones.
Week numbers
<.date_picker field={@form[:due]} week_numbers />With week_numbers, the calendar shows a column of ISO 8601 week
numbers, in every locale and every calendar system. ISO weeks start on
Monday, and week 1 of a year is the week that holds its first Thursday,
so each row of the calendar is numbered by the ISO week of its Thursday,
also when the week starts on another day. The week picker always shows them.
On the server, Koyomi.ISOWeek gives the same numbers:
Koyomi.ISOWeek.week(~D[2027-01-01])
#=> {2026, 53}
Koyomi.ISOWeek.to_iso8601(~D[2027-03-15])
#=> "2027-W11"
Koyomi.ISOWeek.monday(2027, 11)
#=> {:ok, ~D[2027-03-15]}weeks_in_year/1 gives 52 or 53, and from_iso8601/1 reads 2027-W11
into the Monday of the week.
Holidays
<.date_picker field={@form[:due]} holidays="PT" />
<.calendar id="team" holidays={["PT", "GB", MyApp.Closures]} months={2} />The holidays attribute marks public holidays in the calendar. It takes
a country code, a provider module, or a list of them. A holiday day has
the koyomi-holiday class, its name as a tooltip, and its name in the
label that a screen reader reads. A holiday is not disabled: add the
days to the rules when they may not be chosen.
The countries
Koyomi.Holidays.countries/0 lists the built-in countries, by ISO 3166-1
alpha-2 code, in any case:
AR, AT, AU, BE, BR, CA, CH, DE, DK, ES, FI, FR, GB, IE, IN, IT, JP, MX, NL, NO, NZ, PL, PT, SE, US, ZA.
The holidays are computed by rule, with no data to download. They are
the national public holidays: the days off that the law gives in the
whole country. Regional and local days, and days off by custom (such as
Christmas Eve in many countries), are not listed. The module of each
country, such as Koyomi.Holidays.PT, says what it keeps and leaves out.
The rules are those in force in 2026. A holiday that began or ended since 2010 is listed only in the years it was kept; earlier years follow the rules of 2010. One-off holidays are not listed.
Where the law gives another day off for a holiday on a weekend, the holiday stays on its date, and a second entry marks the day off ("Christmas Day (substitute day)").
The names
Each holiday has a name in the main language of its country, and an
English name. The picker shows the first to a locale in that language,
and the English name to the others: Natal in pt-PT and pt-BR,
Christmas Day in en-GB. Two holidays on one day are joined with
·.
In code
Koyomi.Holidays.list("PT", 2027)
#=> [%Koyomi.Holiday{date: ~D[2027-01-01], name: "Ano Novo", english: "New Year's Day", country: "PT"}, ...]
Koyomi.Holidays.between(["PT", "DE"], Date.range(~D[2027-12-24], ~D[2028-01-01]))
Koyomi.Holidays.to_client(["PT", "DE"], Date.range(~D[2027-12-25], ~D[2027-12-25]), "pt-PT")
#=> %{"2027-12-25" => "Natal · Christmas Day"}An unknown country code raises ArgumentError, with the list of codes.
A provider of your own
A module that implements Koyomi.Holidays.Provider gives the holidays
of a region, or the days a business is closed. holidays/1 returns the
holidays of a year as {date, name, english} tuples:
defmodule MyApp.Closures do
@behaviour Koyomi.Holidays.Provider
@impl true
def holidays(year) do
[{Date.new!(year, 12, 24), "Fermeture", "Office closed"}]
end
@impl true
def language, do: "fr"
endlanguage/0 is optional: it says which locales see name, and the
others see english. A provider without it always shows name.
country/0, also optional, sets the country of its
Koyomi.Holiday structs. Koyomi.Holidays.Rules has the date rules that
the built-in countries use: easter/1, nth_weekday/4, on_or_after/2,
on_or_before/2 and substitute_days/2.
The years in the page
The component computes the holidays on the server, and puts them in the page as a map from ISO date to name. It covers whole years:
- from two years before today, or from the year of the
:minrule; - to three years after today, or to the year of the
:maxrule; - never more than ten years before or after today.
A calendar shown outside these years has no holidays marked. Give the
rules a :min and a :max to choose the years, for example for a
picker of past dates.