Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## [0.4.0](https://github.com/hopsor/open_hours/compare/v0.2.0...v0.4.0) (TBD)

* Potentialy breaking chaneg: Make the timezone database configurable ([#41](https://github.com/hopsor/open_hours/pull/41))

## [0.3.0](https://github.com/hopsor/open_hours/compare/v0.2.0...v0.3.0) (2026-03-27)


Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@ The package can be installed by adding `open_hours` to your list of dependencies
```elixir
def deps do
[
{:open_hours, "~> 0.2.0"}
{:open_hours, "~> 0.4.0"}
]
end
```

If you do not already have a timezone database (eg tz or tzdata) configured for your Elixir application
you will also need to do that. We recommend [TZ](https://tz.hexdocs.pm/readme.html)

## Usage

In order to use OpenHours functions you first need a `Schedule` config:
Expand Down
8 changes: 4 additions & 4 deletions lib/open_hours/offset.ex
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ defmodule OpenHours.Offset do
duration
)
when schedule_tz != dt_tz do
{:ok, shifted} = DateTime.shift_zone(dt, schedule_tz, Tzdata.TimeZoneDatabase)
{:ok, shifted} = DateTime.shift_zone(dt, schedule_tz, OpenHours.TimeZoneDatabase.database())
shift(schedule, shifted, duration)
end

Expand Down Expand Up @@ -142,7 +142,7 @@ defmodule OpenHours.Offset do
available = DateTime.diff(slot.ends_at, position)

if available >= remaining_seconds do
DateTime.add(position, remaining_seconds, :second, Tzdata.TimeZoneDatabase)
DateTime.add(position, remaining_seconds, :second, OpenHours.TimeZoneDatabase.database())
else
shift_forward(schedule, slot.ends_at, remaining_seconds - available)
end
Expand All @@ -160,7 +160,7 @@ defmodule OpenHours.Offset do
available = DateTime.diff(position, slot.starts_at)

if available >= remaining_seconds do
DateTime.add(position, -remaining_seconds, :second, Tzdata.TimeZoneDatabase)
DateTime.add(position, -remaining_seconds, :second, OpenHours.TimeZoneDatabase.database())
else
shift_backward(schedule, slot.starts_at, remaining_seconds - available)
end
Expand Down Expand Up @@ -206,7 +206,7 @@ defmodule OpenHours.Offset do
end

defp snap_to_business_time(schedule, date, time, direction) do
dt = DateTime.new!(date, time, schedule.time_zone, Tzdata.TimeZoneDatabase)
dt = DateTime.new!(date, time, schedule.time_zone, OpenHours.TimeZoneDatabase.database())

if Schedule.in_hours?(schedule, dt) do
dt
Expand Down
2 changes: 1 addition & 1 deletion lib/open_hours/schedule.ex
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ defmodule OpenHours.Schedule do
@spec in_hours?(OpenHours.Schedule.t(), DateTime.t()) :: boolean()
def in_hours?(%Schedule{time_zone: schedule_tz} = schedule, %DateTime{time_zone: date_tz} = at)
when schedule_tz != date_tz do
{:ok, shifted_at} = DateTime.shift_zone(at, schedule_tz, Tzdata.TimeZoneDatabase)
{:ok, shifted_at} = DateTime.shift_zone(at, schedule_tz, OpenHours.TimeZoneDatabase.database())
in_hours?(schedule, shifted_at)
end

Expand Down
12 changes: 6 additions & 6 deletions lib/open_hours/time_slot.ex
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ defmodule OpenHours.TimeSlot do
%DateTime{} = ends_at
)
when schedule_tz != start_tz do
{:ok, shifted_starts_at} = DateTime.shift_zone(starts_at, schedule_tz, Tzdata.TimeZoneDatabase)
{:ok, shifted_starts_at} = DateTime.shift_zone(starts_at, schedule_tz, OpenHours.TimeZoneDatabase.database())
between(schedule, shifted_starts_at, ends_at)
end

Expand All @@ -35,7 +35,7 @@ defmodule OpenHours.TimeSlot do
%DateTime{time_zone: end_tz} = ends_at
)
when schedule_tz != end_tz do
{:ok, shifted_ends_at} = DateTime.shift_zone(ends_at, schedule_tz, Tzdata.TimeZoneDatabase)
{:ok, shifted_ends_at} = DateTime.shift_zone(ends_at, schedule_tz, OpenHours.TimeZoneDatabase.database())
between(schedule, starts_at, shifted_ends_at)
end

