View Source NervesTimeZones
Local time and time zones for Nerves devices
NervesTimeZones provides a way of managing local time on embedded devices. It provides the following:
- Set your time zone and have it be used for local time calls like
NaiveDateTime.local_now/0
. The time zone persists across reboots. - Set up Elixir's Calendar time zone
database using
zoneinfo
- Provide a small time zone database appropriate for many embedded devices
It does not support the automatic update of the time zone database like
tzdata and tz.
For now, you'll need to watch for new versions of the nerves_time_zones
package. (We're open to changing this, but it's not as easy as regularly polling
IANA.)
The primary motivation for creating this library was to reduce the size of the
time zone database. tzdata
and tz
both work by compiling the IANA database
to an internal format. At the time, tzdata
compiled to a 3.5 MB ets table
(~600 KB gzip compressed) and tz
compiled to a 300 KB beam file (~250 KB gzip
compressed). Using TZif files (the /usr/share/zoneinfo
ones) and 10 years of
time zone records for all time zones resulted in about 450 KB of data (~16 KB
gzip compressed).
Installation
First, add nerves_time_zones
to your list of dependencies in mix.exs
:
def deps do
[
{:nerves_time_zones, "~> 0.3.2"}
]
end
NervesTimeZones persists the currently selected local time zone to
"/data/nerves_time_zones"
. This works well on Nerves devices. If you're
developing on your laptop, you may want to change the location by adding the
following in your project's config.exs
:
config :nerves_time_zones, data_dir: "./tmp/nerves_time_zones"
The fetched IANA timezone db version defaults to 2022g
, which can be adjusted
like this:
config :nerves_time_zones, version: "2025a"
The default time zone is "Etc/UTC". If you want it to be something else, set it in the config like this:
config :nerves_time_zones, default_time_zone: "Europe/Paris"
NervesTimeZones maintains only a subset of the available timezone database information
to save on file size. The default keeps [2022-01-01, +10 years]
relative to the
date of compilation. Both ends can be adjusted in the config like this:
config :nerves_time_zones,
earliest_date: DateTime.to_unix(~U[2022-05-17 12:02:32Z]),
latest_date: System.os_time(:second) + 5 * 365 * 86400
Database example
If you just start up IEx, you may have seen something like this:
iex> DateTime.now("America/New_York")
{:error, :utc_only_time_zone_database}
NervesTimeZones
automatically sets up the time zone database so once you've
added the :nerves_time_zones
dependency, you'll get this instead:
iex> DateTime.now("America/New_York")
{:ok, #DateTime<2021-03-11 10:19:59.811175-05:00 EST America/New_York>}
NervesTimeZones
is opinionated on the time zone database provider so it forces
the default and will log messages if there's a conflict with tzdata
or tz
.
You can still use those time zone databases if you really want them even though
it defeats the purpose of keeping one database on a device. You'll just need to
manually specify the database in all of your DateTime
calls.
Local time example
By default with NervesTimeZones
, local time will be UTC. You can see this by
running NaiveDateTime.local_now/1
. Be aware that this behavior is different
from the normal behavior of using your system's local time zone setting if you
trying this out on your laptop. Nerves devices don't have time zone settings by
default.
iex> DateTime.utc_now
~U[2021-03-11 15:10:41.573579Z]
iex> NaiveDateTime.local_now
~N[2021-03-11 15:10:44]
You can set the time zone like this (note the time shift by 5 hours):
iex> NervesTimeZones.set_time_zone("America/New_York")
:ok
iex> NaiveDateTime.local_now
~N[2021-03-11 10:11:02]
Running OS commands
It's possible to use the same time zone database with non-BEAM programs. For example, on my system the default for C programs is Eastern time:
iex> System.cmd("date", [])
{"Thu 11 Mar 2021 10:34:14 AM EST\n", 0}
On a Nerves device, this would be UTC, but the concept is the same.
Say I want it to be Hawaii time:
iex> NervesTimeZones.set_time_zone("Pacific/Honolulu")
:ok
This won't affect the date
program since it's not running on the BEAM. All is
not lost. NervesTimeZones
can provide environment settings so that the C
runtime will use the same data base and time zone setting as on the BEAM:
iex> System.cmd("date", [], env: NervesTimeZones.tz_environment())
{"Thu 11 Mar 2021 05:40:38 AM HST\n", 0}
iex)> NaiveDateTime.local_now
~N[2021-03-11 05:40:49]
How it works
NervesTimeZones pulls data from the IANA time zone
database and compiles it to TZif files using
zic(8). This is the same
process used to create the files under /usr/share/zoneinfo
. The difference is
that those contain time period records 50 years or more in the past and over 15
years to the future. NervesTimeZones limits the range substantially to reduce
the database size.
Since the main embedded use cases for time zone information are to show the time and schedule events in the local time, having past time zone information is not needed. This saves a ton of space, since time zones changed a lot in the 20th century.
The second part of the library is a NIF that updates the C runtime's local time
zone setting. This setting also affects Erlang's and Elixir's local time
functions like NaiveDateTime.local_now/0
as well. The NIF is trivially short
and calls tzset(3)
with
the TZif file. Unfortunately, the way to pass the time zone is via the TZ
environment variable, so if TZ
previously pointed to anything before this
library runs, it won't afterwards.
License
Copyright (C) 2021 Frank Hunleth
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.