diff --git a/arrow/arrow.py b/arrow/arrow.py index 28e0b4c5d..89c3e50ae 100644 --- a/arrow/arrow.py +++ b/arrow/arrow.py @@ -162,7 +162,7 @@ def fromtimestamp(cls, timestamp, tzinfo=None): "The provided timestamp '{}' is invalid.".format(timestamp) ) - dt = util.safe_fromtimestamp(float(timestamp), tzinfo) + dt = datetime.fromtimestamp(float(timestamp), tzinfo) return cls( dt.year, @@ -188,7 +188,7 @@ def utcfromtimestamp(cls, timestamp): "The provided timestamp '{}' is invalid.".format(timestamp) ) - dt = util.safe_utcfromtimestamp(float(timestamp)) + dt = datetime.utcfromtimestamp(float(timestamp)) return cls( dt.year, diff --git a/arrow/parser.py b/arrow/parser.py index 5f8c07b5e..379c68737 100644 --- a/arrow/parser.py +++ b/arrow/parser.py @@ -6,7 +6,7 @@ from dateutil import tz -from arrow import locales, util +from arrow import locales from arrow.constants import MAX_TIMESTAMP, MAX_TIMESTAMP_MS, MAX_TIMESTAMP_US try: @@ -383,7 +383,7 @@ def _build_datetime(parts): timestamp = parts.get("timestamp") if timestamp is not None: - return util.safe_fromtimestamp(timestamp, tz=tz.tzutc()) + return datetime.fromtimestamp(timestamp, tz=tz.tzutc()) expanded_timestamp = parts.get("expanded_timestamp") @@ -401,7 +401,7 @@ def _build_datetime(parts): ) ) - return util.safe_fromtimestamp(expanded_timestamp, tz=tz.tzutc()) + return datetime.fromtimestamp(expanded_timestamp, tz=tz.tzutc()) day_of_year = parts.get("day_of_year") diff --git a/arrow/util.py b/arrow/util.py index cf3bf5bba..62f1a0537 100644 --- a/arrow/util.py +++ b/arrow/util.py @@ -2,11 +2,6 @@ from __future__ import absolute_import import datetime -import math -import time -from os import name as os_name - -import dateutil def total_seconds(td): # pragma: no cover @@ -28,44 +23,6 @@ def is_timestamp(value): return False -def windows_datetime_from_timestamp(timestamp, tz=None): - """Computes datetime from timestamp. Supports negative timestamps on Windows platform.""" - sec_frac, sec = math.modf(timestamp) - dt = datetime.datetime(1970, 1, 1, tzinfo=dateutil.tz.tzutc()) + datetime.timedelta( - seconds=sec, microseconds=sec_frac * 1000000 - ) - if tz is None: - tz = dateutil.tz.tzlocal() - - if tz == dateutil.tz.tzlocal(): - # because datetime.astimezone does not work on Windows for tzlocal() and dates before the 1970-01-01 - # take timestamp from appropriate time of the year, because of daylight saving time changes - ts = time.mktime(dt.replace(year=1970).timetuple()) - dt += datetime.datetime.fromtimestamp(ts) - datetime.datetime.utcfromtimestamp( - ts - ) - dt = dt.replace(tzinfo=dateutil.tz.tzlocal()) - else: - dt = dt.astimezone(tz) - return dt - - -def safe_utcfromtimestamp(timestamp): - """ datetime.utcfromtimestamp alternative which supports negative timestamps on Windows platform.""" - if os_name == "nt" and timestamp < 0: - return windows_datetime_from_timestamp(timestamp, dateutil.tz.tzutc()) - else: - return datetime.datetime.utcfromtimestamp(timestamp) - - -def safe_fromtimestamp(timestamp, tz=None): - """ datetime.fromtimestamp alternative which supports negative timestamps on Windows platform.""" - if os_name == "nt" and timestamp < 0: - return windows_datetime_from_timestamp(timestamp, tz) - else: - return datetime.datetime.fromtimestamp(timestamp, tz) - - # Credit to https://stackoverflow.com/a/1700069 def iso_to_gregorian(iso_year, iso_week, iso_day): """Converts an ISO week date tuple into a datetime object.""" @@ -100,12 +57,4 @@ def isstr(s): return isinstance(s, str) -__all__ = [ - "total_seconds", - "is_timestamp", - "isstr", - "iso_to_gregorian", - "windows_datetime_from_timestamp", - "safe_utcfromtimestamp", - "safe_fromtimestamp", -] +__all__ = ["total_seconds", "is_timestamp", "isstr", "iso_to_gregorian"] diff --git a/tests/parser_tests.py b/tests/parser_tests.py index 08c20d1ac..c72da044b 100644 --- a/tests/parser_tests.py +++ b/tests/parser_tests.py @@ -2,13 +2,14 @@ from __future__ import unicode_literals import calendar +import os import time from datetime import datetime from chai import Chai from dateutil import tz -from arrow import parser, util +from arrow import parser from arrow.constants import MAX_TIMESTAMP_US from arrow.parser import DateTimeParser, ParserError, ParserMatchError @@ -227,19 +228,23 @@ def test_parse_timestamp(self): self.parser.parse("{:f}123456".format(float_timestamp), "X"), self.expected ) - # regression test for issue #662 - negative_int_timestamp = -int_timestamp - self.expected = util.safe_fromtimestamp(negative_int_timestamp, tz=tz_utc) - self.assertEqual( - self.parser.parse("{:d}".format(negative_int_timestamp), "X"), self.expected - ) + # NOTE: negative timestamps cannot be handled by datetime on Window + # Must use timedelta to handle them. ref: https://stackoverflow.com/questions/36179914 + if os.name != "nt": + # regression test for issue #662 + negative_int_timestamp = -int_timestamp + self.expected = datetime.fromtimestamp(negative_int_timestamp, tz=tz_utc) + self.assertEqual( + self.parser.parse("{:d}".format(negative_int_timestamp), "X"), + self.expected, + ) - negative_float_timestamp = -float_timestamp - self.expected = util.safe_fromtimestamp(negative_float_timestamp, tz=tz_utc) - self.assertEqual( - self.parser.parse("{:f}".format(negative_float_timestamp), "X"), - self.expected, - ) + negative_float_timestamp = -float_timestamp + self.expected = datetime.fromtimestamp(negative_float_timestamp, tz=tz_utc) + self.assertEqual( + self.parser.parse("{:f}".format(negative_float_timestamp), "X"), + self.expected, + ) # NOTE: timestamps cannot be parsed from natural language strings (by removing the ^...$) because it will # break cases like "15 Jul 2000" and a format list (see issue #447) diff --git a/tests/util_tests.py b/tests/util_tests.py index 980a0a93e..bb84c44b7 100644 --- a/tests/util_tests.py +++ b/tests/util_tests.py @@ -1,10 +1,7 @@ # -*- coding: utf-8 -*- import time -from datetime import datetime from chai import Chai -from dateutil import tz -from mock import patch from arrow import util @@ -36,63 +33,3 @@ def test_iso_gregorian(self): with self.assertRaises(ValueError): util.iso_to_gregorian(2013, 8, 0) - - def test_windows_datetime_from_timestamp(self): - timestamp = 1572204340.6460679 - result = util.windows_datetime_from_timestamp(timestamp) - expected = datetime.fromtimestamp(timestamp).replace(tzinfo=tz.tzlocal()) - self.assertEqual(result, expected) - - def test_windows_datetime_from_timestamp_utc(self): - timestamp = 1572204340.6460679 - result = util.windows_datetime_from_timestamp(timestamp, tz.tzutc()) - expected = datetime.utcfromtimestamp(timestamp).replace(tzinfo=tz.tzutc()) - self.assertEqual(result, expected) - - def test_safe_utcfromtimestamp(self): - timestamp = 1572204340.6460679 - result = util.safe_utcfromtimestamp(timestamp).replace(tzinfo=tz.tzutc()) - expected = datetime.utcfromtimestamp(timestamp).replace(tzinfo=tz.tzutc()) - self.assertEqual(result, expected) - - def test_safe_fromtimestamp_default_tz(self): - timestamp = 1572204340.6460679 - result = util.safe_fromtimestamp(timestamp).replace(tzinfo=tz.tzlocal()) - expected = datetime.fromtimestamp(timestamp).replace(tzinfo=tz.tzlocal()) - self.assertEqual(result, expected) - - def test_safe_fromtimestamp_paris_tz(self): - timestamp = 1572204340.6460679 - result = util.safe_fromtimestamp(timestamp, tz.gettz("Europe/Paris")) - expected = datetime.fromtimestamp(timestamp, tz.gettz("Europe/Paris")) - self.assertEqual(result, expected) - - def test_safe_utcfromtimestamp_negative(self): - timestamp = -1572204340.6460679 - result = util.safe_utcfromtimestamp(timestamp).replace(tzinfo=tz.tzutc()) - expected = datetime(1920, 3, 7, 4, 34, 19, 353932, tzinfo=tz.tzutc()) - self.assertEqual(result, expected) - - def test_safe_fromtimestamp_negative(self): - timestamp = -1572204340.6460679 - result = util.safe_fromtimestamp(timestamp, tz.gettz("Europe/Paris")) - expected = datetime( - 1920, 3, 7, 5, 34, 19, 353932, tzinfo=tz.gettz("Europe/Paris") - ) - self.assertEqual(result, expected) - - @patch.object(util, "os_name", "nt") - def test_safe_utcfromtimestamp_negative_nt(self): - timestamp = -1572204340.6460679 - result = util.safe_utcfromtimestamp(timestamp).replace(tzinfo=tz.tzutc()) - expected = datetime(1920, 3, 7, 4, 34, 19, 353932, tzinfo=tz.tzutc()) - self.assertEqual(result, expected) - - @patch.object(util, "os_name", "nt") - def test_safe_fromtimestamp_negative_nt(self): - timestamp = -1572204340.6460679 - result = util.safe_fromtimestamp(timestamp, tz.gettz("Europe/Paris")) - expected = datetime( - 1920, 3, 7, 5, 34, 19, 353932, tzinfo=tz.gettz("Europe/Paris") - ) - self.assertEqual(result, expected)