Time language support

This page details how you can handle time when developing software on Fuchsia.

In general, you may use any time library available for your language. Most libraries are able to link against Fuchsia's libc implementation to obtain time. It is recommended that you use a platform-agnostic library unless you need Fuchsia specific time operations.

Various time options are available, such as:

Language Monotonic UTC
C clock_gettime time
C++ std::chrono::steady_clock std::chrono::system_clock
Rust std::time::Instant std::time::SystemTime

Fuchsia specific time operations

In some cases, you will need to handle time in a Fuchsia specific manner. This is necessary when you need to handle Fuchsia's representation of time directly, or when you need to handle Fuchsia specific UTC behavior.

Monotonic time

Monotonic time on Fuchsia is represented as a signed 64 bit integer, which contains the number of nanoseconds since the system was powered on. See zx_clock_get_monotonic for more details.

C

Monotonic time is accessible through libzircon.

#include <stdio.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/clock.h>

void monotonic_examples(void) {
  // Read monotonic time.
  zx_time_t mono_nsec = zx_clock_get_monotonic();
  printf("The monotonic time is %ld ns.\n", mono_nsec);
}

C++

Monotonic time is accessible through libzx.

#include <lib/zx/clock.h>
#include <lib/zx/time.h>
#include <stdio.h>

void monotonic_examples() {
  // Read monotonic time.
  zx::time monotonic_time = zx::clock::get_monotonic();
  printf("The monotonic time is %ld ns.\n", monotonic_time.get());
}

Rust

Monotonic time is accessible through the fuchsia_zircon crate. This crate is only available in-tree.

use fuchsia_zircon as zx;

pub fn monotonic_examples() {
    // Read monotonic time.
    let monotonic_time = zx::Time::get_monotonic();
    println!("The monotonic time is {:?}.", monotonic_time);
}

UTC time

UTC time on Fuchsia is represented as a signed 64 bit integer that contains the number of nanoseconds since the Unix epoch (January 1st, 1970).

Operations on the UTC clock require obtaining a handle to the UTC clock provided to the runtime.

Handling the UTC clock directly enables a few Fuchsia specific operations, including:

  • Inspecting the UTC clock's properties.
  • Waiting for the UTC clock to begin running, which indicates it has been synchronized, before reading it.

C

You can obtain a handle to the UTC clock using zx_utc_reference_get and use the syscalls exposed in libzircon.

#include <stdio.h>
#include <zircon/syscalls.h>
#include <zircon/syscalls/clock.h>
#include <zircon/utc.h>

void utc_examples(void) {
  // This is a borrowed handle. Do not close it, and do not replace it using
  // zx_utc_reference_swap while using it.
  zx_handle_t utc_clock = zx_utc_reference_get();

  if (utc_clock != ZX_HANDLE_INVALID) {
    // Wait for the UTC clock to start.  The clock may never start on a device
    // that does not have a RTC or a network connection.
    // A started clock may, but also may not have a valid UTC actual.
    zx_status_t status = zx_object_wait_one(utc_clock, ZX_CLOCK_STARTED, ZX_TIME_INFINITE, NULL);
    if (status == ZX_OK) {
      printf("UTC clock is started.\n");
    } else {
      printf("zx_object_wait_one syscall failed (status = %d).\n", status);
    }

    // Wait for the UTC clock to be externally synchronized.  Once that happens,
    // the clock is known to correspond to UTC actual (with error bounds available through
    // zx_clock_get_details).
    status = zx_object_wait_one(utc_clock, ZX_USER_SIGNAL_0, ZX_TIME_INFINITE, NULL);
    if (status == ZX_OK) {
      printf("UTC clock is externally synchronized.\n");
    } else {
      printf("zx_object_wait_one syscall failed (status = %d).\n", status);
    }

    // Wait for the UTC clock to be of "logging quality".  Logging quality UTC
    // clock is started, but not necessarily corresponding to UTC actual. This
    // clock is to be used only for logging timestamps.
    status = zx_object_wait_one(utc_clock, ZX_USER_SIGNAL_1, ZX_TIME_INFINITE, NULL);
    if (status == ZX_OK) {
      printf("UTC clock is of logging quality.\n");
    } else {
      printf("zx_object_wait_one syscall failed (status = %d).\n", status);
    }

    // Read the UTC clock.
    zx_time_t nsec;
    status = zx_clock_read(utc_clock, &nsec);
    if (status == ZX_OK) {
      printf("It has been %ld ns since the epoch.\n", nsec);
    } else {
      printf("zx_clock_read syscall failed (status = %d).\n", status);
    }

    // Read UTC clock details.
    zx_clock_details_v1_t details;
    status = zx_clock_get_details(utc_clock, ZX_CLOCK_ARGS_VERSION(1), &details);
    if (status == ZX_OK) {
      printf("The UTC clock's backstop time is %ld ns since the epoch.\n", details.backstop_time);
    } else {
      printf("zx_clock_get_details failed (status = %d).\n", status);
    }

  } else {
    printf("Error, our runtime has no clock assigned to it!\n");
  }
}

