System Time
===========

:link_to_translation:`zh_CN:[中文]`

Overview
--------

{IDF_TARGET_NAME} uses two hardware timers for the purpose of keeping system time. System time can be kept by using either one or both of the hardware timers depending on the application's purpose and accuracy requirements for system time. The two hardware timers are:

- **RTC timer**: This timer allows time keeping in various sleep modes, and can also persist time keeping across any resets (with the exception of power-on resets which reset the RTC timer). The frequency deviation depends on the `RTC Timer Clock Sources`_ and affects the accuracy only in sleep modes, in which case the time will be measured at 6.6667 μs resolution.

- **High-resolution timer**: This timer is not available in sleep modes and will not persist over a reset, but has greater accuracy. The timer uses the APB_CLK clock source (typically 80 MHz), which has a frequency deviation of less than ±10 ppm. Time will be measured at 1 μs resolution.

The possible combinations of hardware timers used to keep system time are listed below:

- RTC and high-resolution timer (default)
- RTC
- High-resolution timer
- None

It is recommended that users stick to the default option as it provides the highest accuracy. However, users can also select a different setting via the :ref:`CONFIG_LIBC_TIME_SYSCALL` configuration option.


.. _rtc-clock-source-choice:

RTC Timer Clock Sources
------------------------

The RTC timer has the following clock sources:

.. list::

    - ``Internal 90–150 kHz (depending on chip) RC oscillator`` (default): Features the lowest Deep-sleep current consumption and no dependence on any external components. However, the frequency stability of this clock source is affected by temperature fluctuations, so time may drift in both Deep-sleep and Light-sleep modes.

    :not esp32c2: - ``External 32 kHz crystal``: Requires a 32 kHz crystal to be connected to the external crystal pins. This source provides a better frequency stability at the expense of a slightly higher (by 1 μA) Deep-sleep current consumption. Refer to the `datasheet <{IDF_TARGET_DATASHEET_EN_URL}>`__ for information on which pins to connect to.

    - ``External 32 kHz oscillator``: Allows using a 32 kHz clock generated by an external circuit. The external clock signal must be connected to the external oscillator pin. The input amplitude must ensure that the digital circuit can correctly distinguish between logic high and low levels. Test results show that 1.7 V is the threshold between high and low levels. Therefore, make sure the input waveform has a peak value greater than 1.7 V and a minimum value lower than 1.7 V. For details on pin connections, please refer to the `datasheet <{IDF_TARGET_DATASHEET_EN_URL}>`__.

    :esp32 or esp32s2 or esp32s3 or esp32c2 or esp32c3: - ``Internal 8.5–17.5 MHz oscillator (depending on chip), divided by 256``: Provides better frequency stability than the ``Internal 90–150 kHz RC oscillator`` at the expense of a higher (by 5 μA) Deep-sleep current consumption. It also does not require external components.

The choice depends on your requirements for system time accuracy and power consumption in sleep modes. To modify the RTC clock source, set :ref:`CONFIG_RTC_CLK_SRC` in project configuration.

More details about the wiring requirements for the external crystal or external oscillator, please refer to the `Hardware Design Guidelines <https://docs.espressif.com/projects/esp-hardware-design-guidelines/en/latest/{IDF_TARGET_PATH_NAME}>`_.

Get Current Time
----------------

To get the current time, use the POSIX function ``gettimeofday()``. Additionally, you can use the following standard C library functions to obtain time and manipulate it:

.. code-block:: bash

    gettimeofday
    time
    asctime
    clock
    ctime
    difftime
    gmtime
    localtime
    mktime
    strftime
    adjtime*

To stop smooth time adjustment and update the current time immediately, use the POSIX function ``settimeofday()``.

If you need to obtain time with one second resolution, use the following code snippet:

.. code-block:: c

    time_t now;
    char strftime_buf[64];
    struct tm timeinfo;

    time(&now);
    // Set timezone to China Standard Time
    setenv("TZ", "CST-8", 1);
    tzset();

    localtime_r(&now, &timeinfo);
    strftime(strftime_buf, sizeof(strftime_buf), "%c", &timeinfo);
    ESP_LOGI(TAG, "The current date/time in Shanghai is: %s", strftime_buf);

If you need to obtain time with one microsecond resolution, use the code snippet below:

.. code-block:: c

    struct timeval tv_now;
    gettimeofday(&tv_now, NULL);
    int64_t time_us = (int64_t)tv_now.tv_sec * 1000000L + (int64_t)tv_now.tv_usec;

.. _system-time-sntp-sync:

SNTP Time Synchronization
-------------------------

To set the current time, you can use the POSIX functions ``settimeofday()`` and ``adjtime()``. They are used internally in the lwIP SNTP library to set current time when a response from the NTP server is received. These functions can also be used separately from the lwIP SNTP library.

Some lwIP APIs, including SNTP functions, are not thread safe, so it is recommended to use :doc:`esp_netif component <../network/esp_netif>` when interacting with SNTP module.

To initialize a particular SNTP server and also start the SNTP service, simply create a default SNTP server configuration with a particular server name, then call :cpp:func:`esp_netif_sntp_init()` to register that server and start the SNTP service.

