_Time

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 44

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"] }

2. 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}");

3. 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

4. Manipulate dates and use Duration s:

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 Date s
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 PrimitiveDateTime s
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 OffsetDateTime s
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

Literal Version 1 Version 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

literal

[ whitespace component whitespace ]

[[

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

Version 2

literal

[ whitespace component whitespace ]

\ [

\ 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]

day whitespace padding: zero

space

none

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]

first whitespace [ format_description ] whitespace

A series of FormatItem s (or OwnedFormatItem s) 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]

end

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]

hour whitespace padding: zero

space

none

repr: 24

12

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]

ignore whitespace count: number > 0

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]

minute whitespace padding: zero

space

none

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]

month whitespace padding: zero

space

none

repr: numerical

long

short

case_sensitive: true

false

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]


offset_hour whitespace padding: zero

space

none

sign: automatic

mandatory

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]

offset_minute whitespace padding: zero

space

none

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]

offset_second whitespace padding: zero

space

none

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]

optional whitespace [ format_description ]


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]

ordinal whitespace padding: zero

space

none

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]

period whitespace case: lower

upper

case_sensitive: true

false

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]

second whitespace padding: zero

space

none

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]


subsecond whitespace digits: 1+

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]

unix_timestamp whitespace precision: second

millisecond

microsecond

nanosecond

sign: automatic

mandatory

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]


week_number whitespace padding: zero

space

none

repr: iso

sunday

monday

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]

weekday whitespace repr: long

short

sunday

monday

one_indexed: false

true

case_sensitive: true

false

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]
year whitespace padding: zero

space

none

repr: full

last_two

base: calendar

iso_week

sign: automatic

mandatory

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
GitHub
Issue tracker
Discussions
crates.io
docs.rs

You might also like