C++

You can obtain a handle to the UTC clock using zx_utc_reference_get and use the syscall wrappers in libzx.

#include <lib/zx/clock.h>
#include <lib/zx/time.h>
#include <stdio.h>
#include <zircon/utc.h>

void utc_examples() {
  // This is a borrowed handle. Do not close it, and do not replace it using
  // zx_utc_reference_swap while using it.
  zx_handle_t utc_clock_handle = zx_utc_reference_get();
  zx::unowned_clock utc_clock(utc_clock_handle);

  // Wait for the UTC clock to start.  The clock may never start on a device
  // that does not have a RTC or a network connection.
  // A started clock may, but also may not have a valid UTC actual.
  zx_status_t status = utc_clock->wait_one(ZX_CLOCK_STARTED, zx::time::infinite(), nullptr);
  if (status == ZX_OK) {
    printf("UTC clock is started.\n");
  } else {
    printf("Waiting for the UTC clock to start failed (status = %d).\n", status);
  }

  // Wait for the UTC clock to be externally synchronized.  Once that happens,
  // the clock is known to correspond to UTC actual (with error bounds available through
  // `zx::Clock::get_details`).
  status = utc_clock->wait_one(ZX_USER_SIGNAL_0, zx::time::infinite(), nullptr);
  if (status == ZX_OK) {
    printf("UTC clock is externally synchronized.\n");
  } else {
    printf("Waiting for the UTC clock to start failed (status = %d).\n", status);
  }

  // Wait for the UTC clock to be of "logging quality".  Logging quality UTC
  // clock is started, but not necessarily corresponding to UTC actual. This
  // clock is to be used only for logging timestamps.
  status = utc_clock->wait_one(ZX_USER_SIGNAL_1, zx::time::infinite(), nullptr);
  if (status == ZX_OK) {
    printf("UTC clock is of logging quality.");
  } else {
    printf("zx_object_wait_one syscall failed (status = %d).\n", status);
  }

  // Read the UTC clock.
  zx_time_t utc_time;
  status = utc_clock->read(&utc_time);
  if (status == ZX_OK) {
    printf("The UTC time is %ld ns since the epoch\n", utc_time);
  } else {
    printf("Reading the UTC clock failed (status = %d).\n", status);
  }

  // Read clock details.
  zx_clock_details_v1_t details;
  status = utc_clock->get_details(&details);
  if (status == ZX_OK) {
    printf("The UTC clock's backstop time is %ld ns since the epoch.\n", details.backstop_time);
  } else {
    printf("Reading the UTC clock details failed (status = %d).\n", status);
  }
}

Rust

You can obtain a handle to the UTC clock using the fuchsia_runtime crate and use the syscall wrappers in the fuchsia_zircon crate. The fuchsia_async crate contains utilities to aid waiting for the clock to start. Note that these crates are only available in-tree.

use fuchsia_async as fasync;
use fuchsia_runtime::duplicate_utc_clock_handle;
use fuchsia_zircon as zx;

pub async fn utc_examples() {
    // Obtain a UTC handle.
    let utc_clock = duplicate_utc_clock_handle(zx::Rights::SAME_RIGHTS)
        .expect("Failed to duplicate UTC clock handle.");

    // Wait for the UTC clock to start.  The clock may never start on a device
    // that does not have a RTC or a network connection.
    // A started clock may, but also may not have a valid UTC actual.
    fasync::OnSignals::new(&utc_clock, zx::Signals::CLOCK_STARTED)
        .await
        .expect("Failed to wait for ZX_CLOCK_STARTED.");
    println!("UTC clock is started.");

    // Wait for the UTC clock to be externally synchronized.  Once that happens,
    // the clock is known to correspond to UTC actual (with error bounds available through
    // `zx::Clock::get_details`).
    fasync::OnSignals::new(&utc_clock, zx::Signals::USER_0)
        .await
        .expect("Failed to wait for ZX_SIGNAL_USER_0.");
    println!("UTC clock is externally synchronized.");

    // Wait for the UTC clock to be of "logging quality".  Logging quality UTC
    // clock is started, but not necessarily corresponding to UTC actual. This
    // clock is to be used only for logging timestamps.
    fasync::OnSignals::new(&utc_clock, zx::Signals::USER_1)
        .await
        .expect("Failed to wait for ZX_SIGNAL_USER_1.");
    println!("UTC clock is of logging quality.");

    // Read the UTC clock.
    let utc_time = utc_clock.read().expect("Failed to read UTC clock.");
    println!("The UTC time is {:?}.", utc_time);

    // Read UTC clock details.
    let clock_details = utc_clock.get_details().expect("Failed to read UTC clock details.");
    println!("The UTC clock's backstop time is {:?}.", clock_details.backstop);
}