Skip to content

Commit fb2e860

Browse files
authored
Fix the computation of total seconds with years and months (#482)
1 parent 0735ac4 commit fb2e860

File tree

3 files changed

+31
-24
lines changed

3 files changed

+31
-24
lines changed

pendulum/datetime.py

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -754,7 +754,7 @@ def _add_timedelta_(self, delta):
754754
)
755755
elif isinstance(delta, pendulum.Duration):
756756
return self.add(
757-
years=delta.years, months=delta.months, seconds=delta.total_seconds()
757+
years=delta.years, months=delta.months, seconds=delta._total
758758
)
759759

760760
return self.add(seconds=delta.total_seconds())
@@ -770,14 +770,7 @@ def _subtract_timedelta(self, delta):
770770
"""
771771
if isinstance(delta, pendulum.Duration):
772772
return self.subtract(
773-
years=delta.years,
774-
months=delta.months,
775-
weeks=delta.weeks,
776-
days=delta.remaining_days,
777-
hours=delta.hours,
778-
minutes=delta.minutes,
779-
seconds=delta.remaining_seconds,
780-
microseconds=delta.microseconds,
773+
years=delta.years, months=delta.months, seconds=delta._total
781774
)
782775

783776
return self.subtract(seconds=delta.total_seconds())

pendulum/duration.py

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,19 @@ def __new__(
6666
raise ValueError("Float year and months are not supported")
6767

6868
self = timedelta.__new__(
69-
cls, days, seconds, microseconds, milliseconds, minutes, hours, weeks
69+
cls,
70+
days + years * 365 + months * 30,
71+
seconds,
72+
microseconds,
73+
milliseconds,
74+
minutes,
75+
hours,
76+
weeks,
7077
)
7178

7279
# Intuitive normalization
73-
total = self.total_seconds()
80+
total = self.total_seconds() - (years * 365 + months * 30) * SECONDS_PER_DAY
81+
self._total = total
7482

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

8290
_days = abs(int(total)) // SECONDS_PER_DAY * m
83-
self._days = _days + (years * 365 + months * 30)
91+
self._days = _days
8492
self._remaining_days = abs(_days) % 7 * m
8593
self._weeks = abs(_days) // 7 * m
8694
self._months = months
@@ -103,10 +111,18 @@ def total_weeks(self):
103111
if PYPY:
104112

105113
def total_seconds(self):
114+
days = 0
115+
116+
if hasattr(self, "_years"):
117+
days += self._years * 365
118+
119+
if hasattr(self, "_months"):
120+
days += self._months * 30
121+
106122
if hasattr(self, "_remaining_days"):
107-
days = self._weeks * 7 + self._remaining_days
123+
days += self._weeks * 7 + self._remaining_days
108124
else:
109-
days = self._days
125+
days += self._days
110126

111127
return (
112128
(days * SECONDS_PER_DAY + self._seconds) * US_PER_SECOND
@@ -125,9 +141,11 @@ def months(self):
125141
def weeks(self):
126142
return self._weeks
127143

128-
@property
129-
def days(self):
130-
return self._days
144+
if PYPY:
145+
146+
@property
147+
def days(self):
148+
return self._years * 365 + self._months * 30 + self._days
131149

132150
@property
133151
def remaining_days(self):
@@ -320,7 +338,7 @@ def __mul__(self, other):
320338
return self.__class__(
321339
years=self._years * other,
322340
months=self._months * other,
323-
seconds=self.total_seconds() * other,
341+
seconds=self._total * other,
324342
)
325343

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

344-
# Removing years/months approximation
345-
usec -= (self._years * 365 + self._months * 30) * SECONDS_PER_DAY * 1e6
346-
347362
if isinstance(other, int):
348363
return self.__class__(
349364
0,
@@ -361,9 +376,6 @@ def __truediv__(self, other):
361376
if isinstance(other, timedelta):
362377
return usec / other._to_microseconds()
363378

364-
# Removing years/months approximation
365-
usec -= (self._years * 365 + self._months * 30) * SECONDS_PER_DAY * 1e6
366-
367379
if isinstance(other, int):
368380
return self.__class__(
369381
0,

tests/duration/test_construct.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@ def test_years():
1717
pi = pendulum.duration(years=2)
1818
assert_duration(pi, years=2, weeks=0)
1919
assert 730 == pi.days
20+
assert 63072000 == pi.total_seconds()
2021

2122

2223
def test_months():
2324
pi = pendulum.duration(months=3)
2425
assert_duration(pi, months=3, weeks=0)
2526
assert 90 == pi.days
27+
assert 7776000 == pi.total_seconds()
2628

2729

2830
def test_weeks():

0 commit comments

Comments
 (0)