Skip to content

Commit

Permalink
Added new Excel Date Feature.
Browse files Browse the repository at this point in the history
  • Loading branch information
anishnya committed May 26, 2021
1 parent 119bb67 commit 9e0cb4f
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 0 deletions.
37 changes: 37 additions & 0 deletions arrow/arrow.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
from dateutil import tz as dateutil_tz
from dateutil.relativedelta import relativedelta

import arrow
from arrow import formatter, locales, parser, util
from arrow.constants import DEFAULT_LOCALE, DEHUMANIZE_LOCALES
from arrow.locales import TimeFrameLiteral
Expand Down Expand Up @@ -1787,6 +1788,42 @@ def _get_tzinfo(tz_expr: Optional[TZ_EXPR]) -> dt_tzinfo:
except parser.ParserError:
raise ValueError(f"{tz_expr!r} not recognized as a timezone.")

@classmethod
def excel_date(
cls, delta: Union[int, float], default_windows_date: bool = True
) -> "Arrow":
"""Returns a new :class:`Arrow <arrow.arrow.Arrow>` object, that represents
the date of an Excel Serial formatted date.
:param delta: a ``int`` or ``float`` representing an Excel Serial Date.
:param default_windows_date: (optional) a ``bool`` specifying whether a user
wants to use the Windows or macOS date system. Defaults to 'True' for Windows
date system.
Usage::
>>> arw = Arrow.excel_date(34519)
>>> arw
<Arrow [1994-07-04T00:00:00+00:00]>
>>> arw = Arrow.excel_date(34519, default_windows_date = False)
>>> arw
<Arrow [1998-07-05T00:00:00+00:00]>
"""

if default_windows_date:
# Need to have this clause as Excel incorrectly considers 1900 a leap year
if delta < 60:
start_date = arrow.get("1899-12-31")
else:
start_date = arrow.get("1899-12-30")
else:
start_date = arrow.get("1904-01-01")

shifted_time = start_date.shift(days=delta)

return shifted_time

@classmethod
def _get_datetime(
cls, expr: Union["Arrow", dt_datetime, int, float, str]
Expand Down
12 changes: 12 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,18 @@ Arrow objects can be instantiated directly too, with the same arguments as a dat
>>> arrow.Arrow(2013, 5, 5)
<Arrow [2013-05-05T00:00:00+00:00]>
Arrow objects can be instantiated using the Excel Serial Date format. Both Windows and macOS date systems supported:

.. code-block:: python
>>> arw = Arrow.excel_date(34519)
>>> arw
<Arrow [1994-07-04T00:00:00+00:00]>
>>> arw = Arrow.excel_date(34519, default_windows_date = False)
>>> arw
<Arrow [1998-07-05T00:00:00+00:00]>
Properties
~~~~~~~~~~

Expand Down
46 changes: 46 additions & 0 deletions tests/test_arrow.py
Original file line number Diff line number Diff line change
Expand Up @@ -2867,6 +2867,52 @@ def test_value_error_exception(self):
target.span("week", week_start=55)


class TestExcelDate:
def test_windows_date_leap_year_edge_case(self):
start = arrow.Arrow.fromdatetime(datetime(1900, 2, 28))
test = arrow.Arrow.excel_date(59)
assert start == test

def test_windows_date_leap_year_edge_case_after(self):
# Extra day loops back to be 02/28/1900
start = arrow.Arrow.fromdatetime(datetime(1900, 2, 28))
test = arrow.Arrow.excel_date(60)
assert start == test

def test_windows_date_begin(self):
# Testing in Excel shows 1/1/1900 is serial number 1,
start = arrow.Arrow.fromdatetime(datetime(1900, 1, 1))
test = arrow.Arrow.excel_date(1)
assert start == test

def test_windows_date_large(self):
start = arrow.Arrow.fromdatetime(datetime(1994, 7, 4))
test = arrow.Arrow.excel_date(34519)
assert start == test

def test_mac_date_leap_year_edge_case(self):
start = arrow.Arrow.fromdatetime(datetime(1904, 2, 29))
test = arrow.Arrow.excel_date(59, default_windows_date=False)
assert start == test

def test_mac_date_leap_year_edge_case_after(self):
# Should be no lopp back as no edge case with macOS date system
start = arrow.Arrow.fromdatetime(datetime(1904, 3, 1))
test = arrow.Arrow.excel_date(60, default_windows_date=False)
assert start == test

def test_mac_date_begin(self):
# Testing in Excel shows 1/1/1904 is serial number 0,
start = arrow.Arrow.fromdatetime(datetime(1904, 1, 1))
test = arrow.Arrow.excel_date(0, default_windows_date=False)
assert start == test

def test_mac_date_large(self):
start = arrow.Arrow.fromdatetime(datetime(1998, 7, 5))
test = arrow.Arrow.excel_date(34519, default_windows_date=False)
assert start == test


class TestArrowUtil:
def test_get_datetime(self):

Expand Down

0 comments on commit 9e0cb4f

Please sign in to comment.