Introduction

time is a date and time library for Rust. It is:

  • Easy and safe. time has a straightforward API without footguns.
  • space optimal and efficient. time provides support for dates in the ±9999 year range, with nanosecond precision; ranges up to ±999,999 are supported with the large-dates feature.
  • serde ready. Supports ISO8601, RFC2822 and RFC3339 with the serde-well-known feature. Not in the list? Make your format.
  • const ready. A majority of the API is const, making it ready for resource-constrained applications, with optional macros for easy date creation.
  • no-std support with alloc and std features.
  • numeric traits. Use durations easily: 2.seconds().
  • Supports Windows, Linux, macOS, WebAssembly targets among others.

And more...

Getting started

This short tutorial describes basic usage of time, to get operational quickly.

  1. Install time. Add it to your Cargo.toml. We'll enable macros:
[dependencies]
time = { version = "0.3", features = ["macros"] }
  1. Get the current time With the crate feature std a UTC offset (OffsetDateTime) is available, but with the crate feature local-offset, we can also get the local time.
use time::OffsetDateTime;

let now = OffsetDateTime::now_utc();
// let local = OffsetDateTime::now_local();

println!("{now}");
  1. Create dates and times. We can create dates (Date), dates with times (PrimitiveDateTime) and date times with an UTC offset (OffsetDateTime). A simple Time is also available.
use time::{Date, PrimitiveDateTime, OffsetDateTime, UtcOffset};
use time::Weekday::Wednesday;

let date = Date::from_iso_week_date(2022, 1, Wednesday).unwrap();
let datetime = date.with_hms(13, 0, 55).unwrap();
let datetime_off = datetime.assume_offset(UtcOffset::from_hms(1, 2, 3).unwrap());

println!("{date}, {datetime}, {datetime_off}");
// 2022-01-01, 2022-01-01 13:00:55.0, 2022-01-01 13:00:55.0 +01:02:03

With the macros feature:

use time::macros::{date, datetime};

let date = date!(2022-01-01);
let datetime = datetime!(2022-01-01 13:00:55);
let datetime_off = datetime!(2022-01-01 13:00:55 +1:02:03);

println!("{date}, {datetime}, {datetime_off}");
// 2022-01-01, 2022-01-01 13:00:55.0, 2022-01-01 13:00:55.0 +01:02:03
  1. Manipulate dates and use Durations:
use time::Duration;
use time::macros::{datetime};

let a = datetime!(2022-01-01 10:00:55);
let b = datetime!(2022-01-01 13:00:00);

let duration: Duration = b - a;

println!("{}", b - a);
// 2h59m5s

time vs chrono

time 0.1 was originally a thin wrapper around libc time functions. Because it was relatively barebones, chrono was developed as a richer API on top of time 0.1.

Around 2019, the time crate, which was unmaintained since August 2016, was picked up for maintenance again. time has since been rewritten as of time 0.2, and is incompatible with the 0.1 version.

Today:

  • time has been rewritten from 0.1,and is actively developed.
  • chrono depends on time 0.1, an old version unrelated with current time, and is actively developed as well.

Since they are incompatible with each other, please choose the library that fits your needs.

Create dates and times

time provides a const-ready API. All functions here are const: values can be computed at compile-time if you pass constants, with no difference if used at runtime.

For convenience, macros are provided with feature macros. They let us restrict parameters further, avoiding the need to unwrap() at the cost of compilation time.

Creating Dates

From a constant value, with feature macros:

use time::macros::date;

let _ = date!(2022-01-02);

From a calendar date:

use time::{Date, Month};

let _ = Date::from_calendar_date(2022, Month::January, 2).unwrap();

Creating PrimitiveDateTimes

A PrimitiveDateTime is both a date and a time. We can create them directly:

use time::macros::datetime;

let _ = datetime!(2022-01-02 11:12:13.123_456_789);

or use an existing Date:

use time::macros::{date, time};
use time::Time;

let date = date!(2022-01-02);

