From 85a4f4f13c0c7a67a1ec59a725fe687200d9ecfa Mon Sep 17 00:00:00 2001 From: Daniel Kirkham Date: Mon, 12 Sep 2016 14:47:58 +0100 Subject: [PATCH 1/5] Ensure dates are rounded to nearest second --- cf_units/__init__.py | 38 +++- .../unit/test__num2date_to_nearest_second.py | 210 ++++++++++++++++++ 2 files changed, 246 insertions(+), 2 deletions(-) create mode 100644 cf_units/tests/unit/test__num2date_to_nearest_second.py diff --git a/cf_units/__init__.py b/cf_units/__init__.py index 1f61a09a..6d364900 100644 --- a/cf_units/__init__.py +++ b/cf_units/__init__.py @@ -753,7 +753,41 @@ def num2date(time_value, unit, calendar): if unit_string.endswith(" since epoch"): unit_string = unit_string.replace("epoch", EPOCH) cdftime = netcdftime.utime(unit_string, calendar=calendar) - return cdftime.num2date(time_value) + return _num2date_to_nearest_second(time_value, cdftime) + + +def _num2date_to_nearest_second(time_value, utime): + # Return datetime encoding of numeric time value with respect to the given + # time reference units, with a resolution of 1 second. + + # We account for the edge case where the time is in seconds and has a + # half second: utime.num2date() may produce a date that would round + # down. + # + # Note that this behaviour is different to the num2date function in older + # versions of netcdftime that didn't have microsecond precision. In those + # versions, a half-second value would be rounded up or down arbitrarily. It + # is probably not possible to replicate that behaviour with the current + # version (1.4.1), if one wished to do so for the sake of consistency. + has_half_second = utime.units == 'seconds' and \ + time_value % 1. == 0.5 + date = utime.num2date(time_value) + try: + microsecond = date.microsecond + except AttributeError: + microsecond = 0 + if has_half_second or microsecond > 0: + if has_half_second or microsecond >= 500000: + seconds = Unit('second') + second_frac = seconds.convert(0.75, utime.units) + time_value += second_frac + date = utime.num2date(time_value) + # Create a date object of the same type returned by utime.num2date() + # (either datetime.datetime or netcdftime.datetime), discarding the + # microseconds + date = date.__class__(date.year, date.month, date.day, + date.hour, date.minute, date.second) + return date ######################################################################## @@ -2078,4 +2112,4 @@ def num2date(self, time_value): """ cdf_utime = self.utime() - return cdf_utime.num2date(time_value) + return _num2date_to_nearest_second(time_value, cdf_utime) diff --git a/cf_units/tests/unit/test__num2date_to_nearest_second.py b/cf_units/tests/unit/test__num2date_to_nearest_second.py new file mode 100644 index 00000000..b4468807 --- /dev/null +++ b/cf_units/tests/unit/test__num2date_to_nearest_second.py @@ -0,0 +1,210 @@ +# (C) British Crown Copyright 2016, Met Office +# +# This file is part of cf_units. +# +# cf_units is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cf_units is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with cf_units. If not, see . +"""Test function :func:`cf_units._num2date_to_nearest_second`.""" + +from __future__ import (absolute_import, division, print_function) +from six.moves import (filter, input, map, range, zip) # noqa + +import unittest +import datetime + +import numpy as np +import netcdftime + +from cf_units import _num2date_to_nearest_second, Unit + + +class Test(unittest.TestCase): + def setup_units(self, calendar): + self.useconds = netcdftime.utime('seconds since 1970-01-01', calendar) + self.uminutes = netcdftime.utime('minutes since 1970-01-01', calendar) + self.uhours = netcdftime.utime('hours since 1970-01-01', calendar) + self.udays = netcdftime.utime('days since 1970-01-01', calendar) + + def check_dates(self, nums, utimes, expected): + for num, utime, exp in zip(nums, utimes, expected): + res = _num2date_to_nearest_second(num, utime) + self.assertEqual(exp, res) + + # Gregorian Calendar tests + + def test_simple_gregorian(self): + self.setup_units('gregorian') + nums = [20., 40., + 75., 150., + 8., 16., + 300., 600.] + utimes = [self.useconds, self.useconds, + self.uminutes, self.uminutes, + self.uhours, self.uhours, + self.udays, self.udays] + expected = [datetime.datetime(1970, 1, 1, 0, 0, 20), + datetime.datetime(1970, 1, 1, 0, 0, 40), + datetime.datetime(1970, 1, 1, 1, 15), + datetime.datetime(1970, 1, 1, 2, 30), + datetime.datetime(1970, 1, 1, 8), + datetime.datetime(1970, 1, 1, 16), + datetime.datetime(1970, 10, 28), + datetime.datetime(1971, 8, 24)] + + self.check_dates(nums, utimes, expected) + + def test_fractional_gregorian(self): + self.setup_units('gregorian') + nums = [5./60., 10./60., + 15./60., 30./60., + 8./24., 16./24.] + utimes = [self.uminutes, self.uminutes, + self.uhours, self.uhours, + self.udays, self.udays] + expected = [datetime.datetime(1970, 1, 1, 0, 0, 5), + datetime.datetime(1970, 1, 1, 0, 0, 10), + datetime.datetime(1970, 1, 1, 0, 15), + datetime.datetime(1970, 1, 1, 0, 30), + datetime.datetime(1970, 1, 1, 8), + datetime.datetime(1970, 1, 1, 16)] + + self.check_dates(nums, utimes, expected) + + def test_fractional_second_gregorian(self): + self.setup_units('gregorian') + nums = [0.25, 0.5, 0.75, + 1.5, 2.5, 3.5, 4.5] + utimes = [self.useconds] * 7 + expected = [datetime.datetime(1970, 1, 1, 0, 0, 0), + datetime.datetime(1970, 1, 1, 0, 0, 1), + datetime.datetime(1970, 1, 1, 0, 0, 1), + datetime.datetime(1970, 1, 1, 0, 0, 2), + datetime.datetime(1970, 1, 1, 0, 0, 3), + datetime.datetime(1970, 1, 1, 0, 0, 4), + datetime.datetime(1970, 1, 1, 0, 0, 5)] + + self.check_dates(nums, utimes, expected) + + # 360 day Calendar tests + + def test_simple_360_day(self): + self.setup_units('360_day') + nums = [20., 40., + 75., 150., + 8., 16., + 300., 600.] + utimes = [self.useconds, self.useconds, + self.uminutes, self.uminutes, + self.uhours, self.uhours, + self.udays, self.udays] + expected = [netcdftime.datetime(1970, 1, 1, 0, 0, 20), + netcdftime.datetime(1970, 1, 1, 0, 0, 40), + netcdftime.datetime(1970, 1, 1, 1, 15), + netcdftime.datetime(1970, 1, 1, 2, 30), + netcdftime.datetime(1970, 1, 1, 8), + netcdftime.datetime(1970, 1, 1, 16), + netcdftime.datetime(1970, 11, 1), + netcdftime.datetime(1971, 9, 1)] + + self.check_dates(nums, utimes, expected) + + def test_fractional_360_day(self): + self.setup_units('360_day') + nums = [5./60., 10./60., + 15./60., 30./60., + 8./24., 16./24.] + utimes = [self.uminutes, self.uminutes, + self.uhours, self.uhours, + self.udays, self.udays] + expected = [netcdftime.datetime(1970, 1, 1, 0, 0, 5), + netcdftime.datetime(1970, 1, 1, 0, 0, 10), + netcdftime.datetime(1970, 1, 1, 0, 15), + netcdftime.datetime(1970, 1, 1, 0, 30), + netcdftime.datetime(1970, 1, 1, 8), + netcdftime.datetime(1970, 1, 1, 16)] + + self.check_dates(nums, utimes, expected) + + def test_fractional_second_360_day(self): + self.setup_units('360_day') + nums = [0.25, 0.5, 0.75, + 1.5, 2.5, 3.5, 4.5] + utimes = [self.useconds] * 7 + expected = [netcdftime.datetime(1970, 1, 1, 0, 0, 0), + netcdftime.datetime(1970, 1, 1, 0, 0, 1), + netcdftime.datetime(1970, 1, 1, 0, 0, 1), + netcdftime.datetime(1970, 1, 1, 0, 0, 2), + netcdftime.datetime(1970, 1, 1, 0, 0, 3), + netcdftime.datetime(1970, 1, 1, 0, 0, 4), + netcdftime.datetime(1970, 1, 1, 0, 0, 5)] + + self.check_dates(nums, utimes, expected) + + # 365 day Calendar tests + + def test_simple_365_day(self): + self.setup_units('365_day') + nums = [20., 40., + 75., 150., + 8., 16., + 300., 600.] + utimes = [self.useconds, self.useconds, + self.uminutes, self.uminutes, + self.uhours, self.uhours, + self.udays, self.udays] + expected = [netcdftime.datetime(1970, 1, 1, 0, 0, 20), + netcdftime.datetime(1970, 1, 1, 0, 0, 40), + netcdftime.datetime(1970, 1, 1, 1, 15), + netcdftime.datetime(1970, 1, 1, 2, 30), + netcdftime.datetime(1970, 1, 1, 8), + netcdftime.datetime(1970, 1, 1, 16), + netcdftime.datetime(1970, 10, 28), + netcdftime.datetime(1971, 8, 24)] + + self.check_dates(nums, utimes, expected) + + def test_fractional_365_day(self): + self.setup_units('365_day') + nums = [5./60., 10./60., + 15./60., 30./60., + 8./24., 16./24.] + utimes = [self.uminutes, self.uminutes, + self.uhours, self.uhours, + self.udays, self.udays] + + expected = [netcdftime.datetime(1970, 1, 1, 0, 0, 5), + netcdftime.datetime(1970, 1, 1, 0, 0, 10), + netcdftime.datetime(1970, 1, 1, 0, 15), + netcdftime.datetime(1970, 1, 1, 0, 30), + netcdftime.datetime(1970, 1, 1, 8), + netcdftime.datetime(1970, 1, 1, 16)] + + self.check_dates(nums, utimes, expected) + + def test_fractional_second_365_day(self): + self.setup_units('365_day') + nums = [0.25, 0.5, 0.75, + 1.5, 2.5, 3.5, 4.5] + utimes = [self.useconds] * 7 + expected = [netcdftime.datetime(1970, 1, 1, 0, 0, 0), + netcdftime.datetime(1970, 1, 1, 0, 0, 1), + netcdftime.datetime(1970, 1, 1, 0, 0, 1), + netcdftime.datetime(1970, 1, 1, 0, 0, 2), + netcdftime.datetime(1970, 1, 1, 0, 0, 3), + netcdftime.datetime(1970, 1, 1, 0, 0, 4), + netcdftime.datetime(1970, 1, 1, 0, 0, 5)] + + self.check_dates(nums, utimes, expected) + +if __name__ == '__main__': + unittest.main() From 824102eb8ccbb0ab832df065ade0329a72035e77 Mon Sep 17 00:00:00 2001 From: Daniel Kirkham Date: Tue, 13 Sep 2016 15:13:37 +0100 Subject: [PATCH 2/5] Vectorise _num2date_to_nearest_seconds. Fix date2num --- cf_units/__init__.py | 64 ++++++++++++++----- cf_units/tests/integration/__init__.py | 0 .../test__num2date_to_nearest_second.py | 27 ++++++++ cf_units/tests/integration/test_date2num.py | 64 +++++++++++++++++++ 4 files changed, 138 insertions(+), 17 deletions(-) create mode 100644 cf_units/tests/integration/__init__.py rename cf_units/tests/{unit => integration}/test__num2date_to_nearest_second.py (88%) create mode 100644 cf_units/tests/integration/test_date2num.py diff --git a/cf_units/__init__.py b/cf_units/__init__.py index 6d364900..0949751f 100644 --- a/cf_units/__init__.py +++ b/cf_units/__init__.py @@ -686,9 +686,26 @@ def date2num(date, unit, calendar): if unit_string.endswith(" since epoch"): unit_string = unit_string.replace("epoch", EPOCH) cdftime = netcdftime.utime(unit_string, calendar=calendar) + date = _discard_microsecond(date) return cdftime.date2num(date) +def _discard_microsecond(date): + # Return a date with the microsecond componenet discarded. `date` should be + # a datetime.datetime or netcdftime.datetime object, or a list of such + # objects + try: + l = list(date) + except TypeError: + dates = np.array([date]) + is_scalar = True + else: + dates = np.array(l) + is_scalar = False + dates = [d.__class__(d.year, d.month, d.day, d.hour, d.minute, d.second) + for d in dates] + return dates[0] if is_scalar else dates + def num2date(time_value, unit, calendar): """ Return datetime encoding of numeric time value (resolution of 1 second). @@ -714,6 +731,9 @@ def num2date(time_value, unit, calendar): do not contain a time-zone offset, even if the specified unit contains one. + Works for scalars, sequences and numpy arrays. Returns a scalar + if input is a scalar, else returns a numpy array. + Args: * time_value (float): @@ -759,6 +779,14 @@ def num2date(time_value, unit, calendar): def _num2date_to_nearest_second(time_value, utime): # Return datetime encoding of numeric time value with respect to the given # time reference units, with a resolution of 1 second. + try: + l = list(time_value) + except TypeError: + time_values = np.array([time_value]) + is_scalar = True + else: + time_values = np.array(l) + is_scalar = False # We account for the edge case where the time is in seconds and has a # half second: utime.num2date() may produce a date that would round @@ -769,25 +797,26 @@ def _num2date_to_nearest_second(time_value, utime): # versions, a half-second value would be rounded up or down arbitrarily. It # is probably not possible to replicate that behaviour with the current # version (1.4.1), if one wished to do so for the sake of consistency. - has_half_second = utime.units == 'seconds' and \ - time_value % 1. == 0.5 - date = utime.num2date(time_value) + has_half_seconds = np.logical_and(utime.units == 'seconds', + time_values % 1. == 0.5) + dates = utime.num2date(time_values) try: - microsecond = date.microsecond + # We can assume all or none of the dates have a microsecond attribute + microseconds = np.array([d.microsecond for d in dates]) except AttributeError: - microsecond = 0 - if has_half_second or microsecond > 0: - if has_half_second or microsecond >= 500000: - seconds = Unit('second') - second_frac = seconds.convert(0.75, utime.units) - time_value += second_frac - date = utime.num2date(time_value) - # Create a date object of the same type returned by utime.num2date() - # (either datetime.datetime or netcdftime.datetime), discarding the - # microseconds - date = date.__class__(date.year, date.month, date.day, - date.hour, date.minute, date.second) - return date + microseconds = 0 + round_mask = np.logical_or(has_half_seconds, microseconds != 0) + ceil_mask = np.logical_or(has_half_seconds, microseconds >= 500000) + if time_values[ceil_mask].size > 0: + useconds = Unit('second') + second_frac = useconds.convert(0.75, utime.units) + dates[ceil_mask] = utime.num2date(time_values[ceil_mask] + second_frac) + # Create date objects of the same type returned by utime.num2date() + # (either datetime.datetime or netcdftime.datetime), discarding the + # microseconds + dates[round_mask] = _discard_microsecond(dates[round_mask]) + + return dates[0] if is_scalar else dates ######################################################################## @@ -2070,6 +2099,7 @@ def date2num(self, date): """ cdf_utime = self.utime() + date = _discard_microsecond(date) return cdf_utime.date2num(date) def num2date(self, time_value): diff --git a/cf_units/tests/integration/__init__.py b/cf_units/tests/integration/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/cf_units/tests/unit/test__num2date_to_nearest_second.py b/cf_units/tests/integration/test__num2date_to_nearest_second.py similarity index 88% rename from cf_units/tests/unit/test__num2date_to_nearest_second.py rename to cf_units/tests/integration/test__num2date_to_nearest_second.py index b4468807..6c161d7f 100644 --- a/cf_units/tests/unit/test__num2date_to_nearest_second.py +++ b/cf_units/tests/integration/test__num2date_to_nearest_second.py @@ -23,6 +23,7 @@ import datetime import numpy as np +import numpy.testing import netcdftime from cf_units import _num2date_to_nearest_second, Unit @@ -40,6 +41,32 @@ def check_dates(self, nums, utimes, expected): res = _num2date_to_nearest_second(num, utime) self.assertEqual(exp, res) + def test_scalar(self): + utime = netcdftime.utime('seconds since 1970-01-01', 'gregorian') + num = 5. + exp = datetime.datetime(1970, 1, 1, 0, 0, 5) + res = _num2date_to_nearest_second(num, utime) + self.assertEqual(exp, res) + + def test_sequence(self): + utime = netcdftime.utime('seconds since 1970-01-01', 'gregorian') + nums = [20., 40., 60., 80, 100.] + exp = [datetime.datetime(1970, 1, 1, 0, 0, 20), + datetime.datetime(1970, 1, 1, 0, 0, 40), + datetime.datetime(1970, 1, 1, 0, 1), + datetime.datetime(1970, 1, 1, 0, 1, 20), + datetime.datetime(1970, 1, 1, 0, 1, 40)] + res = _num2date_to_nearest_second(nums, utime) + np.testing.assert_array_equal(exp, res) + + def test_iter(self): + utime = netcdftime.utime('seconds since 1970-01-01', 'gregorian') + nums = iter([5., 10.]) + exp = [datetime.datetime(1970, 1, 1, 0, 0, 5), + datetime.datetime(1970, 1, 1, 0, 0, 10)] + res = _num2date_to_nearest_second(nums, utime) + np.testing.assert_array_equal(exp, res) + # Gregorian Calendar tests def test_simple_gregorian(self): diff --git a/cf_units/tests/integration/test_date2num.py b/cf_units/tests/integration/test_date2num.py new file mode 100644 index 00000000..b374e77d --- /dev/null +++ b/cf_units/tests/integration/test_date2num.py @@ -0,0 +1,64 @@ +# (C) British Crown Copyright 2016, Met Office +# +# This file is part of cf_units. +# +# cf_units is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# cf_units is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with cf_units. If not, see . +"""Test function :func:`cf_units.date2num`.""" + +from __future__ import (absolute_import, division, print_function) +from six.moves import (filter, input, map, range, zip) # noqa + +import unittest +import datetime + +import numpy as np +import numpy.testing +import netcdftime + +from cf_units import date2num, Unit + + +class Test(unittest.TestCase): + def setUp(self): + self.unit = 'seconds since 1970-01-01' + self.calendar = 'gregorian' + + def test_single(self): + date = datetime.datetime(1970, 1, 1, 0, 0, 5) + exp = 5. + res = date2num(date, self.unit, self.calendar) + # num2date won't return an exact value representing the date, + # even if one exists + self.assertAlmostEqual(exp, res, places=4) + + def test_sequence(self): + dates = [datetime.datetime(1970, 1, 1, 0, 0, 20), + datetime.datetime(1970, 1, 1, 0, 0, 40), + datetime.datetime(1970, 1, 1, 0, 1), + datetime.datetime(1970, 1, 1, 0, 1, 20), + datetime.datetime(1970, 1, 1, 0, 1, 40)] + exp = [20., 40., 60., 80, 100.] + res = date2num(dates, self.unit, self.calendar) + np.testing.assert_array_almost_equal(exp, res, decimal=4) + + def test_discard_mircosecond(self): + date = datetime.datetime(1970, 1, 1, 0, 0, 5, 750000) + exp = 5. + res= date2num(date, self.unit, self.calendar) + + self.assertAlmostEqual(exp, res, places=4) + + +if __name__ == '__main__': + unittest.main() From a7018a1a36b7795f327a5cff350b18fda08cbdff Mon Sep 17 00:00:00 2001 From: Daniel Kirkham Date: Wed, 14 Sep 2016 09:28:50 +0100 Subject: [PATCH 3/5] Fix pep8 failures --- cf_units/__init__.py | 1 + cf_units/tests/integration/test_date2num.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cf_units/__init__.py b/cf_units/__init__.py index 0949751f..7dde2ab9 100644 --- a/cf_units/__init__.py +++ b/cf_units/__init__.py @@ -706,6 +706,7 @@ def _discard_microsecond(date): for d in dates] return dates[0] if is_scalar else dates + def num2date(time_value, unit, calendar): """ Return datetime encoding of numeric time value (resolution of 1 second). diff --git a/cf_units/tests/integration/test_date2num.py b/cf_units/tests/integration/test_date2num.py index b374e77d..1ce9de9b 100644 --- a/cf_units/tests/integration/test_date2num.py +++ b/cf_units/tests/integration/test_date2num.py @@ -55,8 +55,8 @@ def test_sequence(self): def test_discard_mircosecond(self): date = datetime.datetime(1970, 1, 1, 0, 0, 5, 750000) exp = 5. - res= date2num(date, self.unit, self.calendar) - + res = date2num(date, self.unit, self.calendar) + self.assertAlmostEqual(exp, res, places=4) From 0f19ad189ffb43cc8001e7a7dd78b4e3098aea23 Mon Sep 17 00:00:00 2001 From: Daniel Kirkham Date: Wed, 14 Sep 2016 15:15:17 +0100 Subject: [PATCH 4/5] Refactor _discard_microsecond --- cf_units/__init__.py | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/cf_units/__init__.py b/cf_units/__init__.py index 7dde2ab9..b12e3dec 100644 --- a/cf_units/__init__.py +++ b/cf_units/__init__.py @@ -694,16 +694,12 @@ def _discard_microsecond(date): # Return a date with the microsecond componenet discarded. `date` should be # a datetime.datetime or netcdftime.datetime object, or a list of such # objects - try: - l = list(date) - except TypeError: - dates = np.array([date]) + is_scalar = False + if not hasattr(date, '__iter__'): + date = [date] is_scalar = True - else: - dates = np.array(l) - is_scalar = False dates = [d.__class__(d.year, d.month, d.day, d.hour, d.minute, d.second) - for d in dates] + for d in date] return dates[0] if is_scalar else dates From 00297eb02d04d9f57adb044b4edbadb8a4850586 Mon Sep 17 00:00:00 2001 From: Daniel Kirkham Date: Wed, 14 Sep 2016 16:32:32 +0100 Subject: [PATCH 5/5] Refactor num2date_to_nearest_second. Create docstrings --- cf_units/__init__.py | 43 +++++++++++++++++++++++++++++++------------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/cf_units/__init__.py b/cf_units/__init__.py index b12e3dec..17542838 100644 --- a/cf_units/__init__.py +++ b/cf_units/__init__.py @@ -691,9 +691,21 @@ def date2num(date, unit, calendar): def _discard_microsecond(date): - # Return a date with the microsecond componenet discarded. `date` should be - # a datetime.datetime or netcdftime.datetime object, or a list of such - # objects + """ + Return a date with the microsecond componenet discarded. + + Works for scalars, sequences and numpy arrays. Returns a scalar + if input is a scalar, else returns a numpy array. + + Args: + + * date (datetime.datetime or netcdftime.datetime): + Date value/s + + Returns: + datetime, or list of datetime object. + + """ is_scalar = False if not hasattr(date, '__iter__'): date = [date] @@ -774,16 +786,23 @@ def num2date(time_value, unit, calendar): def _num2date_to_nearest_second(time_value, utime): - # Return datetime encoding of numeric time value with respect to the given - # time reference units, with a resolution of 1 second. - try: - l = list(time_value) - except TypeError: - time_values = np.array([time_value]) + """ + Return datetime encoding of numeric time value with respect to the given + time reference units, with a resolution of 1 second. + + * time_value (float): + Numeric time value/s. + * utime (netcdftime.utime): + netcdf.utime object with which to perform the conversion/s. + + Returns: + datetime, or numpy.ndarray of datetime object. + """ + is_scalar = False + if not hasattr(time_value, '__iter__'): + time_value = [time_value] is_scalar = True - else: - time_values = np.array(l) - is_scalar = False + time_values = np.array(list(time_value)) # We account for the edge case where the time is in seconds and has a # half second: utime.num2date() may produce a date that would round