# [chrono-intervals][docsrs]: Grouped time intervals for Rust Create chrono time intervals as "per-day", "per-week" etc. [docsrs]: https://docs.rs/chrono-intervals ## Usage The most convenient way to get intervals is by creating an `IntervalGenerator`. ```rust 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: ```rust 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 is `Grouping::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 _before_ `begin`. You can switch this off if you want only full intervals that are strickly _after_ `begin`. - Whether the last interval extends to _after_ `end` or not: By default, the last interval will end at the boundary _after_ `end`. You can switch this off if you want only full intervals that are strickly _before_ `end`. Let's look at an example with all configuration options used: ```rust 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 the `begin` and `end` and have a precision of 1ms. This is pretty close to the default `IntervalGenerator` behavior, just that you have to specify a `Grouping`. - `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: ```rust 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: ```rust 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`: ```rust 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), ), ] ); ```