Repository: crsmithdev/arrow
Pull request #574 (2019-04-11 18:58:49)

Add is_between functionality
Addressing the feature request in #548, @TroyStacer and I have implemented the isbetween functionality in Arrow that functions similarly to the [isBetween function in Moment.js](https://momentjs.com/docs/#/query/is-between/).

Closes: #548

codecov-io: # [Codecov](https://codecov.io/gh/crsmithdev/arrow/pull/574?src=pr&el=h1) Report
> Merging [#574](https://codecov.io/gh/crsmithdev/arrow/pull/574?src=pr&el=desc) into [master](https://codecov.io/gh/crsmithdev/arrow/commit/43ffd2ac65984ca90d6c44b6e6dc9769f5288117?src=pr&el=desc) will **not change** coverage.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/crsmithdev/arrow/pull/574/graphs/tree.svg?width=650&token=4EkGnFsIGn&height=150&src=pr)](https://codecov.io/gh/crsmithdev/arrow/pull/574?src=pr&el=tree)

```diff
@@          Coverage Diff          @@
##           master   #574   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files          13     13           
  Lines        3399   3484   +85     
  Branches      238    244    +6     
=====================================
+ Hits         3399   3484   +85
```


| [Impacted Files](https://codecov.io/gh/crsmithdev/arrow/pull/574?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [tests/arrow\_tests.py](https://codecov.io/gh/crsmithdev/arrow/pull/574/diff?src=pr&el=tree#diff-dGVzdHMvYXJyb3dfdGVzdHMucHk=) | `100% <100%> (ø)` | :arrow_up: |
| [arrow/arrow.py](https://codecov.io/gh/crsmithdev/arrow/pull/574/diff?src=pr&el=tree#diff-YXJyb3cvYXJyb3cucHk=) | `100% <100%> (ø)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/crsmithdev/arrow/pull/574?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/crsmithdev/arrow/pull/574?src=pr&el=footer). Last update [43ffd2a...30f9d75](https://codecov.io/gh/crsmithdev/arrow/pull/574?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

jadchaar: Thanks for the feedback @systemcatch. I have addressed it in the latest commit.

Patch:
diff --git a/arrow/arrow.py b/arrow/arrow.py
index 8db28be..08639a3 100644
--- a/arrow/arrow.py
+++ b/arrow/arrow.py
@@ -871,6 +871,65 @@ class Arrow(object):
             if(trunc(abs(delta)) != 1):
                 granularity += 's'
             return locale.describe(granularity, delta, only_distance=only_distance)
+
+    # query functions
+
+    def is_between(self, start, end, bounds='()'):
+        ''' Returns a boolean denoting whether the specified date and time is between
+        the start and end dates and times.
+
+        :param start: an :class:`Arrow <arrow.arrow.Arrow>`.
+        :param end: an :class:`Arrow <arrow.arrow.Arrow>`.
+        :param bounds: (optional) a ``str`` of either '()', '(]', '[)', or '[]' that specifies
+            whether to include or exclude the start and end values in the range. '(' excludes
+            the start, '[' includes the start, ')' excludes the end, and ']' includes the end.
+            If the bounds are not specified, the default bound '()' is used.
+
+        Usage::
+
+            >>> start = arrow.get(datetime(2013, 5, 5, 12, 30, 10))
+            >>> end = arrow.get(datetime(2013, 5, 5, 12, 30, 36))
+            >>> arrow.get(datetime(2013, 5, 5, 12, 30, 27)).is_between(start, end)
+            True
+
+            >>> start = arrow.get(datetime(2013, 5, 5))
+            >>> end = arrow.get(datetime(2013, 5, 8))
+            >>> arrow.get(datetime(2013, 5, 8)).is_between(start, end, '[]')
+            True
+
+            >>> start = arrow.get(datetime(2013, 5, 5))
+            >>> end = arrow.get(datetime(2013, 5, 8))
+            >>> arrow.get(datetime(2013, 5, 8)).is_between(start, end, '[)')
+            False
+
+        '''
+
+        if bounds != '()' and bounds != '(]' and bounds != '[)' and bounds != '[]':
+            raise AttributeError('Error. Could not understand the specified bounds. Please select between \
+                "()", "(]", "[)", or "[]"')
+
+        if not isinstance(start, Arrow):
+            raise TypeError('Can\'t parse start date argument type of \'{}\''.format(type(start)))
+
+        if not isinstance(end, Arrow):
+            raise TypeError('Can\'t parse end date argument type of \'{}\''.format(type(end)))
+
+        include_start = (bounds[0] == '[')
+        include_end = (bounds[1] == ']')
+
+        target_timestamp = self.float_timestamp
+        start_timestamp = start.float_timestamp
+        end_timestamp = end.float_timestamp
+
+        if include_start and include_end:
+            return target_timestamp >= start_timestamp and target_timestamp <= end_timestamp
+        elif include_start and not include_end:
+            return target_timestamp >= start_timestamp and target_timestamp < end_timestamp
+        elif not include_start and include_end:
+            return target_timestamp > start_timestamp and target_timestamp <= end_timestamp
+        else:
+            return target_timestamp > start_timestamp and target_timestamp < end_timestamp
+
     # math
 
     def __add__(self, other):


Test patch:
diff --git a/tests/arrow_tests.py b/tests/arrow_tests.py
index afa3ca3..fa173c5 100644
--- a/tests/arrow_tests.py
+++ b/tests/arrow_tests.py
@@ -1360,6 +1360,82 @@ class ArrowHumanizeTestsWithLocale(Chai):
 
         assertEqual(result, '2 года назад')
 
+class ArrowIsBetweenTests(Chai):
+
+    def test_start_before_end(self):
+        target = arrow.Arrow.fromdatetime(datetime(2013, 5, 7))
+        start = arrow.Arrow.fromdatetime(datetime(2013, 5, 8))
+        end = arrow.Arrow.fromdatetime(datetime(2013, 5, 5))
+        result = target.is_between(start, end)
+        assertFalse(result)
+
+    def test_exclusive_exclusive_bounds(self):
+        target = arrow.Arrow.fromdatetime(datetime(2013, 5, 5, 12, 30, 27))
+        start = arrow.Arrow.fromdatetime(datetime(2013, 5, 5, 12, 30, 10))
+        end = arrow.Arrow.fromdatetime(datetime(2013, 5, 5, 12, 30, 36))
+        result = target.is_between(start, end, '()')
+        assertTrue(result)
+        result = target.is_between(start, end)
+        assertTrue(result)
+
+    def test_exclusive_exclusive_bounds_same_date(self):
+        target = arrow.Arrow.fromdatetime(datetime(2013, 5, 7))
+        start = arrow.Arrow.fromdatetime(datetime(2013, 5, 7))
+        end = arrow.Arrow.fromdatetime(datetime(2013, 5, 7))
+        result = target.is_between(start, end, '()')
+        assertFalse(result)
+
+    def test_inclusive_exclusive_bounds(self):
+        target = arrow.Arrow.fromdatetime(datetime(2013, 5, 6))
+        start = arrow.Arrow.fromdatetime(datetime(2013, 5, 4))
+        end = arrow.Arrow.fromdatetime(datetime(2013, 5, 6))
+        result = target.is_between(start, end, '[)')
+        assertFalse(result)
+
+    def test_exclusive_inclusive_bounds(self):
+        target = arrow.Arrow.fromdatetime(datetime(2013, 5, 7))
+        start = arrow.Arrow.fromdatetime(datetime(2013, 5, 5))
+        end = arrow.Arrow.fromdatetime(datetime(2013, 5, 7))
+        result = target.is_between(start, end, '(]')
+        assertTrue(result)
+
+    def test_inclusive_inclusive_bounds_same_date(self):
+        target = arrow.Arrow.fromdatetime(datetime(2013, 5, 7))
+        start = arrow.Arrow.fromdatetime(datetime(2013, 5, 7))
+        end = arrow.Arrow.fromdatetime(datetime(2013, 5, 7))
+        result = target.is_between(start, end, '[]')
+        assertTrue(result)
+
+    def test_type_error_exception(self):
+        with assertRaises(TypeError):
+            target = arrow.Arrow.fromdatetime(datetime(2013, 5, 7))
+            start = datetime(2013, 5, 5)
+            end = arrow.Arrow.fromdatetime(datetime(2013, 5, 8))
+            result = target.is_between(start, end)
+
+        with assertRaises(TypeError):
+            target = arrow.Arrow.fromdatetime(datetime(2013, 5, 7))
+            start = arrow.Arrow.fromdatetime(datetime(2013, 5, 5))
+            end = datetime(2013, 5, 8)
+            result = target.is_between(start, end)
+
+        with assertRaises(TypeError):
+            result = target.is_between(None, None)
+
+    def test_attribute_error_exception(self):
+        target = arrow.Arrow.fromdatetime(datetime(2013, 5, 7))
+        start = arrow.Arrow.fromdatetime(datetime(2013, 5, 5))
+        end = arrow.Arrow.fromdatetime(datetime(2013, 5, 8))
+        with assertRaises(AttributeError):
+            result = target.is_between(start, end, "][")
+        with assertRaises(AttributeError):
+            result = target.is_between(start, end, "")
+        with assertRaises(AttributeError):
+            result = target.is_between(start, end, "]")
+        with assertRaises(AttributeError):
+            result = target.is_between(start, end, "[")
+        with assertRaises(AttributeError):
+            result = target.is_between(start, end, "hello")
 
 class ArrowUtilTests(Chai):