Source code for pytoolbox.datetime

import datetime, numbers
from calendar import timegm
from time import mktime

import pytz

from . import module

_all = module.All(globals())


[docs]def datetime_now( format='%Y-%m-%d %H:%M:%S', append_utc=False, offset=None, tz=pytz.utc ): # pylint:disable=redefined-builtin """ Return the current (timezone aware) date and time as UTC, local (tz=None) or related to a timezone. If `format` is not None, the date will be returned in a formatted string. :param format: Output date string formatting :type format: str :param append_utc: Append ' UTC' to date string :type append_utc: bool :param offset: Offset to add to current time :type offset: datetime.timedelta :param tz: The timezone (e.g. ``pytz.timezone('EST')``) :type tz: tz **Example usage** Add an offset: >>> now = datetime_now(format=None) >>> future = datetime_now(offset=datetime.timedelta(hours=2, minutes=10), format=None) >>> result = (future - now) >>> type(result) <class 'datetime.timedelta'> >>> print(result) 2:10:00.00... Append UTC to output date string: >>> type(datetime_now()) <class 'str'> >>> assert ' UTC' not in datetime_now(tz=pytz.utc, append_utc=False) >>> assert ' UTC' not in datetime_now(tz=None, append_utc=True) >>> assert ' UTC' not in datetime_now(tz=pytz.timezone('EST'), append_utc=True) >>> assert ' UTC' in datetime_now(tz=pytz.utc, append_utc=True) Play with timezones:: >> datetime_now(tz=pytz.timezone('Europe/Zurich')) '2013-10-17 09:54:08' >> datetime_now(tz=pytz.timezone('US/Eastern')) '2013-10-17 03:54:08' """ now = datetime.datetime.now(tz) if offset is not None: now += offset if not format: return now return now.strftime(format) + (' UTC' if tz == pytz.utc and append_utc else '')
[docs]def datetime_to_str( date_time, format='%Y-%m-%d %H:%M:%S', append_utc=False ): # pylint:disable=redefined-builtin return date_time.strftime(format) + (' UTC' if append_utc else '')
[docs]def str_to_datetime( date, format='%Y-%m-%d %H:%M:%S', fail=True ): # pylint:disable=redefined-builtin """ Return the `date` string converted into an instance of :class:`datetime.datetime`. Handle 24h+ hour format like 2015:06:28 24:05:00 equal to the 28th June 2015 at midnight and 5 minutes. **Example usage** >>> str_to_datetime('1985-01-06 05:02:00') datetime.datetime(1985, 1, 6, 5, 2) >>> str_to_datetime('this is not a date') Traceback (most recent call last): ... ValueError: time data 'this is not a date' does not match format '%Y-%m-%d %H:%M:%S' """ try: return datetime.datetime.strptime(date.replace(': ', ':0').replace(' 24:', ' 00:'), format) except ValueError: if fail and date != '0000:00:00 00:00:00': raise return None
[docs]def multiply_time(value, factor, as_delta=False): """ Return an instance of :class:`datetime.time`/:class:`datetime.timedelta` corresponding to `value` multiplied by a `factor`. **Example usage** >>> multiply_time('00:10:00', 0.5) datetime.time(0, 5) >>> multiply_time(datetime.timedelta(seconds=60), 3) datetime.time(0, 3) >>> multiply_time(120, 0.1) datetime.time(0, 0, 12) >>> res = multiply_time(datetime.timedelta(seconds=152, microseconds=500000), 1, as_delta=True) >>> type(res) <class 'datetime.timedelta'> >>> print(res) 0:02:32.500000 """ return secs_to_time(total_seconds(value) * factor, as_delta=as_delta)
[docs]def parts_to_time(hours, minutes, seconds, microseconds, as_delta=False): """ Return an instance of :class:`datetime.time`/:class:`datetime.timedelta` out of the parts. **Example usage** >>> parts_to_time(23, 15, 7, 3500) datetime.time(23, 15, 7, 3500) >>> result = parts_to_time(23, 15, 7, 3500, as_delta=True) >>> type(result) <class 'datetime.timedelta'> >>> print(result) 23:15:07.003500 """ if as_delta: return datetime.timedelta( hours=hours, minutes=minutes, seconds=seconds, microseconds=microseconds) return datetime.time(hours, minutes, seconds, microseconds)
[docs]def secs_to_time(value, defaults_to_zero=False, as_delta=False): """ Return an instance of :class:`datetime.time`/:class:`datetime.timedelta`, taking `value` as the number of seconds + microseconds (e.g. 10.3 = 10s 3000us). **Example usage** >>> secs_to_time(83707.0035) datetime.time(23, 15, 7, 3500) >>> secs_to_time(None) >>> secs_to_time(None, defaults_to_zero=True) datetime.time(0, 0) >>> result = secs_to_time(83707.0035, as_delta=True) >>> type(result) <class 'datetime.timedelta'> >>> print(result) 23:15:07.003500 >>> secs_to_time(None, as_delta=True) is None True >>> secs_to_time(None, defaults_to_zero=True, as_delta=True) datetime.timedelta(0) """ try: delta = datetime.timedelta(seconds=float(value)) return delta if as_delta else (datetime.datetime.min + delta).time() except (ValueError, TypeError): if defaults_to_zero and not value: return datetime.timedelta(seconds=0) if as_delta else datetime.time(second=0) return None
[docs]def str_to_time(value, defaults_to_zero=False, as_delta=False): """ Return the string of format 'hh:mm:ss' into an instance of time. **Example usage** >>> str_to_time('08:23:57') datetime.time(8, 23, 57) >>> str_to_time('00:03:02.12') datetime.time(0, 3, 2, 120) >>> str_to_time(None) >>> str_to_time(None, defaults_to_zero=True) datetime.time(0, 0) >>> result = str_to_time('08:23:57', as_delta=True) >>> type(result) <class 'datetime.timedelta'> >>> str(result) '8:23:57' >>> result = str_to_time('00:03:02.12', as_delta=True) >>> type(result) <class 'datetime.timedelta'> >>> str(result) '0:03:02.120000' >>> str_to_time(None, as_delta=True) is None True >>> str_to_time(None, defaults_to_zero=True, as_delta=True) datetime.timedelta(0) """ try: hours, minutes, seconds_float = value.split(':') hours, minutes, seconds_float = int(hours), int(minutes), float(seconds_float) if as_delta: return datetime.timedelta(hours=hours, minutes=minutes, seconds=seconds_float) seconds = int(seconds_float) return datetime.time(hours, minutes, seconds, int(1000 * (seconds_float - seconds))) except (AttributeError, TypeError, ValueError): if defaults_to_zero and not value: return datetime.timedelta(seconds=0) if as_delta else datetime.time(second=0) return None
[docs]def time_ratio(numerator, denominator, zero_div_result=1.0): """ Return the ratio between two times. **Example usage** >>> from pytoolbox.unittest import asserts >>> time_ratio('0:30:00', '01:30:00') 0.33... >>> time_ratio('0:00:05', '00:00:00') 1.0 >>> with asserts.raises(ValueError): ... time_ratio('01:42:34', 'N/A') """ try: ratio = total_seconds(numerator) / total_seconds(denominator) return 0.0 if ratio < 0.0 else 1.0 if ratio > 1.0 else ratio except ZeroDivisionError: return zero_div_result
[docs]def total_seconds(time): """ Return the `time` converted in seconds. **Example usage** >>> total_seconds('00:10:00') 600.0 >>> total_seconds('01:54:17') 6857.0 >>> round(total_seconds('16.40'), 3) 16.4 >>> total_seconds(143.2) 143.2 >>> total_seconds(datetime.timedelta(seconds=152, microseconds=500000)) 152.5 >>> total_seconds(datetime.datetime(2010, 6, 10, 0, 1, 30)) 90.0 >>> total_seconds(datetime.datetime(2010, 6, 10, 14, 15, 23)) 51323.0 >>> total_seconds(datetime.datetime(2010, 6, 10, 23, 59, 59)) 86399.0 """ try: if isinstance(time, datetime.timedelta): return time.total_seconds() if isinstance(time, numbers.Number): return time if isinstance(time, str): hours, minutes, seconds = time.split(':') else: hours, minutes, seconds = time.hour, time.minute, time.second return int(hours) * 3600 + int(minutes) * 60 + float(seconds) except ValueError: return float(time)
[docs]def datetime_to_epoch(date_time, utc=True, factor=1): """ Return the :class:`datetime.datetime`/:class:`datetime.date` converted into an Unix epoch. Default `factor` means that the result is in seconds. **Example usage** >>> datetime_to_epoch(datetime.datetime(1970, 1, 1), factor=1) 0 >>> datetime_to_epoch(datetime.datetime(2010, 6, 10)) 1276128000 >>> datetime_to_epoch(datetime.datetime(2010, 6, 10), factor=1000) 1276128000000 >>> datetime_to_epoch(datetime.date(2010, 6, 10), factor=1000) 1276128000000 >>> datetime_to_epoch(datetime.date(1987, 6, 10), factor=1000) 550281600000 In Switzerland:: >> datetime_to_epoch(datetime.datetime(1970, 1, 1), utc=False, factor=1) -3600 >> datetime_to_epoch(datetime.date(1970, 1, 1), utc=False, factor=1) -3600 """ if utc: has_it = hasattr(date_time, 'utctimetuple') time_tuple = date_time.utctimetuple() if has_it else date_time.timetuple() return int(timegm(time_tuple) * factor) return int(mktime(date_time.timetuple()) * factor)
[docs]def epoch_to_datetime(unix_epoch, tz=pytz.utc, factor=1): """ Return the Unix epoch converted to a :class:`datetime.datetime`. Default `factor` means that the `unix_epoch` is in seconds. **Example usage** >>> epoch_to_datetime(0, factor=1) datetime.datetime(1970, 1, 1, 0, 0, tzinfo=<UTC>) >>> epoch_to_datetime(1276128000, factor=1) datetime.datetime(2010, 6, 10, 0, 0, tzinfo=<UTC>) >>> epoch_to_datetime(1276128000, tz=pytz.timezone('Europe/Zurich'), factor=1) datetime.datetime(2010, 6, 10, 2, 0, tzinfo=<DstTzInfo 'Europe/Zurich' CEST+2:00:00 DST>) >>> epoch_to_datetime(1276128000000, factor=1000) datetime.datetime(2010, 6, 10, 0, 0, tzinfo=<UTC>) >>> today = datetime.datetime(1985, 6, 1, 5, 2, 0, tzinfo=pytz.utc) >>> epoch_to_datetime(datetime_to_epoch(today, factor=1000), factor=1000) == today True """ return datetime.datetime.fromtimestamp(unix_epoch / factor, tz)
__all__ = _all.diff(globals())