.. code-block:: c

    esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG("pool.ntp.org");
    esp_netif_sntp_init(&config);

This code automatically performs time synchronization once a reply from the SNTP server is received. Sometimes it is useful to wait until the time gets synchronized, :cpp:func:`esp_netif_sntp_sync_wait()` can be used for this purpose. Applications can also subscribe to an event posted when time is synchronized (``NETIF_SNTP_EVENT``, ID ``NETIF_SNTP_TIME_SYNC``). The event data is a pointer to :cpp:type:`esp_netif_sntp_time_sync_t` containing the synchronized ``timeval``:

.. code-block:: c

    if (esp_netif_sntp_sync_wait(pdMS_TO_TICKS(10000)) != ESP_OK) {
        printf("Failed to update system time within 10s timeout");
    }

To configure multiple NTP servers (or use more advanced settings, such as DHCP provided NTP servers), please refer to the detailed description of :ref:`esp_netif-sntp-api` in :doc:`esp_netif <../network/esp_netif>` documentation.

The lwIP SNTP library could work in one of the following sync modes:

- :cpp:enumerator:`SNTP_SYNC_MODE_IMMED` (default): Updates system time immediately upon receiving a response from the SNTP server after using ``settimeofday()``.
- :cpp:enumerator:`SNTP_SYNC_MODE_SMOOTH`: Updates time smoothly by gradually reducing time error using the function ``adjtime()``. If the difference between the SNTP response time and system time is more than 35 minutes, update system time immediately by using ``settimeofday()``.

If you want to choose the :cpp:enumerator:`SNTP_SYNC_MODE_SMOOTH` mode, please set the :cpp:member:`esp_sntp_config::smooth` to ``true`` in the SNTP configuration struct. Otherwise (and by default) the :cpp:enumerator:`SNTP_SYNC_MODE_IMMED` mode will be used.

For setting a callback function that is called when time gets synchronized, use the :cpp:member:`esp_sntp_config::sync_cb` field in the configuration struct.

An application with this initialization code periodically synchronizes the time. The time synchronization period is determined by :ref:`CONFIG_LWIP_SNTP_UPDATE_DELAY` (the default value is one hour). To modify the variable, set :ref:`CONFIG_LWIP_SNTP_UPDATE_DELAY` in project configuration.

A code example that demonstrates the implementation of time synchronization based on the lwIP SNTP library is provided in the :example:`protocols/sntp` directory.

Note that it is also possible to use lwIP API directly, but care must be taken to thread safety. Here we list the thread-safe APIs:

- :cpp:func:`sntp_set_time_sync_notification_cb` can be used to set a callback function that notifies of the time synchronization process.
- :cpp:func:`sntp_get_sync_status` and :cpp:func:`sntp_set_sync_status` can be used to get/set time synchronization status.
- :cpp:func:`sntp_set_sync_mode` can be used to set the synchronization mode.
- :cpp:func:`esp_sntp_setoperatingmode` sets the preferred operating mode.:cpp:enumerator:`ESP_SNTP_OPMODE_POLL` and :cpp:func:`esp_sntp_init` initializes SNTP module.
- :cpp:func:`esp_sntp_setservername` configures one SNTP server.


Timezones
---------

To set the local timezone, use the following POSIX functions:

1. Call ``setenv()`` to set the ``TZ`` environment variable to the correct value based on the device location. The format of the time string is the same as described in the `GNU libc documentation <https://www.gnu.org/software/libc/manual/html_node/TZ-Variable.html>`_ (although the implementation is different).
2. Call ``tzset()`` to update C library runtime data for the new timezone.

Once these steps are completed, call the standard C library function ``localtime()``, and it returns the correct local time taking into account the timezone offset and daylight saving time.


Year 2036 and 2038 Overflow Issues
----------------------------------

SNTP/NTP 2036 Overflow
^^^^^^^^^^^^^^^^^^^^^^

SNTP/NTP timestamps are represented as 64-bit unsigned fixed point numbers, where the first 32 bits represent the integer part, and the last 32 bits represent the fractional part. The 64-bit unsigned fixed point number represents the number of seconds since 00:00 on 1st of January 1900, thus SNTP/NTP times will overflow in the year 2036.

To address this issue, lifetime of the SNTP/NTP timestamps has been extended by convention by using the MSB (bit 0 by convention) of the integer part to indicate time ranges between years 1968 to 2104 (see `RFC2030 <https://www.rfc-editor.org/rfc/rfc2030>`_ for more details). This convention is implemented in lwIP library SNTP module. Therefore SNTP-related functions in ESP-IDF are future-proof until year 2104.


Unix Time 2038 Overflow
^^^^^^^^^^^^^^^^^^^^^^^

Unix time (type ``time_t``) was previously represented as a 32-bit signed integer, leading to an overflow in year 2038 (i.e., `Y2K38 issue <https://en.wikipedia.org/wiki/Year_2038_problem>`_). To address the Y2K38 issue, ESP-IDF uses a 64-bit signed integer to represent ``time_t`` starting from release v5.0, thus deferring ``time_t`` overflow for another 292 billion years.


API Reference
-------------

.. include-build-file:: inc/esp_sntp.inc
