From 63130c2440913ee67b829d620291af0d0bb0e055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Grazina?= Date: Thu, 12 Sep 2024 18:38:46 +0100 Subject: [PATCH] Feat: support for TIME and TIMEVALUE --- SUPPORTED_FORMULAS.md | 8 ++++---- hotxlfp/formulas/dateandtime.py | 16 ++++++++++++++++ tests/test_operators.py | 29 +++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 4 deletions(-) diff --git a/SUPPORTED_FORMULAS.md b/SUPPORTED_FORMULAS.md index a709b3d..9b5854e 100644 --- a/SUPPORTED_FORMULAS.md +++ b/SUPPORTED_FORMULAS.md @@ -1,4 +1,4 @@ -# Supported Formulas - 152 +# Supported Formulas - 154 * ABS * ACOS @@ -141,6 +141,8 @@ * TANH * TEXT * TEXTJOIN +* TIME +* TIMEVALUE * TODAY * TRUE * UPPER @@ -154,7 +156,7 @@ * YEAR -# Not Yet Supported Formulas - 336 +# Not Yet Supported Formulas - 334 * ACCRINT * ACCRINTM @@ -458,8 +460,6 @@ * TBILLPRICE * TBILLYIELD * TDIST -* TIME -* TIMEVALUE * TINV * TRANSPOSE * TREND diff --git a/hotxlfp/formulas/dateandtime.py b/hotxlfp/formulas/dateandtime.py index accbe68..eb2fb5d 100644 --- a/hotxlfp/formulas/dateandtime.py +++ b/hotxlfp/formulas/dateandtime.py @@ -22,11 +22,27 @@ def DATE(year, month, day): return datetime.datetime(year, month, day) +@dispatcher.register_for('TIME') +def TIME(hour, minute, second): + year = utils.parse_number(hour) + minute = utils.parse_number(minute) + second = utils.parse_number(second) + if utils.any_is_error((year, minute, second)): + return error.VALUE + return datetime.datetime(1900, 1, 1, hour, minute, second) + + @dispatcher.register_for('DATEVALUE') def DATEVALUE(date): return utils.serialize_date(date) +@dispatcher.register_for('TIMEVALUE') +def TIMEVALUE(time): + # -1 is because excel has epoch starting on day 0 of 1900 and datetime starts at 1 + return utils.serialize_date(datetime.datetime.combine(utils.date_1900, utils.parse_date(time).time())) - 1 + + @dispatcher.register_for('YEAR') def YEAR(serial_number): serial_number = utils.parse_date(serial_number) diff --git a/tests/test_operators.py b/tests/test_operators.py index 6be9983..9251bfd 100644 --- a/tests/test_operators.py +++ b/tests/test_operators.py @@ -51,6 +51,35 @@ def test_div(self): self.assertEqual(ret['result'], None) self.assertEqual(ret['error'], '#DIV/0!') + def test_time_logic(self): + p = Parser(debug=True) + # Numbers + ret = p.parse('TIME(22,0,0) = TIME(23,0,0)') + self.assertEqual(ret['result'], False) # 0 + self.assertEqual(ret['error'], None) + ret = p.parse('TIME(22,0,0) <> TIME(23,0,0)') + self.assertEqual(ret['result'], True) + self.assertEqual(ret['error'], None) + ret = p.parse('TIME(22,0,0) > TIME(23,0,0)') + self.assertEqual(ret['result'], False) + self.assertEqual(ret['error'], None) + ret = p.parse('TIME(22,0,0) < TIME(23,0,0)') + self.assertEqual(ret['result'], True) + self.assertEqual(ret['error'], None) + ret = p.parse('HOUR(TIME(22,15,30))') + self.assertEqual(ret['result'], 22) + self.assertEqual(ret['error'], None) + ret = p.parse('MINUTE(TIME(22,15,30))') + self.assertEqual(ret['result'], 15) + self.assertEqual(ret['error'], None) + ret = p.parse('SECOND(TIME(22,15,30))') + self.assertEqual(ret['result'], 30) + self.assertEqual(ret['error'], None) + ret = p.parse('TIMEVALUE("12:00")') + self.assertEqual(ret['result'], 0.5) + self.assertEqual(ret['error'], None) + + def test_logic(self): p = Parser(debug=True) # Numbers