Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,21 @@ repository = "https://github.com/gpgkd906/date_component/"
documentation = "https://gpgkd906.github.io/date_component/date_component/"
readme = "README.md"

[lib]
name = "date_component"
test = false
doctest = false

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
chrono = "0.4"
chrono-tz = "0.10"
chrono = "0.4.41"
chrono-tz = "0.10.4"
lazy_static = "1.5.0"

[dev-dependencies]
test-case = "3.3"
criterion = "0.5.1"
test-case = "3.3.1"
criterion = "0.7.0"

[[bench]]
name = "benchmark"
Expand Down
32 changes: 26 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,13 @@ pub mod date_component {
/// Returns a DateComponent object that represents the difference between the from and to datetime.
pub fn calculate<T: chrono::TimeZone>(from_datetime: &DateTime<T>, to_datetime: &DateTime<T>) -> DateComponent {
let timezone = from_datetime.timezone();
let utc_from = from_datetime.with_timezone(&Utc);
let utc_to = to_datetime.with_timezone(&Utc);
let to_datetime_in_from_tz = to_datetime.with_timezone(&timezone);

let duration = utc_from.signed_duration_since(utc_to);
let duration = from_datetime.clone().signed_duration_since(to_datetime.clone());
let seconds = duration.num_seconds();
let (start, end, invert) = match seconds {
x if x <= 0 => (from_datetime, to_datetime, false),
_ => (to_datetime, from_datetime, true),
x if x <= 0 => (from_datetime.clone(), to_datetime_in_from_tz, false),
_ => (to_datetime_in_from_tz, from_datetime.clone(), true),
};

// Use mutable variables for interval components
Expand Down Expand Up @@ -131,4 +130,25 @@ pub mod date_component {
}

#[cfg(test)]
mod tests;
mod internal_tests {
use super::*;
use chrono::prelude::*;

#[test]
fn test_get_nearest_day_before_regular() {
let dt = get_nearest_day_before(2023, 2, 30, 0, 0, 0, &Utc);
assert_eq!(dt.day(), 28);
}

#[test]
fn test_get_nearest_day_before_leap() {
let dt = get_nearest_day_before(2024, 2, 30, 0, 0, 0, &Utc);
assert_eq!(dt.day(), 29);
}

#[test]
fn test_get_nearest_day_before_big_month() {
let dt = get_nearest_day_before(2023, 1, 32, 0, 0, 0, &Utc);
assert_eq!(dt.day(), 31);
}
}
4 changes: 4 additions & 0 deletions tests/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
mod test_basic_units;
mod test_dst;
mod test_edge_cases;
mod test_integrations;
269 changes: 1 addition & 268 deletions src/tests.rs → tests/test_basic_units.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::date_component::*;
use date_component::date_component::*;
use chrono::prelude::*;
use test_case::test_case;
use chrono_tz::US::Pacific;
Expand All @@ -8,7 +8,6 @@ use chrono_tz::Asia::Kolkata;
use chrono_tz::Europe::Paris;
use chrono_tz::Pacific::Midway;
use chrono_tz::Africa::Lome;
use chrono_tz::America::Los_Angeles;

#[test_case(1998, 1999; "world cup")]
#[test_case(1999, 2000; "end of century")]
Expand Down Expand Up @@ -942,269 +941,3 @@ fn test_previous_seconds(
assert_eq!(sut.interval_seconds, 1);
assert_eq!(sut.invert, true);
}

#[test]
fn test_next_year_month_day_hour_minute_second() {
let from = Utc.with_ymd_and_hms(2020, 1, 6, 0, 0, 0).unwrap();
let to = Utc.with_ymd_and_hms(2021, 2, 14, 1, 1, 1).unwrap();
let duration = to.signed_duration_since(from);

let sut = calculate(&from, &to);
assert_eq!(
sut,
DateComponent {
year: 1,
month: 1,
week: 1,
modulo_days: 1,
day: 8,
hour: 1,
minute: 1,
second: 1,
interval_days: duration.num_days().abs() as isize,
interval_hours: duration.num_hours().abs() as isize,
interval_minutes: duration.num_minutes().abs() as isize,
interval_seconds: duration.num_seconds().abs() as isize,
invert: false,
}
);
}

#[test]
fn test_previous_year_month_day_hour_minute_second() {
let from = Utc.with_ymd_and_hms(2021, 2, 14, 1, 1, 1).unwrap();
let to = Utc.with_ymd_and_hms(2020, 1, 6, 0, 0, 0).unwrap();
let duration = to.signed_duration_since(from);

let sut = calculate(&from, &to);
assert_eq!(
sut,
DateComponent {
year: 1,
month: 1,
week: 1,
modulo_days: 1,
day: 8,
hour: 1,
minute: 1,
second: 1,
interval_days: duration.num_days().abs() as isize,
interval_hours: duration.num_hours().abs() as isize,
interval_minutes: duration.num_minutes().abs() as isize,
interval_seconds: duration.num_seconds().abs() as isize,
invert: true,
}
);
}

#[test]
fn test_difference_during_dst() {
let start = Los_Angeles.with_ymd_and_hms(2022, 3, 14, 1, 30, 0).unwrap();
let end = Los_Angeles.with_ymd_and_hms(2022, 3, 14, 3, 30, 0).unwrap();
let diff = calculate(&start, &end);
assert_eq!(diff.hour, 2);
}

#[test]
fn test_dst_transition_backward() {
let start = Los_Angeles.with_ymd_and_hms(2022, 11, 6, 0, 30, 0).unwrap();
let end = Los_Angeles.with_ymd_and_hms(2022, 11, 6, 2, 30, 0).unwrap();
let diff = calculate(&start, &end);
println!("{:?}", diff);
assert_eq!(diff.hour, 3); // Ensure that the difference is 3 hours due to the DST transition
}

fn assert_date_component_eq(actual: DateComponent, expected: DateComponent) {
assert_eq!(actual.year, expected.year);
assert_eq!(actual.month, expected.month);
assert_eq!(actual.week, expected.week);
assert_eq!(actual.modulo_days, expected.modulo_days);
assert_eq!(actual.day, expected.day);
assert_eq!(actual.hour, expected.hour);
assert_eq!(actual.minute, expected.minute);
assert_eq!(actual.second, expected.second);
assert_eq!(actual.interval_seconds, expected.interval_seconds);
assert_eq!(actual.interval_minutes, expected.interval_minutes);
assert_eq!(actual.interval_hours, expected.interval_hours);
assert_eq!(actual.interval_days, expected.interval_days);
assert_eq!(actual.invert, expected.invert);
}

#[test]
fn test_dst_start_transition_los_angeles() {
let before_dst_start = Los_Angeles.with_ymd_and_hms(2022, 3, 13, 1, 59, 59).unwrap();
let after_dst_start = Los_Angeles.with_ymd_and_hms(2022, 3, 13, 3, 0, 0).unwrap();
let diff = calculate(&before_dst_start, &after_dst_start);
let expected = DateComponent {
year: 0,
month: 0,
week: 0,
modulo_days: 0,
day: 0,
hour: 0,
minute: 0,
second: 1,
interval_seconds: 1,
interval_minutes: 0,
interval_hours: 0,
interval_days: 0,
invert: false,
};
assert_date_component_eq(diff, expected);
}

#[test]
fn test_dst_end_transition_los_angeles() {
let before_dst_end = Los_Angeles.with_ymd_and_hms(2022, 11, 6, 0, 59, 59).unwrap();
let after_dst_end = Los_Angeles.with_ymd_and_hms(2022, 11, 6, 2, 0, 0).unwrap();
let diff = calculate(&before_dst_end, &after_dst_end);
let expected = DateComponent {
year: 0,
month: 0,
week: 0,
modulo_days: 0,
day: 0,
hour: 2,
minute: 0,
second: 1,
interval_seconds: 7201,
interval_minutes: 120,
interval_hours: 2,
interval_days: 0,
invert: false,
};
assert_date_component_eq(diff, expected);
}

#[test]
fn test_dst_start_transition_paris() {
let before_dst_start = Paris.with_ymd_and_hms(2022, 3, 27, 1, 59, 59).unwrap();
let after_dst_start = Paris.with_ymd_and_hms(2022, 3, 27, 3, 0, 0).unwrap();
let diff = calculate(&before_dst_start, &after_dst_start);
let expected = DateComponent {
year: 0,
month: 0,
week: 0,
modulo_days: 0,
day: 0,
hour: 0,
minute: 0,
second: 1,
interval_seconds: 1,
interval_minutes: 0,
interval_hours: 0,
interval_days: 0,
invert: false,
};
assert_date_component_eq(diff, expected);
}

#[test]
fn test_dst_end_transition_paris() {
let before_dst_end = Paris.with_ymd_and_hms(2022, 10, 30, 3, 0, 0).unwrap();
let after_dst_end = Paris.with_ymd_and_hms(2022, 10, 30, 1, 59, 59).unwrap();
let diff = calculate(&before_dst_end, &after_dst_end);
let expected = DateComponent {
year: 0,
month: 0,
week: 0,
modulo_days: 0,
day: 0,
hour: 2,
minute: 0,
second: 1,
interval_seconds: 7201,
interval_minutes: 120,
interval_hours: 2,
interval_days: 0,
invert: true,
};
assert_date_component_eq(diff, expected);
}

#[test]
fn test_leap_year_calculation() {
// leap year
let from = Utc.with_ymd_and_hms(2020, 2, 28, 0, 0, 0).unwrap();
let to = Utc.with_ymd_and_hms(2020, 3, 1, 0, 0, 0).unwrap();

let diff = calculate(&from, &to);
assert_eq!(diff.interval_days, 2);
assert_eq!(diff.invert, false);

// compare with non leap year
let from = Utc.with_ymd_and_hms(2023, 2, 28, 0, 0, 0).unwrap();
let to = Utc.with_ymd_and_hms(2023, 3, 1, 0, 0, 0).unwrap();

let diff = calculate(&from, &to);
assert_eq!(diff.interval_days, 1);
assert_eq!(diff.invert, false);
}

#[test]
fn test_month_edge_cases() {
// from big month to small month
let from = Utc.with_ymd_and_hms(2023, 1, 31, 0, 0, 0).unwrap();
let to = Utc.with_ymd_and_hms(2023, 2, 1, 0, 0, 0).unwrap();
let diff = calculate(&from, &to);
assert_eq!(diff.interval_days, 1);

// from small month to big month
let from = Utc.with_ymd_and_hms(2023, 2, 28, 0, 0, 0).unwrap();
let to = Utc.with_ymd_and_hms(2023, 3, 1, 0, 0, 0).unwrap();
let diff = calculate(&from, &to);
assert_eq!(diff.interval_days, 1);

// from big month to big month
let from = Utc.with_ymd_and_hms(2023, 7, 31, 0, 0, 0).unwrap();
let to = Utc.with_ymd_and_hms(2023, 8, 1, 0, 0, 0).unwrap();
let diff = calculate(&from, &to);
assert_eq!(diff.interval_days, 1);
}

#[test]
fn test_timezone_crossing_date_line() {
use chrono_tz::Pacific::Auckland;
use chrono_tz::America::Los_Angeles;

// forward test
let from = Auckland.with_ymd_and_hms(2023, 1, 1, 0, 0, 0).unwrap();
let to = Los_Angeles.with_ymd_and_hms(2022, 12, 31, 0, 0, 0).unwrap();

let diff = calculate(&from, &to);
assert!(diff.invert);
assert!(diff.interval_hours > 0);

// backward test
let from = Los_Angeles.with_ymd_and_hms(2022, 12, 31, 0, 0, 0).unwrap();
let to = Auckland.with_ymd_and_hms(2023, 1, 1, 0, 0, 0).unwrap();

let diff = calculate(&from, &to);
assert!(!diff.invert);
assert!(diff.interval_hours > 0);
}

#[test]
fn test_exact_one_year_period() {
// non leap year
let from = Utc.with_ymd_and_hms(2022, 1, 1, 0, 0, 0).unwrap();
let to = Utc.with_ymd_and_hms(2023, 1, 1, 0, 0, 0).unwrap();

let diff = calculate(&from, &to);
assert_eq!(diff.year, 1);
assert_eq!(diff.month, 0);
assert_eq!(diff.day, 0);
assert_eq!(diff.interval_days, 365);
assert!(!diff.invert);

// leap year
let from = Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap();
let to = Utc.with_ymd_and_hms(2021, 1, 1, 0, 0, 0).unwrap();

let diff = calculate(&from, &to);
assert_eq!(diff.year, 1);
assert_eq!(diff.month, 0);
assert_eq!(diff.day, 0);
assert_eq!(diff.interval_days, 366);
assert!(!diff.invert);
}
Loading