Expand Down Expand Up @@ -110,7 +110,7 @@ defmodule OpenHours.TimeSlot do

def stream_next(%Schedule{time_zone: schedule_tz} = schedule, %DateTime{time_zone: dt_tz} = at, opts)
when schedule_tz != dt_tz do
{:ok, shifted} = DateTime.shift_zone(at, schedule_tz, Tzdata.TimeZoneDatabase)
{:ok, shifted} = DateTime.shift_zone(at, schedule_tz, OpenHours.TimeZoneDatabase.database())
stream_next(schedule, shifted, opts)
end

Expand Down Expand Up @@ -158,7 +158,7 @@ defmodule OpenHours.TimeSlot do

def stream_previous(%Schedule{time_zone: schedule_tz} = schedule, %DateTime{time_zone: dt_tz} = at, opts)
when schedule_tz != dt_tz do
{:ok, shifted} = DateTime.shift_zone(at, schedule_tz, Tzdata.TimeZoneDatabase)
{:ok, shifted} = DateTime.shift_zone(at, schedule_tz, OpenHours.TimeZoneDatabase.database())
stream_previous(schedule, shifted, opts)
end

Expand Down Expand Up @@ -210,8 +210,8 @@ defmodule OpenHours.TimeSlot do
|> get_intervals_for(day)
|> Enum.map(fn {interval_start, interval_end} ->
%TimeSlot{
starts_at: DateTime.new!(day, interval_start, schedule.time_zone, Tzdata.TimeZoneDatabase),
ends_at: DateTime.new!(day, interval_end, schedule.time_zone, Tzdata.TimeZoneDatabase)
starts_at: DateTime.new!(day, interval_start, schedule.time_zone, OpenHours.TimeZoneDatabase.database()),
ends_at: DateTime.new!(day, interval_end, schedule.time_zone, OpenHours.TimeZoneDatabase.database())
}
end)
end
Expand Down
21 changes: 21 additions & 0 deletions lib/open_hours/time_zone_database.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
defmodule OpenHours.TimeZoneDatabase do
@moduledoc """
Resolves the configured `Calendar.TimeZoneDatabase` implementation.

By default, the Elixir global time zone database is used (set via
`Calendar.put_time_zone_database/1`). You can override this for OpenHours
specifically by setting:

config :open_hours, :time_zone_database, Tz.TimeZoneDatabase

Any module that implements the `Calendar.TimeZoneDatabase` behaviour can be used.
"""

@doc """
Returns the configured time zone database module.
"""
@spec database() :: module()
def database do
Application.get_env(:open_hours, :time_zone_database, Calendar.get_time_zone_database())
end
end
7 changes: 4 additions & 3 deletions mix.exs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
defmodule OpenHours.MixProject do
use Mix.Project

@version "0.3.0"
@version "0.4.0"

def project do
[
Expand All @@ -19,14 +19,15 @@ defmodule OpenHours.MixProject do
# Run "mix help compile.app" to learn about applications.
def application do
[
extra_applications: [:logger, :tzdata]
extra_applications: [:logger]
]
end

# Run "mix help deps" to learn about dependencies.
defp deps do
[
{:tzdata, "~> 1.1"},
{:tzdata, "~> 1.1", optional: true},
{:tz, "~> 0.28", optional: true},
{:ex_doc, "~> 0.31", only: :dev, runtime: false}
]
end
Expand Down
1 change: 1 addition & 0 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"},
"rangex": {:git, "https://github.com/openapi-ro/rangex.git", "a92b03f8f075d73fef1bbbf4b1c088feaff8724b", [tag: "master"]},
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"},
"tz": {:hex, :tz, "0.28.2", "6c47f3d1a8ee5c33a1d8f0ba49e5a851b0a30c408a587d907aff6e71228b3b32", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:mint, "~> 1.6", [hex: :mint, repo: "hexpm", optional: true]}], "hexpm", "a6bf7355a33f0a7511602ab4566432ac0901d8abff81e94e455bca19708a0c87"},
"tzdata": {:hex, :tzdata, "1.1.3", "b1cef7bb6de1de90d4ddc25d33892b32830f907e7fc2fccd1e7e22778ab7dfbc", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "d4ca85575a064d29d4e94253ee95912edfb165938743dbf002acdf0dcecb0c28"},
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"},
}
2 changes: 2 additions & 0 deletions test/test_helper.exs
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
Application.put_env(:elixir, :time_zone_database, Tz.TimeZoneDatabase)

ExUnit.start()