Chrono
β οΈ Work in Progress β οΈ
Chrono is a date and time package for Dart supporting all platforms. It offers strongly-typed data classes for timestamps, timezone-independent (plain/local) date and time, as well as different durations based on the proleptic Gregorian calendar, including:
- arithmetic operations and conversions
- parsing and formatting for a subset of
ISO 8601
,RFC 3339
, andCC 18011:2018
- this is implemented for
Instant
and the date/time classes, but still missing for durations
- this is implemented for
The following features are not implemented, but could be added in the future:
- timezone support (work in progress)
- customizable parsing and formatting
- internationalization
- leap second support
Usage
Since Dart Core also provides classes called DateTime
and Duration
, you might have to add import 'package:chrono/chrono.dart';
manually.
If you want to use classes from both sources, you can use an import prefix:
import 'dart:core' as core;
import 'package:chrono/chrono.dart';
void main() {
final dartCoreDateTime = core.DateTime.now();
final chronoDateTime = DateTime.nowInLocalZone();
}
Timestamps
A timestamp is a unique point on the UTC timeline, stored as the duration since the Unix epoch. Chrono provides these classes:
UnixEpochTimestamp<D>
, generic over aTimeDuration
typeInstant
: a subclass with arbitrary precisionUnixEpochNanoseconds
: a subclass with nanosecond precisionUnixEpochMicroseconds
: a subclass with microsecond precisionUnixEpochMilliseconds
: a subclass with millisecond precisionUnixEpochSeconds
: a subclass with second precision
If you want to store the exact point in time when something happened, e.g., when a message in a chat app was sent, one of these classes is usually the best choice. The specific one is up to you, based on how precise you want to be.
Past dates (e.g., birthdays) should rather be represented as a Date
.
Future timestamps, e.g., for scheduling a meeting, should rather be represented as a DateTime
and the corresponding timezone.
Date and Time
DateTime
combines Date
and Time
without timezone information.
This is also called plain or local time in other languages.
For example, April 23, 2023 at 18:24:20 happened at different moments in different time zones. In Chrono's classes, it would be represented as:
Class | Encoding |
---|---|
DateTime |
2023-04-23T18:24:20 |
Date |
2023-04-23 |
Year |
2023 |
Month |
4 |
YearMonth |
2023-04 |
MonthDay |
--04-23 |
OrdinalDate |
2023-113 |
WeekDate |
2023-W16-7 |
YearWeek |
2023-W16 |
Weekday |
7 |
Time |
18:24:20 |
π
Date
An ISO 8601 calendar date without timezone information, e.g., April 23, 2023.
Dates can be represented by three different classes:
Class | Components | Conversion | Encoding |
---|---|---|---|
Date |
Year + Month + day of month |
.asDate |
2023-04-23 |
WeekDate |
YearWeek + Weekday |
.asWeekDate |
2023-W16-7 |
OrdinalDate |
Year + day of year |
.asOrdinalDate |
2023-113 |
Year
: year in the ISO 8601 calendar (see the class documentation for representation of BCE years)Month
: enum of monthsWeekday
: enum of weekdaysYearMonth
:Year
+Month
, e.g., for getting the length of a month honoring leap yearsMonthDay
:Month
+`Day`
, e.g., for representing birthdays without a year (February 29 is allowed)YearWeek
:Year
+ ISO week from Monday to Sunday
β Time
An ISO 8601 time without timezone information, e.g., 18:24:20.123456.
Since fractional seconds are represented using the fixed-point number Fixed
class of the package fixed, there's basically no limit to the precision.
Chrono does not support leap seconds: It assumes that all minutes are 60 seconds long.
Durations
Durations fall into three categories:
- time-based durations, e.g., 1 hour
- day-based durations, e.g., 2 days
- month-based durations, e.g., 3 months
One day is not always 24 hours long (due to daylight savings time changes), and one month is not always 30/31 days long (due to leap years and different month lengths). Chrono offers different classes for each category (listed by inheritance):
Duration
: abstract base classTimeDuration
: time-based durations: hours, minutes, seconds, milliseconds, etc.FractionalSeconds
: fractional number of seconds with unlimited precision (usingFixed
)Hours
,Minutes
,Seconds
,Milliseconds
,Microseconds
,Nanoseconds
: a whole number of hours/etc.
DateDuration
: day- and month-based durationsFixedDaysDuration
: day-based durations, can beDays
orWeeks
MonthsDuration
: month-based durations, can beMonths
orYears
CompoundDaysDuration
:Months
+Days
CompoundDuration
:Months
+Days
+FractionalSeconds
The Duration
class from Dart Core corresponds to TimeDuration
/FractionalSeconds
, but limited to microsecond precision.
Some duration classes also have a corresponding β¦Duration
class, e.g., Minutes
and MinutesDuration
.
MinutesDuration
is an abstract base class for all time-based durations consisting of a whole number of minutes.
Minutes
is a concrete class extending MinutesDuration
.
When constructing or returning values, you should use Minutes
directly.
However, in parameters, you should accept any MinutesDuration
.
This way, callers can pass not only Minutes
, but also Hours
.
To convert this to Minutes
, call asMinutes
Compound Durations
CompoundDuration
and CompoundDaysDuration
can represent values with mixed signs, e.g., -1 month and 1 day.
When performing additions and subtractions with compound durations, first the Months
are evaluated, then the Days
, and finally the FractionalSeconds
.
For example, adding 1 month and -1 day to 2023-08-31 results in 2023-09-29:
- First, 1 month is added, resulting in 2023-09-30. (September only has 30 days, so the day is clamped.)
- Then, 1 day is subtracted, resulting in 2023-09-29.
(If the order of operations was reversed, the result would be 2023-09-30.)
Serialization
All timestamp, date, and time classes support JSON serialization. (Support for durations will be added in the future.)
Because there are often multiple possibilities of how to encode a value, Chono lets you choose the format:
There are subclasses of JsonConverter
for each class called <Chrono type>As<JSON type>JsonConverter
, e.g., DateTimeAsIsoStringJsonConverter
.
These are all converters and how they encode February 3, 2001 at 4:05:06.007008009 UTC:
json_serializable
If you use json_serializable, you can choose between the following approaches:
-
Annotate your field with the converter you want to use:
@JsonSerializable() class MyClass { const MyClass(this.value); factory MyClass.fromJson(Map<String, dynamic> json) => _$MyClassFromJson(json); @DateTimeAsIsoStringJsonConverter() final DateTime value; Map<String, dynamic> toJson() => _$MyClassToJson(this); }
-
Specify the converter in the
JsonSerializable
annotation so it applies to all fields in that class:@JsonSerializable(converters: [DateTimeAsIsoStringJsonConverter()]) class MyClass { // ... }
-
Create a customized instance of
JsonSerializable
that you can reuse for all your classes:const jsonSerializable = JsonSerializable(converters: [DateTimeAsIsoStringJsonConverter()]); @jsonSerializable class MyClass { // ... }
You can also combine these approaches:
For example, create a customized JsonSerializable
instance with your defaults (approach 3) and overwrite the converter individual fields (approach 1).
Testing
All functions that are based on the current time accept an optional Clock
parameter.
Please have a look at the clock package for how this can be used to overwrite the time during tests.
Chrono uses Glados for property-based testing.
Comparison to other languages
Dart: chrono |
Java/Kotlin | Rust |
---|---|---|
UnixEpochTimestamp<D> |
β | β |
Instant |
β | β |
UnixEpochNanoseconds |
Instant |
std::time::{Instant, SystemTime} |
UnixEpochMicroseconds |
β | β |
UnixEpochMilliseconds |
β | β |
UnixEpochSeconds |
β | β |
DateTime |
LocalDateTime |
chrono::NaiveDateTime |
Date |
LocalDate |
chrono::NaiveDate |
Year |
Year |
β |
YearMonth |
YearMonth |
β |
Month |
Month |
chrono::Month |
MonthDay |
MonthDay |
β |
YearWeek |
β | chrono::IsoWeek |
Weekday |
DayOfWeek |
chrono::Weekday |
Time |
LocalTime |
chrono::NaiveTime |
DateDuration |
Period |
β |
MonthsDuration , Months , Years |
β | chrono::Months |
FixedDaysDuration , Days , Weeks |
β | chrono::Days |
TimeDuration |
Duration |
std::time::Duration |
Clock (from clock) |
Clock |
β |