Skip to content

Commit

Permalink
Fix the computation of total seconds with years and months
Browse files Browse the repository at this point in the history
  • Loading branch information
sdispater committed Jul 4, 2020
1 parent 0735ac4 commit 9274f5c
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 24 deletions.
11 changes: 2 additions & 9 deletions pendulum/datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -754,7 +754,7 @@ def _add_timedelta_(self, delta):
)
elif isinstance(delta, pendulum.Duration):
return self.add(
years=delta.years, months=delta.months, seconds=delta.total_seconds()
years=delta.years, months=delta.months, seconds=delta._total
)

return self.add(seconds=delta.total_seconds())
Expand All @@ -770,14 +770,7 @@ def _subtract_timedelta(self, delta):
"""
if isinstance(delta, pendulum.Duration):
return self.subtract(
years=delta.years,
months=delta.months,
weeks=delta.weeks,
days=delta.remaining_days,
hours=delta.hours,
minutes=delta.minutes,
seconds=delta.remaining_seconds,
microseconds=delta.microseconds,
years=delta.years, months=delta.months, seconds=delta._total
)

return self.subtract(seconds=delta.total_seconds())
Expand Down
42 changes: 27 additions & 15 deletions pendulum/duration.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,19 @@ def __new__(
raise ValueError("Float year and months are not supported")

self = timedelta.__new__(
cls, days, seconds, microseconds, milliseconds, minutes, hours, weeks
cls,
days + years * 365 + months * 30,
seconds,
microseconds,
milliseconds,
minutes,
hours,
weeks,
)

# Intuitive normalization
total = self.total_seconds()
total = self.total_seconds() - (years * 365 + months * 30) * SECONDS_PER_DAY
self._total = total

m = 1
if total < 0:
Expand All @@ -80,7 +88,7 @@ def __new__(
self._seconds = abs(int(total)) % SECONDS_PER_DAY * m

_days = abs(int(total)) // SECONDS_PER_DAY * m
self._days = _days + (years * 365 + months * 30)
self._days = _days
self._remaining_days = abs(_days) % 7 * m
self._weeks = abs(_days) // 7 * m
self._months = months
Expand All @@ -103,10 +111,18 @@ def total_weeks(self):
if PYPY:

def total_seconds(self):
days = 0

if hasattr(self, "_years"):
days += self._years * 365

if hasattr(self, "_months"):
days += self._months * 30

if hasattr(self, "_remaining_days"):
days = self._weeks * 7 + self._remaining_days
days += self._weeks * 7 + self._remaining_days
else:
days = self._days
days += self._days

return (
(days * SECONDS_PER_DAY + self._seconds) * US_PER_SECOND
Expand All @@ -125,9 +141,11 @@ def months(self):
def weeks(self):
return self._weeks

@property
def days(self):
return self._days
if PYPY:

@property
def days(self) -> int:
return self._years * 365 + self._months * 30 + self._days

@property
def remaining_days(self):
Expand Down Expand Up @@ -320,7 +338,7 @@ def __mul__(self, other):
return self.__class__(
years=self._years * other,
months=self._months * other,
seconds=self.total_seconds() * other,
seconds=self._total * other,
)

if isinstance(other, float):
Expand All @@ -341,9 +359,6 @@ def __floordiv__(self, other):
if isinstance(other, timedelta):
return usec // other._to_microseconds()

# Removing years/months approximation
usec -= (self._years * 365 + self._months * 30) * SECONDS_PER_DAY * 1e6

if isinstance(other, int):
return self.__class__(
0,
Expand All @@ -361,9 +376,6 @@ def __truediv__(self, other):
if isinstance(other, timedelta):
return usec / other._to_microseconds()

# Removing years/months approximation
usec -= (self._years * 365 + self._months * 30) * SECONDS_PER_DAY * 1e6

if isinstance(other, int):
return self.__class__(
0,
Expand Down
2 changes: 2 additions & 0 deletions tests/duration/test_construct.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ def test_years():
pi = pendulum.duration(years=2)
assert_duration(pi, years=2, weeks=0)
assert 730 == pi.days
assert 63072000 == pi.total_seconds()


def test_months():
pi = pendulum.duration(months=3)
assert_duration(pi, months=3, weeks=0)
assert 90 == pi.days
assert 7776000 == pi.total_seconds()


def test_weeks():
Expand Down

0 comments on commit 9274f5c

Please sign in to comment.