Repository: sdispater/pendulum
Pull request #138 (2017-08-23 19:43:25)

Change travis-badge to SVG
Just saw the pixelated png badge ...


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 12701ee..f33c32c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,13 @@
 # Change Log
 
+## [Unreleased]
+
+### Fixed
+
+- Fixed normalization of microseconds in durations.
+- Fixed microseconds not being included in `average()`. (Thanks to [ericfrederich](https://github.com/ericfrederich)) 
+
+
 ## [1.2.4] - 2017-06-20
 
 ### Fixed
@@ -26,10 +34,10 @@
 ## [1.2.1] - 2017-05-23
 
 ### Fixed
-
 - Fixed incorrect `fold` attribute on Python 3.6 when not passing a timezone. (Thanks to [neonquill](https://github.com/neonquill))
 
 
+
 ## [1.2.0] - 2017-03-24
 
 ### Added
diff --git a/README.rst b/README.rst
index 40403e1..330965a 100644
--- a/README.rst
+++ b/README.rst
@@ -10,7 +10,7 @@ Pendulum
 .. image:: https://img.shields.io/codecov/c/github/sdispater/pendulum/master.svg
     :target: https://codecov.io/gh/sdispater/pendulum/branch/master
 
-.. image:: https://travis-ci.org/sdispater/pendulum.png
+.. image:: https://travis-ci.org/sdispater/pendulum.svg
     :alt: Pendulum Build status
     :target: https://travis-ci.org/sdispater/pendulum
 
diff --git a/docs/_docs/difference.rst b/docs/_docs/difference.rst
index 9e56f01..53c8570 100644
--- a/docs/_docs/difference.rst
+++ b/docs/_docs/difference.rst
@@ -83,7 +83,7 @@ You may also pass ``True`` as a 2nd parameter to remove the modifiers `ago`, `fr
     # The most typical usage is for comments
     # The instance is the date the comment was created
     # and its being compared to default now()
-    pendulum.now().subtract(dayss=1).diff_for_humans()
+    pendulum.now().subtract(days=1).diff_for_humans()
     '5 days ago'
 
     pendulum.now().diff_for_humans(Pendulum.now().subtract(years=1))
diff --git a/pendulum/interval.py b/pendulum/interval.py
index e8c1d97..1c26a1e 100644
--- a/pendulum/interval.py
+++ b/pendulum/interval.py
@@ -60,7 +60,7 @@ class BaseInterval(timedelta):
         if total < 0:
             m = -1
 
-        self._microseconds = abs(round(total % 1 * 1e6)) * m
+        self._microseconds = round(total % m * 1e6)
         self._seconds = abs(int(total)) % SECONDS_PER_DAY * m
         self._days = abs(int(total)) // SECONDS_PER_DAY * m
 
diff --git a/pendulum/pendulum.py b/pendulum/pendulum.py
index 247112a..96ffd95 100644
--- a/pendulum/pendulum.py
+++ b/pendulum/pendulum.py
@@ -1769,7 +1769,8 @@ class Pendulum(Date, datetime.datetime):
         if dt is None:
             dt = Pendulum.now(self._tz)
 
-        return self.add(seconds=int(self.diff(dt, False).in_seconds() / 2))
+        diff = self.diff(dt, False)
+        return self.add(microseconds=(diff.in_seconds() * 1000000 + diff.microseconds) // 2)
 
     def _get_datetime(self, value, pendulum=False):
         """


Test patch:
diff --git a/tests/pendulum_tests/test_diff.py b/tests/pendulum_tests/test_diff.py
index 316439d..0da9a7f 100644
--- a/tests/pendulum_tests/test_diff.py
+++ b/tests/pendulum_tests/test_diff.py
@@ -1,5 +1,7 @@
 # -*- coding: utf-8 -*-
 
+import pendulum
+
 from datetime import datetime
 from contextlib import contextmanager
 from pendulum import Pendulum
@@ -593,3 +595,13 @@ class DiffTest(AbstractTestCase):
 
         self.assertEqual(3600, (future - d).total_seconds())
         self.assertEqual(3600, (future_dt - d).total_seconds())
+
+    def test_normalization(self):
+        d1 = pendulum.create(2012, 1, 1, 1, 2, 3, 123456)
+        d2 = pendulum.create(2011, 12, 31, 22, 2, 3)
+        delta = d2 - d1
+
+        assert delta.days == 0
+        assert delta.seconds == -10800
+        assert delta.microseconds == -123456
+        assert d1 + delta == d2
diff --git a/tests/pendulum_tests/test_start_end_of.py b/tests/pendulum_tests/test_start_end_of.py
index fbc034c..f3ff332 100644
--- a/tests/pendulum_tests/test_start_end_of.py
+++ b/tests/pendulum_tests/test_start_end_of.py
@@ -1,7 +1,8 @@
 # -*- coding: utf-8 -*-
 
-from pendulum import Pendulum
+from itertools import product, tee
 
+from pendulum import Pendulum
 from .. import AbstractTestCase
 
 
@@ -208,19 +209,70 @@ class StartEndOfTest(AbstractTestCase):
         self.assertIsInstanceOfPendulum(d)
 
     def test_average_from_same(self):
-        d1 = Pendulum.create(2000, 1, 31, 2, 3, 4)
-        d2 = Pendulum.create(2000, 1, 31, 2, 3, 4).average(d1)
-        self.assertPendulum(d2, 2000, 1, 31, 2, 3, 4)
+        d1 = Pendulum.create(2000, 1, 31, 2, 3, 4, 5)
+        d2 = Pendulum.create(2000, 1, 31, 2, 3, 4, 5).average(d1)
+        self.assertPendulum(d2, 2000, 1, 31, 2, 3, 4, 5)
 
     def test_average_from_greater(self):
-        d1 = Pendulum.create(2000, 1, 1, 1, 1, 1, tz='local')
-        d2 = Pendulum.create(2009, 12, 31, 23, 59, 59, tz='local').average(d1)
-        self.assertPendulum(d2, 2004, 12, 31, 12, 30, 30)
+        d1 = Pendulum.create(2000, 1, 1, 1, 1, 1, 1, tz='local')
+        d2 = Pendulum.create(2009, 12, 31, 23, 59, 59, 999999, tz='local').average(d1)
+        self.assertPendulum(d2, 2004, 12, 31, 12, 30, 30, 500000)
 
     def test_average_from_lower(self):
-        d1 = Pendulum.create(2009, 12, 31, 23, 59, 59, tz='local')
-        d2 = Pendulum.create(2000, 1, 1, 1, 1, 1, tz='local').average(d1)
-        self.assertPendulum(d2, 2004, 12, 31, 12, 30, 30)
+        d1 = Pendulum.create(2009, 12, 31, 23, 59, 59, 999999, tz='local')
+        d2 = Pendulum.create(2000, 1, 1, 1, 1, 1, 1, tz='local').average(d1)
+        self.assertPendulum(d2, 2004, 12, 31, 12, 30, 30, 500000)
+
+    def test_average_with_microseconds(self):
+        for d1, d2, expected_average in [
+            # 3ms and 5ms should average to 4
+            ((1982, 12, 4, 17, 13, 0, 3), (1982, 12, 4, 17, 13, 0, 5), (1982, 12, 4, 17, 13, 0, 4)),
+            # 999999ms and subsequent 1ms should average to 0
+            ((1982, 12, 4, 0, 0, 0, 999999), (1982, 12, 4, 0, 0, 1, 1), (1982, 12, 4, 0, 0, 1, 0)),
+            # 1.000002 should average to 0.500001
+            ((2000, 1, 1, 0, 0, 0, 0), (2000, 1, 1, 0, 0, 1, 2), (2000, 1, 1, 0, 0, 0, 500001)),
+        ]:
+            d1 = Pendulum.create(*d1)
+            d2 = Pendulum.create(*d2)
+            a1 = d1.average(d2)
+            a2 = d2.average(d1)
+            self.assertPendulum(a1, *expected_average)
+            self.assertPendulum(a2, *expected_average)
+
+    def test_average_commutative(self):
+        for d1, d2 in product(*tee([
+            Pendulum.create(1981, 10,  9,  1,  2,  3,  4),
+            Pendulum.create(1982, 12,  4,  5,  6,  7,  8),
+            Pendulum.create(2013,  4, 13,  9, 10, 11, 12),
+            Pendulum.create(1982,  9, 24, 13, 14, 15, 16),
+        ])):
+            a1 = d1.average(d2)
+            a2 = d2.average(d1)
+            self.assertEqual(a1, a2)
+
+    def test_bisect_creates_all_times(self):
+
+        def bisect(start, end):
+            # should generate, in order, all microseconds between start and end
+            mid = start.average(end)
+            if mid == start or mid == end:
+                return
+            for d in bisect(start, mid):
+                yield d
+            yield mid
+            for d in bisect(mid, end):
+                yield d
+
+        start = Pendulum.now()
+        end = start.add(microseconds=1000)
+        d_next = start.copy()
+        n_values = 0
+        for d in bisect(start, end):
+            n_values += 1
+            d_next = d_next.add(microseconds=1)
+            self.assertEqual(d, d_next)
+
+        self.assertEqual(n_values, 999)
 
     def start_of_with_invalid_unit(self):
         self.assertRaises(ValueError, Pendulum.now().start_of('invalid'))