// A date with 00:00:00 time
let _ = date.midnight();
// You can also provide a desired time...
let _ = date.with_hms(11, 12, 13).unwrap();
// or pass an existing `Time`
let _ = date.with_time(Time::from_hms_nano(11, 12, 13, 123_456_789).unwrap());
// with macros:
let _ = date.with_time(time!(11:12:13.123_456_789));

Creating OffsetDateTimes

An OffsetDateTime is a date, time and UTC offset . Use it if you deal with timezones:

use time::macros::datetime;

// When we pass an offset at the end to `datetime!`, it will return an 
// `OffsetDateTime` instead of an `PrimitiveDateTime`
let _ = datetime!(2022-01-02 11:12:13 UTC);
// With a positive offset:
let _ = datetime!(2022-01-02 11:12:13 +1);
// and a negative offset:
let _ = datetime!(2022-01-02 11:12:13.123_456_789 -2:34:56);

or, using an existing PrimitiveDateTime, with UtcOffset:

use time::macros::{datetime, offset};
use time::UtcOffset;

let dt = datetime!(2022-01-02 11:12:13);

// With UTC:
let _ = dt.assume_utc();
// or with another offset:
let _ = dt.assume_offset(UtcOffset::from_hms(1, 2, 3));
// with macros:
let _ = dt.assume_offset(offset!(-11));

Parse dates

time has can parse datetimes from strings, in any given format.

Parse a date from a string

  1. Enable feature parsing.

  2. Using the common ISO 8601 format, via the Iso8601 format description:

use time::format_description::well_known::Iso8601;
use time::PrimitiveDateTime;

let date = PrimitiveDateTime::parse("2022-01-02T11:12:13", &Iso8601::DEFAULT)
    .unwrap();

Parsing custom formats

time supports a few common formats, that we call well-known formats. We support arbitrary formats as well.

  1. Enable feature macros and parsing. macros are used to call format_description!, but you can also call the equivalent function.

  2. Create a format and parse:

use time::macros::format_description;
use time::Time;

let my_format = format_description!("h=[hour],m=[minute],s=[second]");
let time = Time::parse("h=11,m=12,s=13", &my_format).unwrap();

Reference for format descriptions can be found here.

Parsing into structs with serde

For convenience, you can use Serde with time.

  1. Enable [feature serde-well-known][serde-well-known].

  2. Create a struct and parse from a format, eg. JSON using serde-json:

use time::macros::format_description;
use time::{OffsetDateTime, Time};
use serde::{Deserialize};

#[derive(Deserialize)]
struct Notification {
    message: String,
    #[serde(with = "time::serde::iso8601")]
    timestamp: OffsetDateTime,
}

fn main() {
    let input = r#"{
      "message": "foo",
      "timestamp": "2022-01-02T11:12:13Z"
    }"#;

    let notification: Notification = serde_json::from_str(input).unwrap();
    println!("{:?}", notification.timestamp);
}

Construction

Conversion between types

Arithmetic

Format description

A format description is the manner in which the time crate knows how a value should be formatted and parsed. However, a format description is not a single type; it is instead represented by two internal traits (one for formatting and one for parsing) that are implemented by a number of types. Currently, all types that implement one trait also implement the other, but this is not guaranteed.

