Repository: sdispater/pendulum
Pull request #154 (2017-10-11 21:49:28)

README: remove duplicated words


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e09585d..4a09a8e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
 # Change Log
 
+## [1.3.1] - 2017-10-22
+
+### Fixed
+
+- Fixed inaccuracy of `in_days()` method on DST transitions.
+- Fixed seconds inaccuracy for past datetimes.
+
+
 ## [1.3.0] - 2017-09-25
 
 ### Added
@@ -23,7 +31,7 @@
 ### Fixed
 
 - Fixed normalization of microseconds in durations.
-- Fixed microseconds not being included in `average()`. (Thanks to [ericfrederich](https://github.com/ericfrederich)) 
+- Fixed microseconds not being included in `average()`. (Thanks to [ericfrederich](https://github.com/ericfrederich))
 
 
 ## [1.2.4] - 2017-06-20
@@ -296,7 +304,7 @@
 
 ### Changed
 
-- Makes `.offset_hours` return a float. 
+- Makes `.offset_hours` return a float.
 
 ### Fixed
 
@@ -412,7 +420,8 @@ Initial release
 
 
 
-[Unreleased]: https://github.com/sdispater/pendulum/compare/1.3.0...master
+[Unreleased]: https://github.com/sdispater/pendulum/compare/1.3.1...master
+[1.3.1]: https://github.com/sdispater/pendulum/releases/tag/1.3.1
 [1.3.0]: https://github.com/sdispater/pendulum/releases/tag/1.3.0
 [1.2.5]: https://github.com/sdispater/pendulum/releases/tag/1.2.5
 [1.2.4]: https://github.com/sdispater/pendulum/releases/tag/1.2.4
diff --git a/README.rst b/README.rst
index 4d0ce85..8887e5e 100644
--- a/README.rst
+++ b/README.rst
@@ -133,7 +133,7 @@ Even though the ``Pendulum`` class is a subclass of ``datetime`` there are some
 it can't replace the native class directly. Here is a list (non-exhaustive) of the reported cases with
 a possible solution, if any:
 
-* ``sqlite3`` will use the the ``type()`` function to determine the type of the object by default. To work around it you can register a new adapter:
+* ``sqlite3`` will use the ``type()`` function to determine the type of the object by default. To work around it you can register a new adapter:
 
 .. code-block:: python
 
@@ -142,7 +142,7 @@ a possible solution, if any:
 
     register_adapter(Pendulum, lambda val: val.isoformat(' '))
 
-* ``mysqlclient`` (former ``MySQLdb``) and ``PyMySQL`` will use the the ``type()`` function to determine the type of the object by default. To work around it you can register a new adapter:
+* ``mysqlclient`` (former ``MySQLdb``) and ``PyMySQL`` will use the ``type()`` function to determine the type of the object by default. To work around it you can register a new adapter:
 
 .. code-block:: python
 
diff --git a/docs/conf.py b/docs/conf.py
index 0e61d54..7c771e7 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -60,7 +60,7 @@ author = 'Sébastien Eustace'
 # The short X.Y version.
 version = '1.3'
 # The full version, including alpha/beta/rc tags.
-release = '1.3.0'
+release = '1.3.1'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff --git a/pendulum/_extensions/_helpers.c b/pendulum/_extensions/_helpers.c
index 075c577..dc3e93e 100644
--- a/pendulum/_extensions/_helpers.c
+++ b/pendulum/_extensions/_helpers.c
@@ -317,7 +317,7 @@ PyObject* local_time(PyObject *self, PyObject *args) {
     }
 
     year = EPOCH_YEAR;
-    seconds = (int64_t) unix_time;
+    seconds = (int64_t) floor(unix_time);
 
     // Shift to a base year that is 400-year aligned.
     if (seconds >= 0) {
diff --git a/pendulum/_extensions/helpers.py b/pendulum/_extensions/helpers.py
index 2979c86..b9aa2cf 100644
--- a/pendulum/_extensions/helpers.py
+++ b/pendulum/_extensions/helpers.py
@@ -1,4 +1,5 @@
 # -*- coding: utf-8 -*-
+import math
 
 from ..constants import (
     EPOCH_YEAR,
@@ -27,7 +28,7 @@ def local_time(unix_time, utc_offset, microseconds):
     :rtype: tuple
     """
     year = EPOCH_YEAR
-    seconds = int(unix_time)
+    seconds = int(math.floor(unix_time))
 
     # Shift to a base year that is 400-year aligned.
     if seconds >= 0:
diff --git a/pendulum/helpers.py b/pendulum/helpers.py
index f5851b0..23b7023 100644
--- a/pendulum/helpers.py
+++ b/pendulum/helpers.py
@@ -3,7 +3,7 @@
 import pendulum
 
 from math import copysign
-from datetime import timedelta
+from datetime import datetime, timedelta
 
 try:
     from ._extensions._helpers import local_time, parse_iso8601 as _parse_iso8601
@@ -136,13 +136,25 @@ def precise_diff(d1, d2):
         'hours': 0,
         'minutes': 0,
         'seconds': 0,
-        'microseconds': 0
+        'microseconds': 0,
+        'total': {
+            'days': 0
+        }
     }
     sign = 1
 
     if d1 == d2:
         return diff
 
+    tzinfo1 = d1.tzinfo if isinstance(d1, datetime) else None
+    tzinfo2 = d2.tzinfo if isinstance(d2, datetime) else None
+
+    if (tzinfo1 is None and tzinfo2 is not None
+        or tzinfo2 is None and tzinfo1 is not None):
+        raise ValueError(
+            'Comparison between naive and aware datetimes is not supported'
+        )
+
     if d1 > d2:
         d1, d2 = d2, d1
         sign = -1
@@ -154,8 +166,44 @@ def precise_diff(d1, d2):
     min_diff = 0
     sec_diff = 0
     mic_diff = 0
+    total_days = (
+        _day_number(d2.year, d2.month, d2.day)
+        - _day_number(d1.year, d1.month, d1.day)
+    )
+    in_same_tz = False
+    tz1 = None
+    tz2 = None
+
+    # Trying to figure out the timezone names
+    # If we can't find them, we assume different timezones
+    if tzinfo1 and tzinfo2:
+        if hasattr(tzinfo1, 'name'):
+            # Pendulum timezone
+            tz1 = tzinfo1.name
+        elif hasattr(tzinfo1, 'zone'):
+            # pytz timezone
+            tz1 = tzinfo1.zone
+
+        if hasattr(tzinfo2, 'name'):
+            tz2 = tzinfo2.name
+        elif hasattr(tzinfo2, 'zone'):
+            tz2 = tzinfo2.zone
+
+        in_same_tz = tz1 == tz2 and tz1 is not None
 
     if hasattr(d2, 'hour'):
+        # If we are not in the same timezone
+        # we need to adjust
+        if not in_same_tz:
+            offset1 = d1.utcoffset()
+            offset2 = d2.utcoffset()
+
+            if offset1:
+                d1 = d1 - offset1
+
+            if offset2:
+                d2 = d2 - offset2
+
         hour_diff = d2.hour - d1.hour
         min_diff = d2.minute - d1.minute
         sec_diff = d2.second - d1.second
@@ -221,6 +269,7 @@ def precise_diff(d1, d2):
     diff['days'] = sign * d_diff
     diff['months'] = sign * m_diff
     diff['years'] = sign * y_diff
+    diff['total']['days'] = sign * total_days
 
     return diff
 
@@ -243,5 +292,18 @@ def days_in_year(year):
 
     return DAYS_PER_N_YEAR
 
+
 def _sign(x):
     return int(copysign(1, x))
+
+
+def _day_number(year, month, day):
+    month = (month + 9) % 12
+    year = year - month // 10
+
+    return (
+        365 * year
+        + year // 4 - year // 100 + year // 400
+        + (month * 306 + 5) // 10
+        + (day - 1 )
+    )
diff --git a/pendulum/period.py b/pendulum/period.py
index 82b0abb..5b7cd0c 100644
--- a/pendulum/period.py
+++ b/pendulum/period.py
@@ -131,6 +131,18 @@ class Period(WordableIntervalMixin, BaseInterval):
         """
         return self.years * MONTHS_PER_YEAR + self.months
 
+    def in_weeks(self):
+        days = self.in_days()
+        sign = 1
+
+        if days < 0:
+            sign = -1
+
+        return sign * (abs(days) // 7)
+
+    def in_days(self):
+        return self._delta['total']['days']
+
     def in_weekdays(self):
         start, end = self.start.start_of('day'), self.end.start_of('day')
         if not self._absolute and self.invert:
diff --git a/pendulum/version.py b/pendulum/version.py
index c4eb02c..5d2d0ff 100644
--- a/pendulum/version.py
+++ b/pendulum/version.py
@@ -1,3 +1,3 @@
 # -*- coding: utf-8 -*-
 
-VERSION = '1.3.0'
+VERSION = '1.3.1'


Test patch:
diff --git a/tests/pendulum_tests/test_construct.py b/tests/pendulum_tests/test_construct.py
index 35d3fac..a103638 100644
--- a/tests/pendulum_tests/test_construct.py
+++ b/tests/pendulum_tests/test_construct.py
@@ -2,6 +2,9 @@
 
 import os
 import pytz
+
+import pendulum
+
 from datetime import datetime, timedelta
 from dateutil import tz
 from pendulum import Pendulum, PRE_TRANSITION, POST_TRANSITION
@@ -207,3 +210,8 @@ class ConstructTest(AbstractTestCase):
         d = Pendulum(2013, 3, 31, 2, 30, tzinfo='Europe/Paris', fold=1)
         self.assertPendulum(d, 2013, 3, 31, 3, 30)
         self.assertEqual(d.fold, 1)
+
+    def test_second_inaccuracy_on_past_datetimes(self):
+        dt = pendulum.create(1901, 12, 13, 0, 0, 0, 555555, tz='US/Central')
+
+        self.assertPendulum(dt, 1901, 12, 13, 0, 0, 0, 555555)
diff --git a/tests/period_tests/test_construct.py b/tests/period_tests/test_construct.py
index 80a6478..8d25f33 100644
--- a/tests/period_tests/test_construct.py
+++ b/tests/period_tests/test_construct.py
@@ -85,6 +85,9 @@ class ConstructTest(AbstractTestCase):
         assert period.hours == 0
         assert period.remaining_seconds == 0
 
+        assert period.in_days() == 6
+        assert period.in_hours() == 5 * 24 + 23
+
     def test_timedelta_behavior(self):
         dt1 = Pendulum(2000, 11, 20, 1)
         dt2 = Pendulum(2000, 11, 25, 2)