README.md

# nerves_time

[![CircleCI](https://circleci.com/gh/nerves-time/nerves_time.svg?style=svg)](https://circleci.com/gh/nerves-time/nerves_time)
[![Hex version](https://img.shields.io/hexpm/v/nerves_time.svg "Hex version")](https://hex.pm/packages/nerves_time)

`NervesTime` keeps the system clock on [Nerves](http://nerves-project.org)
devices in sync when connected to the network and close to in sync when
disconnected. It's especially useful for devices lacking a [Battery-backed
real-time clock](https://en.wikipedia.org/wiki/Real-time_clock) and will advance
the clock at startup to a reasonable guess.

## Installation

First add `nerves_time` to your project's dependencies:

```elixir
def deps do
  [
    {:nerves_time, "~> 0.3.0"}
  ]
end
```

Ensure that your `vm.args` allows for
[timewarps](http://erlang.org/doc/apps/erts/time_correction.html#time-warp-modes).
If it doesn't, `nerves_time` will update the OS system time, but Erlang's system
time will lag. The following line should be in the beginning or middle of the
`vm.args` file:

```elixir
+C multi_time_warp
```

If you're using one of the official Nerves Systems, then this is all that's
needed. `nerves_time` requires Busybox's `ntpd` and `date` applets to be
enabled. If you haven't explicitly disabled the, they're probably enabled.

## Configuration

`nerves_time` uses [ntp.pool.org](https://www.ntppool.org/en/) for time
synchronization. Please see their [terms of
use](https://www.ntppool.org/tos.html) before tweaking `nerves_time`.
Alternative NTP servers can be specified using the `config.exs`:

```elixir
# config/config.exs

config :nerves_time, :servers, [
    "0.pool.ntp.org",
    "1.pool.ntp.org",
    "2.pool.ntp.org",
    "3.pool.ntp.org"
  ]
```

It's also possible to configure NTP servers at runtime. See
`NervesTime.set_ntp_servers/1`.

`nerves_time` also has a concept of a valid time range. This minimizes time
errors on systems without clocks or Internet connections or that may have some
issue that causes a very wrong time value. The default valid time range is
hardcoded and moves forward each release. It is not the build timestamp since
that results in [non-reproducible builds](https://reproducible-builds.org).
Applications can override the valid range via the application config:

```elixir
# config/config.exs

config :nerves_time, earliest_time: ~N[2019-10-04 00:00:00], latest_time: ~N[2022-01-01 00:00:00]
```

## Algorithm

Here's the basic idea behind `nerves_time`:

* If the clock hasn't been set or is invalid, set it to the earliest valid
  time known to `nerves_time`. This is either set in the application config or
  defaulted to a reasonable value that likely moves forward a little each
  `nerves_time` release.
* check for time via a [Real Time Clock](#Real-Time-Clock)
* Run Busybox `ntpd` to synchronize time using the [NTP
  protocol](https://en.wikipedia.org/wiki/Network_Time_Protocol).
* Update [Real Time Clock](#Real-Time-Clock) periodically and on graceful power
  downs. This is currently only done at around 11 minute intervals.

To check the NTP synchronization status, call `NervesTime.synchronized?/0`.

## Real Time Clock

A hardware based real time clock can be configured by added a config.exs entry:

```elixir
config :nerves_time, rtc: {SomeImplementingModule, [some: :initialization_opt]}
```

By default Nerves Time is configured to use `NervesTime.FileTime` which will
Check for `~/.nerves_time`. If it exists, advance the clock to it's last
modification time.

See the documentation for `NervesTime.RealTimeClock` to implement your own
real time clock.

## Credits and license

This project started as a fork of
[nerves_ntp](https://hex.pm/packages/nerves_ntp) by Marcin Operacz and Wojciech
Mandrysz. It has quite a few changes from since when they worked on the project,
but some of their code still exists. Both their project and this one are covered
by the [Apache-2.0 license](https://opensource.org/licenses/Apache-2.0).