# `Offlixir`
[🔗](https://github.com/F0lamie/Offlixir/blob/v0.1.0/lib/offlixir.ex#L1)

Public holidays of European countries, computed offline.

Offlixir keeps the public holidays that apply to the whole country.

Every date returned is a `Date` struct of the `Calendar.ISO` calendar.

A country is an ISO 3166-1 alpha-2 code in upper case, as an atom (`:FR`) or as
a string (`"FR"`). A year is an integer between 1 and 9999.

## Observed dates

Some countries move a holiday that falls on a week-end to a working day. A
`Offlixir.Holiday` carries both the calendar date (`:date`) and the day
actually taken off (`:observed_date`). The functions that return or test plain
dates (`dates!/2`, `dates_between!/3`, `holiday?/2`) use the observed date.

# `country`

```elixir
@type country() :: atom() | String.t()
```

# `day`

```elixir
@type day() :: Date.t() | NaiveDateTime.t() | DateTime.t()
```

# `reason`

```elixir
@type reason() ::
  {:unknown_country, term()}
  | {:invalid_year, term()}
  | {:invalid_range, {Date.t(), Date.t()}}
```

# `countries`

```elixir
@spec countries() :: [atom()]
```

Returns the supported countries, as upper case atoms in alphabetical order.

    iex> :FR in Offlixir.countries()
    true

# `dates`

```elixir
@spec dates(country(), integer()) :: {:ok, [Date.t()]} | {:error, reason()}
```

Returns the observed dates of the holidays of `country` for `year`, in
ascending order and without duplicates.

    iex> Offlixir.dates(:FR, 2026)
    {:ok,
     [~D[2026-01-01], ~D[2026-04-06], ~D[2026-05-01], ~D[2026-05-08], ~D[2026-05-14],
      ~D[2026-05-25], ~D[2026-07-14], ~D[2026-08-15], ~D[2026-11-01], ~D[2026-11-11],
      ~D[2026-12-25]]}

    iex> Offlixir.dates(:FR, 10_000)
    {:error, {:invalid_year, 10_000}}

# `dates!`

```elixir
@spec dates!(country(), integer()) :: [Date.t()]
```

Same as `dates/2` but returns the list, or raises `ArgumentError`.

The result is a plain list of `Date` structs, usable with `in`:

    iex> ~D[2026-07-14] in Offlixir.dates!(:FR, 2026)
    true

    iex> Offlixir.dates!(:fr, 2026)
    ** (ArgumentError) unknown country :fr, expected a supported ISO 3166-1 alpha-2 code in upper case such as :FR or "FR"

# `dates_between!`

```elixir
@spec dates_between!(country(), day(), day()) :: [Date.t()]
```

Returns the observed dates of the holidays of `country` from `from` to `to`,
both included, in ascending order and without duplicates.

    iex> Offlixir.dates_between!(:FR, ~D[2025-12-01], ~D[2026-01-31])
    [~D[2025-12-25], ~D[2026-01-01]]

# `holiday?`

```elixir
@spec holiday?(day(), country()) :: boolean()
```

Returns `true` when `day` is the observed date of a holiday of `country`.

`day` can be a `Date`, a `NaiveDateTime` or a `DateTime`. Only the date part is
read. For a `DateTime` it is the date in its own time zone.

    iex> Offlixir.holiday?(~D[2026-07-14], :FR)
    true

    iex> Offlixir.holiday?(~N[2026-07-15 08:30:00], "FR")
    false

# `holidays`

```elixir
@spec holidays(country(), integer()) ::
  {:ok, [Offlixir.Holiday.t()]} | {:error, reason()}
```

Returns the holidays of `country` for `year`, sorted by date.

Two holidays that fall on the same date are both returned.

    iex> {:ok, [first | _]} = Offlixir.holidays(:FR, 2026)
    iex> first
    %Offlixir.Holiday{
      country: :FR,
      id: "FR-NEWYEARSDAY-01",
      date: ~D[2026-01-01],
      observed_date: ~D[2026-01-01],
      local_name: "Jour de l'an",
      english_name: "New Year's Day"
    }

    iex> Offlixir.holidays(:XX, 2026)
    {:error, {:unknown_country, :XX}}

# `holidays!`

```elixir
@spec holidays!(country(), integer()) :: [Offlixir.Holiday.t()]
```

Same as `holidays/2` but returns the list, or raises `ArgumentError`.

    iex> Offlixir.holidays!("FR", 2026) |> Enum.map(& &1.english_name) |> Enum.take(3)
    ["New Year's Day", "Easter Monday", "Labour Day"]

# `holidays_between!`

```elixir
@spec holidays_between!(country(), day(), day()) :: [Offlixir.Holiday.t()]
```

Returns the holidays of `country` observed from `from` to `to`, both included.

Raises `ArgumentError` when the country is unknown, when a year of the range is
invalid or when `to` is before `from`.

    iex> Offlixir.holidays_between!(:FR, ~D[2025-12-01], ~D[2026-01-31]) |> Enum.map(& &1.id)
    ["FR-CHRISTMASDAY-01", "FR-NEWYEARSDAY-01"]

# `weekend?`

```elixir
@spec weekend?(day(), country()) :: boolean()
```

Returns `true` when `day` falls on the week-end of `country`.

The week-end is Saturday and Sunday in every supported country.

    iex> Offlixir.weekend?(~D[2026-07-11], :FR)
    true

    iex> Offlixir.weekend?(~D[2026-07-13], :FR)
    false

---

*Consult [api-reference.md](api-reference.md) for complete listing*
