Skip to content

Commit

Permalink
Merge pull request #3048 from meold/fix_python_datetime_functions
Browse files Browse the repository at this point in the history
Fix python datetime functions
  • Loading branch information
kroitor committed Jun 6, 2018
2 parents d7a312b + 388ab18 commit f257568
Show file tree
Hide file tree
Showing 2 changed files with 106 additions and 21 deletions.
62 changes: 41 additions & 21 deletions python/ccxt/base/exchange.py
Expand Up @@ -665,11 +665,19 @@ def microseconds():
return int(time.time() * 1000000)

@staticmethod
def iso8601(timestamp):
def iso8601(timestamp=None):
if timestamp is None:
return timestamp
utc = datetime.datetime.utcfromtimestamp(int(round(timestamp / 1000)))
return utc.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-6] + "{:<03d}".format(int(timestamp) % 1000) + 'Z'
if not isinstance(timestamp, int):
return None
if int(timestamp) < 0:
return None

try:
utc = datetime.datetime.utcfromtimestamp(int(round(timestamp / 1000)))
return utc.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-6] + "{:<03d}".format(int(timestamp) % 1000) + 'Z'
except (TypeError, OverflowError, OSError):
return None

@staticmethod
def dmy(timestamp, infix='-'):
Expand All @@ -687,18 +695,25 @@ def ymdhms(timestamp, infix=' '):
return utc_datetime.strftime('%Y-%m-%d' + infix + '%H:%M:%S')

@staticmethod
def parse_date(timestamp):
def parse_date(timestamp=None):
if timestamp is None:
return timestamp
if not isinstance(timestamp, str):
return None
if 'GMT' in timestamp:
string = ''.join([str(value) for value in parsedate(timestamp)[:6]]) + '.000Z'
dt = datetime.datetime.strptime(string, "%Y%m%d%H%M%S.%fZ")
return calendar.timegm(dt.utctimetuple()) * 1000
try:
string = ''.join([str(value) for value in parsedate(timestamp)[:6]]) + '.000Z'
dt = datetime.datetime.strptime(string, "%Y%m%d%H%M%S.%fZ")
return calendar.timegm(dt.utctimetuple()) * 1000
except (TypeError, OverflowError, OSError):
return None
else:
return Exchange.parse8601(timestamp)

@staticmethod
def parse8601(timestamp):
def parse8601(timestamp=None):
if timestamp is None:
return timestamp
yyyy = '([0-9]{4})-?'
mm = '([0-9]{2})-?'
dd = '([0-9]{2})(?:T|[\\s])?'
Expand All @@ -708,19 +723,24 @@ def parse8601(timestamp):
ms = '(\\.[0-9]{1,3})?'
tz = '(?:(\\+|\\-)([0-9]{2})\\:?([0-9]{2})|Z)?'
regex = r'' + yyyy + mm + dd + h + m + s + ms + tz
match = re.search(regex, timestamp, re.IGNORECASE)
yyyy, mm, dd, h, m, s, ms, sign, hours, minutes = match.groups()
ms = ms or '.000'
msint = int(ms[1:])
sign = sign or ''
sign = int(sign + '1')
hours = int(hours or 0) * sign
minutes = int(minutes or 0) * sign
offset = datetime.timedelta(hours=hours, minutes=minutes)
string = yyyy + mm + dd + h + m + s + ms + 'Z'
dt = datetime.datetime.strptime(string, "%Y%m%d%H%M%S.%fZ")
dt = dt + offset
return calendar.timegm(dt.utctimetuple()) * 1000 + msint
try:
match = re.search(regex, timestamp, re.IGNORECASE)
if match is None:
return None
yyyy, mm, dd, h, m, s, ms, sign, hours, minutes = match.groups()
ms = ms or '.000'
msint = int(ms[1:])
sign = sign or ''
sign = int(sign + '1')
hours = int(hours or 0) * sign
minutes = int(minutes or 0) * sign
offset = datetime.timedelta(hours=hours, minutes=minutes)
string = yyyy + mm + dd + h + m + s + ms + 'Z'
dt = datetime.datetime.strptime(string, "%Y%m%d%H%M%S.%fZ")
dt = dt + offset
return calendar.timegm(dt.utctimetuple()) * 1000 + msint
except (TypeError, OverflowError, OSError, ValueError):
return None

@staticmethod
def hash(request, algorithm='md5', digest='hex'):
Expand Down
65 changes: 65 additions & 0 deletions python/test/test_exchange_datetime_functions.py
@@ -0,0 +1,65 @@
from unittest import TestCase

import os
import sys

# ------------------------------------------------------------------------------

root = os.path.dirname(os.path.abspath(__file__))
sys.path.append(root)

# ------------------------------------------------------------------------------

import ccxt # noqa: E402

# ------------------------------------------------------------------------------


exchange = ccxt.Exchange()


class TestExchange(TestCase):
def test_iso8601(self):
self.assertEqual(exchange.iso8601(714862627000), '1992-08-26T20:57:07.000Z')
self.assertEqual(exchange.iso8601(0), '1970-01-01T00:00:00.000Z')

self.assertEqual(exchange.iso8601(None), None)
self.assertEqual(exchange.iso8601(), None)
self.assertEqual(exchange.iso8601(-1), None)
self.assertEqual(exchange.iso8601({}), None)
self.assertEqual(exchange.iso8601(''), None)
self.assertEqual(exchange.iso8601('a'), None)
self.assertEqual(exchange.iso8601([]), None)
self.assertEqual(exchange.iso8601([1]), None)

def test_parse_date(self):
self.assertEqual(exchange.parse_date('1996-04-26 00:00:00'), 830476800000)
self.assertEqual(exchange.parse_date('1996-04-26T01:23:47.000Z'), 830481827000)
self.assertEqual(exchange.parse_date('1996-13-13 00:00:00'), None)

self.assertEqual(exchange.parse_date('Sun, 18 Mar 2012 05:50:34 GMT'), 1332049834000)

self.assertEqual(exchange.parse_date('GMT'), None)
self.assertEqual(exchange.parse_date('42 GMT'), None)

self.assertEqual(exchange.parse_date(None), None)
self.assertEqual(exchange.parse_date(), None)
self.assertEqual(exchange.parse_date(1), None)
self.assertEqual(exchange.parse_date({}), None)
self.assertEqual(exchange.parse_date([]), None)
self.assertEqual(exchange.parse_date([1]), None)

def test_parse8601(self):
self.assertEqual(exchange.parse8601('1986-04-26T01:23:47.000Z'), 514862627000)

self.assertEqual(exchange.parse8601('1986-14-26T23:01:47.000Z'), None)
self.assertEqual(exchange.parse8601('1986-04-26T25:71:47.000Z'), None)

self.assertEqual(exchange.parse8601(None), None)
self.assertEqual(exchange.parse8601(), None)
self.assertEqual(exchange.parse8601(''), None)
self.assertEqual(exchange.parse8601('1'), None)
self.assertEqual(exchange.parse8601(1), None)
self.assertEqual(exchange.parse8601({}), None)
self.assertEqual(exchange.parse8601([]), None)
self.assertEqual(exchange.parse8601([1]), None)

0 comments on commit f257568

Please sign in to comment.