The following types currently implement both the Formattable and Parsable traits:

  • FormatItem<'_>
  • [FormatItem<'_>]
  • T where <T as Deref>::Target: Formattable (or Parsable)
  • All well known formats

While it is possible to construct a format description manually, this is generally not recommended, as it is more tedious and less readable than the alternative. Unless you are doing this, you will likely never need to know anything about FormatItem other than that it is produced by the format_description! macro or any of the various parsing methods.

If the format description is statically known, you should use the format_description! macro. This is identical to the format_description::parse method, but runs at compile-time, throwing an error if the format description is invalid. If you do not know the desired format statically (such as if you are using one provided by the user), you should use the format_description::parse_owned method (or similar method in the format_description module), which is fallible.

Format descriptions have components and literals. Literals are formatted and parsed as-is. Components are the mechanism by which values (such as a Time or Date) are dynamically formatted and parsed. They have significant flexibility, allowing for differences in padding, variable widths for subsecond values, numerical or textual representations, and more.

Either a literal or a component may be present at the start of the format description. It is valid to have both consecutive literals and consecutive components. Components must be fully contained between brackets with optional whitespace. Escaping behavior varies by version, and is described below.

Versioning

There are multiple versions of the format description syntax in time. Similar to Rust editions, all versions are and will remain supported indefinitely. Some features may only be available in newer versions for technical reasons.

In most cases, you do not need to worry about the version of the format description. However, there are some differences.

Differences

LiteralVersion 1Version 2
[[[\[
]]\]
\\\\

[first] and [optional] are supported in both version 1 and version 2, but individual methods may prevent their use. This is because some methods return a format description that is entirely borrowed. However, when parsing [first] and [optional], the generated sequence is necessarily owned. For this reason, you will need to use the format_description::parse_owned method or the format_description! macro to use these components.

Version used

format_description::parse uses version 1 unconditionally. This is the only method that has a non-configurable version. format_description::parse_borrowed and format_description::parse_owned require the user to specify the version. If the version is not valid, compilation will fail. format_description! defaults to version 1, but can be configured to use a different version.

Configuring format_description!

For backwards-compatibility reasons, the format_description! macro defaults to version 1. If you want to use a different version, you can do so by setting the version parameter to 2. Note that this is only necessary if you are relying on a difference in behavior between the versions.

use time::macros::format_description;
let _ = format_description!("[hour]:[minute]:[second]"); // Version 1 is implied.
let _ = format_description!(version = 1, "[hour]:[minute]:[second]");
let _ = format_description!(version = 2, "[hour]:[minute]:[second]");

Attempting to provide an invalid version will result in a compile-time error.

use time::macros::format_description;
// 0 is not a valid version, so compilation will fail.
let _ = format_description!(version = 0, "[hour]:[minute]:[second]");

Version 1

version 1 top-level syntax

[[ produces a literal [. No other character must be escaped.

Version 2

version 2 top-level syntax

\ is used to begin an escape sequence. Currently, the only valid escape sequences are \[, \], and \\. Any other character following \ is invalid.

Components

Follows is the syntax for all components in alphabetical order. Any of the following may be used where component is present in the above diagram. "Whitespace" refers to any non-empty sequence of ASCII whitespace characters.

  • Day of month: [day]

    syntax for day component

    The padded value has a width of 2. You can choose between padding with zeroes, spaces, or having no padding at all. The default is to pad the value with zeroes.

  • First item: [first]

    syntax for first component

    A series of FormatItems (or OwnedFormatItems) where, when parsing, the first successful parse is used. When formatting, the first item is used.

    format_description refers to a complete format description that is nested; whitespace (including leading and trailing) is significant.

  • End of input: [end]

    syntax for end component

    This component indicates the end of the input. When formatting, it is a no-op. When parsing, it will only succeed if there is no further input. It does not consume any input.

    There is no customization available for this component.

  • Clock hour: [hour]

    syntax for hour component

    The padded value has a width of 2. You can choose between padding with zeroes, spacing, or having no padding at all. The default is to pad the value with zeroes.

    Users have the option to choose between two representations. One is the 12-hour clock, frequently used in the Anglosphere, while the alternative (the 24-hour clock) is frequently used elsewhere. The 12-hour clock is typically used in conjunction with AM/PM.

  • Ignore: [ignore count:X]

    syntax for ignore component

    When parsing, this ignores the indicated number of bytes. This component is a no-op when formatting. The count modifier is mandatory. Its value must be a positive integer.

  • Minute within the clock hour: [minute]

    syntax for minute component

    The padded value has a width of 2. You can choose between padding with zeroes, spaces, or having no padding at all. The default is to pad the value with zeroes.

  • Month: [month]

    syntax for month component

    The padded value has a width of 2. You can choose between padding with zeroes, spaces, or having no padding at all. The default is to pad the value with zeroes.

    Users have the option to choose between three representations. The default is numerical. Alternatives are long and short, both of which are textual formats and have no padding. The long format is the full English name of the month, while the short format is the first three letters of it.

    When parsing, there is the option to consume text-based formats case-insensitively.

  • Whole hours offset from UTC: [offset_hour]

    syntax for offset hour component

    The padded value has a width of 2. You can choose between padding with zeroes, spaces, or having no padding at all. The default is to pad the value with zeroes.

    Users have the option to choose whether the sign is automatic (the default) or mandatory. If the sign is automatic, it will only be present when the value is negative. If mandatory, it will always be present.

  • Minutes within the hour offset from UTC: [offset_minute]

    syntax for offset minute component

    The padded value has a width of 2. You can choose between padding with zeroes, spaces, or having no padding at all. The default is to pad the value with zeroes.

    This value is always positive. As such, it has no sign.

  • Seconds within the minute offset from UTC: [offset_second]

    syntax for offset second component

    The padded value has a width of 2. You can choose between padding with zeroes, spaces, or having no padding at all. The default is to pad the value with zeroes.

    This value is always positive. As such, it has no sign.

  • Optional items: [optional]

    syntax for optional component

    An item that may or may not be present while parsing. While formatting, the value is always present.

    format_description refers to a complete format description that is nested; whitespace (including leading and trailing) is significant.

  • Day of year: [ordinal]

    syntax for ordinal component

    The padded value has a width of 3. You can choose between padding with zeroes, spaces, or having no padding at all. The default is to pad the value with zeroes.

  • AM/PM: [period]

    syntax for period component

    Users have the option to choose whether the value is uppercase or lowercase. This component is typically used in conjunction with the hour of the day with repr:12.

    When parsing, there is the option to consume text-based formats case-insensitively.

  • Second within the clock minute: [second]

    syntax for second component

    The padded value has a width of 2. You can choose between padding with zeroes, spaces, or having no padding at all. The default is to pad the value with zeroes.

  • Subsecond within the clock second: [subsecond]

    syntax for subsecond component

    Users have the choice of how many digits should be displayed or parsed. By default, this is one or more, where the minimum number of digits will be used when formatting and any nonzero number of digits are accepted by the parser (though digits after the ninth will be discarded). There is the option to require a fixed number of digits between one and nine. When formatting, the value is not rounded if more digits would otherwise be present.

  • Unix timestamp: [unix_timestamp]

    syntax for unix timestamp component

    Users can choose between four levels of precision: second (the default), millisecond, microsecond, and nanosecond. The sign can also be made mandatory rather than optional.

  • Week of the year: [week_number]

    syntax for week number component

    The padded value has a width of 2. You can choose between padding with zeroes, spaces, or having no padding at all. The default is to pad the value with zeroes.

    Users can choose between three representations: iso (the default), sunday, and monday. ISO week numbers are in between 1 and 53, while others are between 0 and 53. ISO week one is the Monday-to-Sunday week that contains January 4. Week one of other representations begins on the first instance of that day in the calendar year (e.g. Sunday-based week numbering has week one start on the first Sunday of the year).

  • Day of the week: [weekday]

    syntax for weekday component

    Users can choose between a number of representations for the day of the week. There are long (the default) and short, both of which are textual representations; the long representation is the weekday's full name in English, while the short is the first three letters. There are also sunday and monday representations, which are numerical. These formats are either zero to six or one to seven (depending on whether one_indexed is false or true, respectively), with the named day being at the start of that range.

    When parsing, there is the option to consume text-based formats case-insensitively.

  • Year: [year]

    syntax for year component

    The padded value has a width of 4. You can choose between padding with zeroes, spaces, or having no padding at all. The default is to pad the value with zeroes.

    Users can choose between two representations: the full year (the default) and the last two digits of the year. This should be relatively straightforward. Note that when parsing, if only the last two digits of the year are present, the value returned may not be what was expected — if the return is successful at all (it's not guaranteed).

    There are two bases for the year: calendar and iso_week. The former is what you want if using the month, day, ordinal, or similar. You likely only want to use iso_week if you are using the week number with repr:iso. Don't be like Twitter; know which should be used when.

    Users have the option to choose whether the sign is automatic (the default) or mandatory. If the sign is automatic, it will only be present when the value is negative or if the large-dates feature is enabled and the value contains more than four digits. If mandatory, it will always be present.

    When the large-dates feature is enabled, ambiguities may exist when parsing. For example, if a year is immediately followed by the week number, the parser will eagerly consume six digits even if the year should only be four and the week number the remaining two.

Well-known format descriptions

A number of well-known format descriptions are provided by time. These are intended to be used when you need to be fully compliant with a specification. Many specifications have various edge-cases that are difficult to model with a custom format description. Using a well-known format description allows you to handle all relevant edge cases.

Guarantees

The guarantees provided by well-known formats are deliberately minimal. The only guarantees, unless otherwise documented, are:

  • When formatting, the output will be valid according to the specification.
  • Parsing will succeed if and only if the input is valid according to the specification.

If you are expecting a specific output and not just any valid output, you should use a custom format.

Mutual agreement

Some well-known format descriptions require mutual agreement for certain behavior. For example, ISO 8601 requires that the year be four digits unless additional digits are mutually agreed upon. time inherently has no way to enforce mutual agreement. As such, it is assumed to be present. If you are using the default configuration, you must ensure that the other party is able to accept the formatted value.

ISO 8601

The format described in ISO 8601. It can be found in the api documentation here.

The examples and format description shown below are for the default configuration. Various options are available for this format which you can change. Additional information can be found in the configuration api documentation.

Note: When using the time::serde::iso8601 module in conjunction with serde, the default configuration is different. In that case, the years are always six digits and preceded by a sign.

Examples:

  • 1997-11-12T09:55:06.000000000-06:00
  • 2022-09-08T13:55:24.000000000+02:00
  • 2010-03-14T18:32:03.000000000Z

Approximate format description:
[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:9][offset_hour]:[offset_minute]

Approximate format description when the time zone is UTC:
[year]-[month]-[day]T[hour]:[minute]:[second].[subsecond digits:9]Z

RFC 2822

The format described in RFC 2822. It can be found in the api documentation here.

Examples:

  • Wed, 12 Nov 1997 09:55:06 -0600
  • Thu, 08 Sep 2022 13:55:24 +0200
  • Sun, 14 Mar 2010 18:32:03 +0000

Approximate format description:
[weekday repr:short], [day] [month repr:short] [year] [hour]:[minute]:[second] [offset_hour][offset_minute]

RFC 3339

The format described in RFC 3339. It can be found in the api documentation here.

Examples:

  • 1997-11-12T09:55:06-06:00
  • 2022-09-08T13:55:24+02:00
  • 2010-03-14T18:32:03Z

Approximate format description:
[year]-[month]-[day]T[hour]:[minute]:[second][offset_hour]:[offset_minute]

Approximate format description when the time zone is UTC:
[year]-[month]-[day]T[hour]:[minute]:[second]Z

Formatting

Parsing

Macros

Extension traits

The time crate contains two extension traits: time::ext::NumericalDuration and time::ext::NumericalStdDuration. These traits exist to make writing code involving durations (both from the time crate and the standard library) cleaner to read. Rather than writing Duration::seconds(5), it is possible to write 5.seconds(). It is possible to use floating point literals such that 1.5.weeks() is equivalent to 3.days() + 12.hours().

NumericalDuration provides the following methods that return a time::Duration:

  • .nanoseconds()
  • .microseconds()
  • .milliseconds()
  • .seconds()
  • .minutes()
  • .hours()
  • .days()
  • .weeks()

NumericalStdDuration provides the following methods that return a core::time::Duration:

  • .std_nanoseconds()
  • .std_microseconds()
  • .std_milliseconds()
  • .std_seconds()
  • .std_minutes()
  • .std_hours()
  • .std_days()
  • .std_weeks()

The NumericalDuration trait is implemented for i64 and f64, such that both integer and float literals are able to use the methods. The NumericalStdDuration trait is implemented for u64 and f64 for the same reasons, though the latter will perform a runtime check ensuring the value is non-negative.

While it is possible to use these extension methods on non-literals, such usage is discouraged for ease of reading.

Utility functions

Interop with stdlib

Feature flags

serde

rand

quickcheck

Guarantees

Period type

tzdb support

0.2 to 0.3

Disclaimer

Date algorithms

Internal representation

Hacks

Testing

Benchmarks

Resources