Last active
May 8, 2018 23:02
-
-
Save AlmightyOatmeal/6f7c0adbc0e089e3bc24f165ead2d61f to your computer and use it in GitHub Desktop.
Fun with Python datetime objects! Works on Python 2.7.x.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import datetime | |
import pytz | |
def convert_dt_to_milliseconds(dt_obj): | |
"""Convert datetime object to a Unix epoch timestamp in milliseconds. | |
:param dt_obj: Datetime object to be converted. | |
:type dt_obj: instance | |
:return: Milliseconds since the Unix epoch. | |
:rtype: int or long | |
""" | |
return int((dt_obj - datetime.datetime(1970, 1, 1)).total_seconds() * 1000) | |
def convert_utc_milliseconds_to_dt(utc_ms): | |
"""Convert Unix epoch timestamp in milliseconds to a datetime object. | |
:param utc_ms: Timestamp in milliseconds. | |
:type utc_ms: int or long | |
:return: Datetime.datetime() instance. | |
:rtype: instance | |
""" | |
return pytz.utc.localize(datetime.datetime.utcfromtimestamp(utc_ms / 1000)) | |
def get_utcnow_in_milliseconds(): | |
"""Get the current UTC time and convert it to a Unix epoch timestamp in milliseconds. | |
:return: Milliseconds since the Unix epoch. | |
:rtype: int or long | |
""" | |
return int((datetime.datetime.utcnow() - datetime.datetime(1970, 1, 1)).total_seconds() * 1000) | |
def get_utcnow_date_str(datetime_obj=None): | |
"""Get the date in ISO format. | |
:param datetime_obj: (optional) If specified, returns the ISO format of the `datetime` instance. | |
(default: None) | |
:type datetime_obj: datetime.datetime, datetime.date | |
:return: Date string. | |
:rtype: str | |
""" | |
if datetime_obj is None: | |
datetime_obj = datetime.datetime.utcnow() | |
return datetime_obj.date().isoformat() | |
def convert_local_dt_to_utc_dt(local_dt): | |
"""Convert local `datetime.datetime` instance to UTC. | |
:param local_dt: Local `datetime.datetime` instance. | |
:type local_dt: datetime.datetime | |
:return: UTC `datetime.datetime` instance. | |
:rtype: datetime.datetime | |
""" | |
if time.daylight: | |
offset = time.altzone / 3600 | |
else: | |
offset = time.timezone / 3600 | |
return local_dt + datetime.timedelta(hours=offset) | |
def datetime_utc_to_timezone_traverse(obj, path=None, callback=None, convert_to_tz=None): | |
"""Recursively looks for a key in a dictionary and returns the value, if found. | |
convert_to_tz='US/Pacific' | |
:param obj: Dictionary to iterate through. | |
:return: Result or None. | |
:rtype: str, unicode, int, float, long, or NoneType | |
""" | |
# TODO: Fix this docstring. | |
if path is None: | |
path = [] | |
if isinstance(obj, dict): | |
value = {k: datetime_utc_to_timezone_traverse(v, path + [k], callback, convert_to_tz) for k, v in obj.items()} | |
elif isinstance(obj, (list, set)): | |
value = [datetime_utc_to_timezone_traverse(elem, path + [[]], callback, convert_to_tz) for elem in obj] | |
elif isinstance(obj, (datetime.datetime, datetime.time)): | |
# value = obj.replace(tzinfo=pytz.utc) | |
# TODO: Add checking to see if a timezone is already set then localize if not else obj.astimezone(...) | |
# value = pytz.utc.localize(obj).astimezone(pytz.timezone(convert_to_tz)) | |
value = obj.astimezone(pytz.timezone(convert_to_tz)) | |
else: | |
value = obj | |
if callback is None: # if a callback is provided, call it to get the new value | |
return value | |
else: | |
return callback(path, value) | |
def timestamp_to_str_traverse(obj, path=None, callback=None): | |
"""Recursively looks for datetime objects and converts them to ISO formatted strings. | |
:param obj: Dictionary to iterate through. | |
:param key: Key to look for. | |
:return: Result or None. | |
:rtype: str, unicode, int, float, long, or NoneType | |
""" | |
# TODO: Fix this docstring. | |
if path is None: | |
path = [] | |
if isinstance(obj, dict): | |
value = {k: timestamp_to_str_traverse(v, path + [k], callback) for k, v in obj.items()} | |
elif isinstance(obj, (list, set)): | |
value = [timestamp_to_str_traverse(elem, path + [[]], callback) for elem in obj] | |
else: | |
if isinstance(obj, (datetime.datetime, datetime.date, datetime.time)): | |
obj.isoformat() | |
else: | |
value = obj | |
if callback is None: # if a callback is provided, call it to get the new value | |
return value | |
else: | |
return callback(path, value) | |
# Thanks ZenDesk -- Convert "2016-10-10T14:21:54Z" to datetime object. | |
import re | |
regex_timestamp_zendesk = re.compile('^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}[A-Za-z]+$') | |
regex_timestamp_iso = re.compile('^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}$') | |
regex_timestamp_iso_tz = re.compile('^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\.{0,1}[0-9]*[+\-][0-9]{2}:?[0-9]{2}$') | |
regex_timestamp_ms = re.compile('\.\d+') | |
def timestamp_to_datetime_traverse(obj, path=None, callback=None, convert_to_tz=None): | |
"""Recursively looks for a key in a dictionary and returns the value, if found. | |
convert_to_tz='US/Pacific' | |
:param obj: Dictionary to iterate through. | |
:param key: Key to look for. | |
:return: Result or None. | |
:rtype: str, unicode, int, float, long, or NoneType | |
""" | |
# TODO: Fix this docstring. | |
if path is None: | |
path = [] | |
if isinstance(obj, dict): | |
value = {k: timestamp_to_datetime_traverse(v, path + [k], callback, convert_to_tz) for k, v in obj.items()} | |
elif isinstance(obj, (list, set)): | |
value = [timestamp_to_datetime_traverse(elem, path + [[]], callback, convert_to_tz) for elem in obj] | |
else: | |
if isinstance(obj, (str, unicode)): | |
# Convert '2017-10-04T21:09:43.717000Z' to '2017-10-04T21:09:43Z' | |
if '.' in obj and obj.endswith('Z'): | |
obj_split = obj.split('.') | |
obj = '{}Z'.format(obj_split[0]) | |
if regex_timestamp_zendesk.match(obj): | |
value = pytz.utc.localize(datetime.datetime.strptime(obj, '%Y-%m-%dT%H:%M:%SZ')) | |
if convert_to_tz: | |
value = value.astimezone(pytz.timezone(convert_to_tz)) | |
elif regex_timestamp_iso.match(obj): | |
value = pytz.utc.localize(datetime.datetime.strptime(obj, '%Y-%m-%dT%H:%M:%S')) | |
if convert_to_tz: | |
value = value.astimezone(pytz.timezone(convert_to_tz)) | |
elif regex_timestamp_iso_tz.match(obj): | |
# The Python 2 strptime() function indeed does not the support %z format for timezones | |
# (because the underlying time.strptime() function doesn't support it) so skip it. | |
# TODO: Add support for determining the timezone just in-case some wise-ass isn't using UTC... | |
if obj.endswith('00:00'): | |
value = pytz.utc.localize(datetime.datetime.strptime(obj[:19], '%Y-%m-%dT%H:%M:%S')) | |
if convert_to_tz: | |
value = value.astimezone(pytz.timezone(convert_to_tz)) | |
elif obj.endswith(':00'): | |
# obj = re.sub('\.\d+', '', obj) | |
obj = regex_timestamp_ms.sub('', obj) | |
# THIS SHOULD REMEDY THE TO-DO FROM ABOVE... | |
ret = datetime.datetime.strptime(obj[0:18], '%Y-%m-%dT%H:%M:%S') | |
if obj[19] == '+': | |
ret -= datetime.timedelta(hours=int(obj[20:22]), minutes=int(obj[23:])) | |
elif obj[19] == '-': | |
ret += datetime.timedelta(hours=int(obj[20:22]), minutes=int(obj[23:])) | |
value = pytz.utc.localize(ret) | |
if convert_to_tz: | |
value = value.astimezone(pytz.timezone(convert_to_tz)) | |
else: | |
value = pytz.utc.localize(datetime.datetime.strptime(obj, '%Y-%m-%dT%H:%M:%SZ')) | |
if convert_to_tz: | |
value = value.astimezone(pytz.timezone(convert_to_tz)) | |
else: | |
value = obj | |
else: | |
value = obj | |
if callback is None: # if a callback is provided, call it to get the new value | |
return value | |
else: | |
return callback(path, value) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment