Source code for dccd.tools.date_time

#!/usr/bin/env python3
# coding: utf-8
# @Author: ArthurBernard
# @Email: arthur.bernard.92@gmail.com
# @Date: 2019-02-13 16:53:04
# @Last modified by: ArthurBernard
# @Last modified time: 2019-08-14 18:49:11

""" Tools to manage some time functions with respect to specific API exchanges.

"""

# Built-in packages
import calendar
import datetime
import logging
import time

# Third party packages

# Local packages

_logger = logging.getLogger(__name__)

__all__ = [
    'TS_to_date', 'date_to_TS', 'str_to_span', 'span_to_str',
    'span_label', 'binance_interval',
]

_SPAN_LABEL: dict[int, str] = {
    60: '1m', 180: '3m', 300: '5m', 900: '15m', 1800: '30m',
    3600: '1h', 7200: '2h', 14400: '4h', 21600: '6h', 28800: '8h',
    43200: '12h', 86400: '1d', 604800: '1w',
}


[docs] def TS_to_date(TS: int, form: str = '%Y-%m-%d %H:%M:%S', tz: str = 'local') -> str: """ Convert timestamp to date in specified format. Parameters ---------- TS : int A timestamp to convert. form : str, optional strftime format string. Default ``'%Y-%m-%d %H:%M:%S'``. tz : str, optional Timezone to use. ``'local'`` (default) uses the system timezone, ``'UTC'`` uses UTC, any other value is interpreted as an IANA timezone name (e.g. ``'Europe/Paris'``). Returns ------- str Date formatted according to *form*. Examples -------- >>> TS_to_date(1548432099, form='%y-%m-%d %H:%M:%S', tz='UTC') '19-01-25 16:01:39' """ if tz.upper() == 'LOCAL': return time.strftime(form, time.localtime(TS)) elif tz.upper() == 'UTC': return time.strftime(form, time.gmtime(TS)) else: from zoneinfo import ZoneInfo dt = datetime.datetime.fromtimestamp(TS, tz=ZoneInfo(tz)) return dt.strftime(form)
[docs] def date_to_TS(date: str, form: str = '%Y-%m-%d %H:%M:%S', tz: str = 'local') -> int: """ Convert a date string to a Unix timestamp. Parameters ---------- date : str Date string to convert. form : str, optional strftime format string. Default ``'%Y-%m-%d %H:%M:%S'``. tz : str, optional Timezone used to interpret *date*. ``'local'`` (default) uses the system timezone, ``'UTC'`` treats the string as UTC, any other value is an IANA timezone name (e.g. ``'Europe/Paris'``). Returns ------- int Unix timestamp. Examples -------- >>> date_to_TS('2019-01-25 16:01:39', tz='UTC') 1548432099 """ if form == '%Y-%m-%d %H:%M:%S' and len(date) == 10: form = '%Y-%m-%d' t = time.strptime(date, form) if tz.upper() == 'LOCAL': return int(time.mktime(t)) elif tz.upper() == 'UTC': return int(calendar.timegm(t)) else: from zoneinfo import ZoneInfo dt = datetime.datetime(*t[:6], tzinfo=ZoneInfo(tz)) return int(dt.timestamp())
# def TS_to_YMD(TS): # a = time.strftime('%Y %m %d', time.localtime(int(TS))).split(' ') # return dt.datetime(int(a[0]), int(a[1]), int(a[2]))
[docs] def str_to_span(string: str) -> int | None: """ Return the equivalent interval time in seconds. Parameters ---------- string : str Time periodicity. Accepted values (case-insensitive): ``'monthly'``, ``'1M'``, ``'15d'``, ``'weekly'``, ``'3d'``, ``'daily'``, ``'12h'``, ``'8h'``, ``'6h'``, ``'4h'``, ``'bi-hourly'``, ``'hourly'``, ``'half-hourly'``, ``'quarter-hourly'``, ``'5min'``, ``'3m'``, ``'minutely'``, and common aliases. Returns ------- span : int Number of seconds in time interval. Examples -------- >>> str_to_span('minutely') 60 """ s = string.lower() if s in ['monthly', 'month', '1m']: return 2592000 elif s in ['15-daily', '15-day', '15d']: return 1296000 elif s in ['weekly', 'week', '7d', '1w', 'w']: return 604800 elif s in ['3-daily', '3-day', '3d']: return 259200 elif s in ['daily', 'day', '24h', '1d', 'd']: return 86400 elif s in ['12-hourly', '12-hour', '12h']: return 43200 elif s in ['8-hourly', '8-hour', '8h']: return 28800 elif s in ['6-hourly', '6-hour', '6h']: return 21600 elif s in ['4-hourly', '4-hour', '4h']: return 14400 elif s in ['bi-hourly', 'bi-hour', '2h']: return 7200 elif s in ['hourly', 'hour', '1h', '60min', 'h']: return 3600 elif s in ['half-hourly', 'half-hour', '30min']: return 1800 elif s in ['quarter-hourly', 'quarter-hour', '15min', '15m']: return 900 elif s in ['5-minute', 'five-minute', '5 minute', 'five minute', '5min']: return 300 elif s in ['3-minute', 'three-minute', '3min', '3m']: return 180 elif s in ['minutely', 'minute', '1min', 'min']: return 60 else: _logger.warning( 'Error, string not understood. Expected values such as "minutely", ' '"5min", "15m", "hourly", "4h", "daily", "weekly", "monthly", etc.' ) return None
[docs] def span_to_str(span: int) -> str | None: """ Return the time periodicity label for the given span in seconds. Parameters ---------- span : int Time interval in seconds. Supported values: 60, 180, 300, 900, 1800, 3600, 7200, 14400, 21600, 28800, 43200, 86400, 259200, 604800, 1296000, 2592000. Returns ------- date : str Time periodicity label used for directory naming. Examples -------- >>> span_to_str(3600) 'Hourly' """ _map = { 60: 'Minutely', 180: 'Three_Minutely', 300: 'Five_Minutely', 900: 'Quarter_Hourly', 1800: 'Half_Hourly', 3600: 'Hourly', 7200: 'Bi_Hourly', 14400: 'Four_Hourly', 21600: 'Six_Hourly', 28800: 'Eight_Hourly', 43200: 'Twelve_Hourly', 86400: 'Daily', 259200: 'Three_Daily', 604800: 'Weekly', 1296000: 'Fifteen_Daily', 2592000: 'Monthly', } label = _map.get(span) if label is None: _logger.warning('Error, no string correspond to this time in seconds.') return label
[docs] def span_label(span: int) -> str: """ Return a short directory-safe label for *span* seconds. Parameters ---------- span : int Candle interval in seconds. Returns ------- str Short label (e.g. ``'1m'``, ``'1h'``, ``'1d'``). Falls back to ``'{span}s'`` for unknown spans. Examples -------- >>> span_label(3600) '1h' >>> span_label(86400) '1d' >>> span_label(7777) '7777s' """ return _SPAN_LABEL.get(span, f'{span}s')
[docs] def binance_interval(interval: int) -> str | None: """ Return the time interval in the specific format allowed by Binance. Parameters ---------- interval : int Must be in seconds as 60, 180, 300, 900, 1800, 3600, 7200, 14400, 21600, 28800, 43200, 86400, 259200, 604800, 2592000. Returns ------- form : str Specific format allowed by Binance. Examples -------- >>> binance_interval(7200) '2h' """ if interval / 60 in [1, 3, 5, 15, 30]: return '{}m'.format(int(interval / 60)) elif interval / 3600 in [1, 2, 4, 6, 8, 12]: return '{}h'.format(int(interval / 3600)) elif interval / 86400 in [1, 3]: return '{}d'.format(int(interval / 86400)) elif interval == 604800: return '1w' elif interval == 2592000: return '1M' else: _logger.warning('No format allowed.') return None
if __name__ == '__main__': import doctest doctest.testmod()