3 releases (breaking)
0.3.0 | Nov 5, 2022 |
---|---|
0.2.0 | Nov 1, 2022 |
0.1.0 | Oct 31, 2022 |
#288 in Date and time
398 downloads per month
30KB
286 lines
chrono-intervals: Grouped time intervals for Rust
Create chrono time intervals as "per-day", "per-week" etc.
Usage
The most convenient way to get intervals is by creating an
IntervalGenerator
.
use chrono::{DateTime, TimeZone, Utc};
use chrono_intervals::{IntervalGenerator};
let begin = DateTime::parse_from_rfc3339("2022-06-25T08:23:45.000000Z").unwrap();
let end = DateTime::parse_from_rfc3339("2022-06-27T09:31:12.000000Z").unwrap();
let daily_intervals = IntervalGenerator::new().get_intervals(begin, end);
assert_eq!(
daily_intervals,
vec![
(
Utc.ymd(2022, 6, 25).and_hms(0, 0, 0),
Utc.ymd(2022, 6, 25).and_hms_milli(23, 59, 59, 999),
),
(
Utc.ymd(2022, 6, 26).and_hms(0, 0, 0),
Utc.ymd(2022, 6, 26).and_hms_milli(23, 59, 59, 999),
),
(
Utc.ymd(2022, 6, 27).and_hms(0, 0, 0),
Utc.ymd(2022, 6, 27).and_hms_milli(23, 59, 59, 999),
),
]
);
The IntervalGenerator
can be configured in many ways. Let's look at an
example of retrieving monthly intervals but in the Pacific Daylight Time
(PDT) timezone:
use chrono::{DateTime, TimeZone, Utc};
use chrono_intervals::{Grouping, IntervalGenerator};
// We want to obtain monthly intervals for month in PDT instead of in UTC.
let begin = DateTime::parse_from_rfc3339("2022-06-10T12:23:45.000000-07:00").unwrap();
let end = DateTime::parse_from_rfc3339("2022-08-26T12:23:45.000000-07:00").unwrap();
// PDT is 7h behind of UTC (towards the **west**), thus the
// `offset_west_seconds` are 7*3600
let pdt_offset_west_seconds = 7 * 3600;
let monthly_intervals = IntervalGenerator::new()
.with_grouping(Grouping::PerMonth)
.with_offset_west_secs(pdt_offset_west_seconds)
.get_intervals(begin, end);
// In UTC, we expect the intervals to start 7h after the month boundary.
assert_eq!(
monthly_intervals,
vec![
(
Utc.ymd(2022, 6, 1).and_hms(7, 0, 0),
Utc.ymd(2022, 7, 1).and_hms_milli(6, 59, 59, 999),
),
(
Utc.ymd(2022, 7, 1).and_hms(7, 0, 0),
Utc.ymd(2022, 8, 1).and_hms_milli(6, 59, 59, 999),
),
(
Utc.ymd(2022, 8, 1).and_hms(7, 0, 0),
Utc.ymd(2022, 9, 1).and_hms_milli(6, 59, 59, 999),
),
]
);
Configuration options and defaults
Here is an overview of configurable options and their defaults:
- The interval grouping: You can choose any grouping represented in,
Grouping
, the default isGrouping::PerDay
. - The time span between the end of one interval and the beginning of the
next (precision): This defaults to 1ms but can be overwritten by passing
an arbitrary
chrono::Duration
. We do not check that the precision is reasonable. You probably want to set it to the smallest duration that you still consider, e.g. milliseconds or microseconds. - The offset in seconds towards the west of your local timezone: If you want time intervals for e.g. Pacific Daylight Time (PDT) which is at GMT-7, you have to pass 7*3600, so the time difference in seconds with a shift towards the west as positive values. Central European Time (CET) at GMT+1 for example would need -3600 offset seconds towards the west.
- Whether the first interval extends to before
begin
or not: By default, the first interval will start on the boundary beforebegin
. You can switch this off if you want only full intervals that are strickly afterbegin
. - Whether the last interval extends to after
end
or not: By default, the last interval will end at the boundary afterend
. You can switch this off if you want only full intervals that are strickly beforeend
.
Let's look at an example with all configuration options used:
use chrono::{DateTime, Duration, TimeZone, Utc};
use chrono_intervals::{Grouping, IntervalGenerator};
let begin = DateTime::parse_from_rfc3339("2022-10-02T08:23:45.000000Z").unwrap();
let end = DateTime::parse_from_rfc3339("2022-10-18T08:23:45.000000Z").unwrap();
let inter_gen = IntervalGenerator::new()
.with_grouping(Grouping::PerWeek)
.with_precision(Duration::microseconds(1))
.with_offset_west_secs(-3600)
.without_extended_begin()
.without_extended_end();
let weekly_intervals = inter_gen.get_intervals(begin, end);
assert_eq!(
weekly_intervals,
vec![
(
Utc.ymd(2022, 10, 2).and_hms(23, 0, 0),
Utc.ymd(2022, 10, 9).and_hms_micro(22, 59, 59, 999999),
),
(
Utc.ymd(2022, 10, 9).and_hms(23, 0, 0),
Utc.ymd(2022, 10, 16).and_hms_micro(22, 59, 59, 999999),
),
]
);
Using functions instead of the generator
The generator is the most convenient way. However you can also use two different functions to obtain intervals:
get_extended_utc_intervals
returns grouped intervals which enclose thebegin
andend
and have a precision of 1ms. This is pretty close to the defaultIntervalGenerator
behavior, just that you have to specify aGrouping
.get_utc_intervals_opts
returns grouped intervals and allows to specify all options that the generator also accepts.
Examples
Get daily intervals between two times with default options:
use chrono::{DateTime, TimeZone, Utc};
use chrono_intervals::{Grouping, get_extended_utc_intervals};
let begin = DateTime::parse_from_rfc3339("2022-06-25T08:23:45.000000Z").unwrap();
let end = DateTime::parse_from_rfc3339("2022-06-27T09:31:12.000000Z").unwrap();
let daily_intervals =
get_extended_utc_intervals(begin, end, &Grouping::PerDay, 0);
assert_eq!(
daily_intervals,
vec![
(
Utc.ymd(2022, 6, 25).and_hms(0, 0, 0),
Utc.ymd(2022, 6, 25).and_hms_milli(23, 59, 59, 999),
),
(
Utc.ymd(2022, 6, 26).and_hms(0, 0, 0),
Utc.ymd(2022, 6, 26).and_hms_milli(23, 59, 59, 999),
),
(
Utc.ymd(2022, 6, 27).and_hms(0, 0, 0),
Utc.ymd(2022, 6, 27).and_hms_milli(23, 59, 59, 999),
),
]
);
Get monthly intervals with default options in the Pacific Daylight Time (PDT) timezone:
use chrono::{DateTime, TimeZone, Utc};
use chrono_intervals::{Grouping, get_extended_utc_intervals};
// We want to obtain monthly intervals for months in PDT instead of in UTC.
let begin = DateTime::parse_from_rfc3339("2022-06-10T12:23:45.000000-07:00").unwrap();
let end = DateTime::parse_from_rfc3339("2022-08-26T12:23:45.000000-07:00").unwrap();
// PDT is 7h behind of UTC (towards the **west**), thus the
// `offset_west_seconds` are 7*3600
let pdt_offset_west_seconds = 7 * 3600;
let monthly_intervals =
get_extended_utc_intervals(begin, end, &Grouping::PerMonth, pdt_offset_west_seconds);
// In UTC, we expect the intervals to start 7h after the day boundary.
assert_eq!(
monthly_intervals,
vec![
(
Utc.ymd(2022, 6, 1).and_hms(7, 0, 0),
Utc.ymd(2022, 7, 1).and_hms_milli(6, 59, 59, 999),
),
(
Utc.ymd(2022, 7, 1).and_hms(7, 0, 0),
Utc.ymd(2022, 8, 1).and_hms_milli(6, 59, 59, 999),
),
(
Utc.ymd(2022, 8, 1).and_hms(7, 0, 0),
Utc.ymd(2022, 9, 1).and_hms_milli(6, 59, 59, 999),
),
]
);
Specify options for get_utc_intervals_opts
:
use chrono::{DateTime, Duration, TimeZone, Utc};
use chrono_intervals::{Grouping, get_utc_intervals_opts};
let begin = DateTime::parse_from_rfc3339("2022-06-15T08:23:45.000000Z").unwrap();
let end = DateTime::parse_from_rfc3339("2022-06-30T09:31:12.000000Z").unwrap();
let weekly_intervals =
get_utc_intervals_opts(
begin,
end,
&Grouping::PerWeek,
0,
Duration::microseconds(1), // interval end is 1µs before the next
false, // start on the boundary after `start`
true, // end at the boundary after `end`
);
assert_eq!(
weekly_intervals,
vec![
(
// First interval begins **after** `begin`
Utc.ymd(2022, 6, 20).and_hms(0, 0, 0),
Utc.ymd(2022, 6, 26).and_hms_micro(23, 59, 59, 999999),
),
(
Utc.ymd(2022, 6, 27).and_hms(0, 0, 0),
// Last interval ends **after** `end`
Utc.ymd(2022, 7, 3).and_hms_micro(23, 59, 59, 999999),
),
]
);
Dependencies
~1MB
~18K SLoC