Skip to content
Open
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
35 changes: 14 additions & 21 deletions rust/src/python/helpers.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#![allow(clippy::useless_conversion)]
use std::cmp::Ordering;

use pyo3::{
prelude::*,
Expand Down Expand Up @@ -308,6 +307,9 @@ pub fn precise_diff<'py>(
hour_diff -= 1;
}

// The time of day of dt2 is earlier than that of dt1
let time_borrow = hour_diff < 0;

if hour_diff < 0 {
hour_diff += 24;
day_diff -= 1;
Expand All @@ -332,26 +334,17 @@ pub fn precise_diff<'py>(
let days_in_month =
DAYS_PER_MONTHS[usize::from(helpers::is_leap(dtinfo2.year))][dtinfo2.month as usize];

match day_diff.cmp(&(days_in_month - days_in_last_month)) {
Ordering::Less => {
// We don't have a full month, we calculate days
if days_in_last_month < dtinfo1.day {
day_diff += dtinfo1.day;
} else {
day_diff += days_in_last_month;
}
}
Ordering::Equal => {
// We have exactly a full month
// We remove the days difference
// and add one to the months difference
day_diff = 0;
month_diff += 1;
}
Ordering::Greater => {
// We have a full month
day_diff += days_in_last_month;
}
if dtinfo2.day == days_in_month && !time_borrow {
// dt2 is the last day of its month and dt1's day does not
// exist in it, so adding the months to dt1 lands exactly
// on dt2 (e.g. Jan 31 + 1 month = Feb 28): a full month
day_diff = 0;
month_diff += 1;
} else if days_in_last_month < dtinfo1.day {
// We don't have a full month, we calculate days
day_diff += dtinfo1.day;
} else {
day_diff += days_in_last_month;
}

month_diff -= 1;
Expand Down
20 changes: 9 additions & 11 deletions src/pendulum/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,8 @@ def precise_diff(
hour_diff += 24
d_diff -= 1

# A negative d_diff here means the time of day of d2 is earlier than d1's
time_borrow = d_diff < 0
y_diff = d2.year - d1.year
m_diff = d2.month - d1.month
d_diff += d2.day - d1.day
Expand All @@ -272,20 +274,16 @@ def precise_diff(
days_in_last_month = DAYS_PER_MONTHS[leap][month]
days_in_month = DAYS_PER_MONTHS[int(is_leap(d2.year))][d2.month]

if d_diff < days_in_month - days_in_last_month:
# We don't have a full month, we calculate days
if days_in_last_month < d1.day:
d_diff += d1.day
else:
d_diff += days_in_last_month
elif d_diff == days_in_month - days_in_last_month:
# We have exactly a full month
# We remove the days difference
# and add one to the months difference
if d2.day == days_in_month and not time_borrow:
# d2 is the last day of its month and d1's day does not
# exist in it, so adding the months to d1 lands exactly
# on d2 (e.g. Jan 31 + 1 month = Feb 28): a full month
d_diff = 0
m_diff += 1
elif days_in_last_month < d1.day:
# We don't have a full month, we calculate days
d_diff += d1.day
else:
# We have a full month
d_diff += days_in_last_month

m_diff -= 1
Expand Down
4 changes: 2 additions & 2 deletions tests/date/test_diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ def test_diff_for_humans_other_and_month():

assert today.diff_for_humans(today.add(months=1)) == "1 month before"

with pendulum.travel_to(pendulum.datetime(2017, 1, 31)):
with pendulum.travel_to(pendulum.local(2017, 1, 31)):
today = pendulum.today().date()

assert today.diff_for_humans(today.add(weeks=4)) == "1 month before"
Expand Down Expand Up @@ -314,7 +314,7 @@ def test_diff_for_humans_other_and_future_month():
assert today.diff_for_humans(today.subtract(weeks=4)) == "4 weeks after"
assert today.diff_for_humans(today.subtract(months=1)) == "1 month after"

with pendulum.travel_to(pendulum.datetime(2017, 2, 28)):
with pendulum.travel_to(pendulum.local(2017, 2, 28)):
today = pendulum.today().date()

assert today.diff_for_humans(today.subtract(weeks=4)) == "1 month after"
Expand Down
27 changes: 27 additions & 0 deletions tests/test_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,33 @@ def test_precise_diff() -> None:
assert_diff(diff, days=6, hours=0)


def test_precise_diff_partial_month() -> None:
# May 12 to June 11 is 30 days, not a full month
diff = precise_diff(datetime(2020, 5, 12, 4), datetime(2020, 6, 11, 10))
assert_diff(diff, days=30, hours=6)

diff = precise_diff(datetime(2020, 6, 11, 10), datetime(2020, 5, 12, 4))
assert_diff(diff, days=-30, hours=-6)

# https://github.com/python-pendulum/pendulum/issues/790
diff = precise_diff(datetime(2024, 1, 3), datetime(2024, 2, 1))
assert_diff(diff, days=29)


def test_precise_diff_end_of_month() -> None:
# Jan 31 + 1 month is clamped to Feb 28, so this is a full month
diff = precise_diff(datetime(2023, 1, 31), datetime(2023, 2, 28))
assert_diff(diff, months=1)

# https://github.com/python-pendulum/pendulum/issues/402
diff = precise_diff(datetime(2019, 8, 30), datetime(2020, 2, 29))
assert_diff(diff, months=6)

# Not a full month if the time of day has not been reached yet
diff = precise_diff(datetime(2023, 1, 31, 10), datetime(2023, 2, 28, 9))
assert_diff(diff, days=27, hours=23)


def test_precise_diff_timezone() -> None:
paris = pendulum.timezone("Europe/Paris")
toronto = pendulum.timezone("America/Toronto")
Expand Down