Rust implementation of
relativedelta
known from Python's dateutil library. Extension to theDuration
from the time library, which allows for calculating datetimes based on a relative representation of date and time.
Put this in your Cargo.toml
:
[dependencies]
relativedelta = "0.2"
serde
: Enable serialization/deserialization via serde.
The RelativeDelta
datatype holds both relative and absolute values for year, month, day, hour, minute, seconds and
nanosecond.
Relative parts are manipulated and accessed through methods typically ending in "s" (e.g. ::with_years
, .and_days
).
Absolute values without "s".
All relative values represents an offset to date and time and therefore can take on both positive and negative values,
and can take on any value within its datatypes limitations. On creation, the Builder
will attempt to aggregate values
up, so e.g. if hours are not in the range [-23;23], the datatype will be updated to instead add or subtract extra
days, with only the remainder as hours.
All offsets are set to zero as default.
Absolute values represents explicit years, months, days and so on. So if one e.g. always seeks a certain day in the
month, one would use the ::with_month
or .and_month
method. All absolute values are Options and set to None
as
default.
RelativeDelta
also holds a weekday value, which is an Option of a tuple with (Weekday, nth)
. This allows one to e.g.
ask for the second tuesday one year from today,
with Utc::now() + RelativeDelta::with_years(1).and_weekday(Some(Weekday::Tue, 2)).new()
.
// Construction
let years1 = RelativeDelta::with_years(1).new();
let months12 = RelativeDelta::with_months(12).new();
assert_eq!(years1, months12);
let years1 = RelativeDelta::with_years(1).and_days(32).new();
// If same parameter is specified twice, only the latest is applied.
let months6 = RelativeDelta::with_months(12).with_months(6).new();
assert_eq!(months6, RelativeDelta::with_months(6).new());
// Below is identical to: RelativeDelta::yysmmsdds(Some(2020), 1, Some(1), 3, None, 12).new();
let rddt = RelativeDelta::with_year(2020).and_years(1).and_month(Some(1)).and_months(3).and_days(12).new();
// Two or more RelativeDeltas can be added and substracted. However, note that constants are lost in the process.
let lhs = RelativeDelta::yysmmsdds(Some(2020), - 4, Some(1), 3, None, 0).new();
let rhs = RelativeDelta::yysmmsdds(Some(2020), 1, Some(1), 42, None, 0).new();
assert_eq!(lhs + rhs, RelativeDelta::with_years(-3).and_months(45).new());
assert_eq!(lhs - rhs, RelativeDelta::with_years(-5).and_months(-39).new());
assert_eq!(-lhs + rhs, RelativeDelta::with_years(5).and_months(39).new());
// The RelativeDelta can be multiplied with a f64.
assert_eq!(rhs * 0.5, RelativeDelta::with_years(2).and_year(Some(2020)).and_months(3).and_month(Some(1)).new());
// This crates party piece is the ability to calculate dates based on already existing chrono::DateTime
// If one would like to get the last day of the month that one is currently in, it could be done with:
println!("{}", Utc::now() + RelativeDelta::with_day(1).and_months(1).and_days(-1).new());
// Above first sets the day of the month to the 1st, then adds a month and subtracts a day.
// One could also request the first monday after one year by
let first_monday_after_one_year = RelativeDelta::with_years(1).and_weekday(Some((Weekday::Mon, 1))).new();
let d = Utc.ymd(2020, 1, 1).and_hms(0, 0, 0) + first_monday_after_one_year;
assert_eq!(d, Utc.ymd(2021, 1, 4).and_hms(0, 0, 0));
Use "typed-builder" crate to get rid of a lot of builder code