You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

907 lines
68 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import calendar
import datetime
import time
from typing import Any, Callable, Dict, Optional, TypeVar, Union
import tzlocal
from dateutil.relativedelta import relativedelta
from dateutil.tz import tz
from .isoweek import ISOWeek
from .month import Month
from .quarter import Quarter
from .week import Week
T = TypeVar('T')
TimeAnyT = Union[datetime.datetime, datetime.date, str, bytes]
TimeAnyNT = Union[datetime.datetime, datetime.date, str, bytes, None]
class TimeConvertTools(object):
def __get_base_time_zone(self):
if hasattr(tzlocal, 'get_localzone_name'):
return tzlocal.get_localzone_name()
tz_localzone = tzlocal.get_localzone()
if hasattr(tz_localzone, 'unwrap_shim'):
tz_localzone = tz_localzone.unwrap_shim()
return tz_localzone.key if hasattr(tz_localzone, 'key') else tz_localzone.zone
def __init__(self, timezone: Optional[str] = None, format: Optional[str] = None):
self.BASE_TIME_ZONE = self.__get_base_time_zone()
self.DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S'
self.DATETIME_ISOFORMAT = '%Y-%m-%dT%H:%M:%S.%f'
self.DATE_FORMAT = '%Y-%m-%d'
# %U - Week number of the year (Sunday as the first day of the week) as a zero-padded decimal number. All days in a new year preceding the first Sunday are considered to be in week 0.
# %W - Week number of the year (Monday as the first day of the week) as a zero-padded decimal number. All days in a new year preceding the first Monday are considered to be in week 0.
# %Y - Year with century as a decimal number.
# %V - ISO 8601 week as a decimal number with Monday as the first day of the week. Week 01 is the week containing Jan 4.
# %G - ISO 8601 year with century representing the year that contains the greater part of the ISO week (%V).
self.WEEK_FORMAT = '%W'
self.WEEK_FORMAT_U = '%U'
self.WEEK_FORMAT_W = '%W'
self.WEEK_FORMAT_ISO = 'ISO'
self.MODE_WEEK_FORMAT = {
0: self.WEEK_FORMAT_U,
3: self.WEEK_FORMAT_ISO,
5: self.WEEK_FORMAT_W,
}
self.YEARWEEK_FORMAT = '%YW%W'
self.YEARWEEK_FORMAT_U = '%YW%U'
self.YEARWEEK_FORMAT_W = '%YW%W'
self.YEARWEEK_FORMAT_ISO = 'ISO'
self.MODE_yearweek_FORMAT = {
0: self.YEARWEEK_FORMAT_U,
3: self.YEARWEEK_FORMAT_ISO,
5: self.YEARWEEK_FORMAT_W,
}
self.LEN_FORMAT = {
19: self.DATETIME_FORMAT,
10: self.DATE_FORMAT,
}
self.TIME_ZONE = timezone or self.BASE_TIME_ZONE
self.TIME_FORMAT = format or self.DATETIME_FORMAT
self.TIME_ISOFORMAT = format or self.DATETIME_ISOFORMAT
self.SECOND_MILLISECOND = 10 ** 3
self.SECOND_MICROSECOND = 10 ** 6
def timezone(self, timezone: Optional[str] = None) -> str:
# In [1]: import pytz
# In [2]: pytz.all_timezones
return timezone or self.TIME_ZONE
def format(self, format: Optional[str] = None) -> str:
return format or self.TIME_FORMAT
def isoformat(self, format: Optional[str] = None) -> str:
return format or self.TIME_ISOFORMAT
def date_format(self, format: Optional[str] = None) -> str:
return format or self.DATE_FORMAT
def value_format(self, value: Optional[str] = None, format: Optional[str] = None) -> str:
if format:
return format
return self.LEN_FORMAT.get(len(value)) if isinstance(value, (str, bytes)) else format
def tzinfo(self, timezone: Optional[str] = None, tzname: str = None):
# tzname = self.timezone(timezone)
# tzinfo = tz.gettz(tzname)
# return tzinfo
return tz.gettz(tzname or self.timezone(timezone))
# PRIVATE
def __utc_datetime(self, utc_dt: Optional[datetime.datetime] = None, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0) -> datetime.datetime:
return self.several_time_coming(dt=utc_dt or self.basic_utc_datetime(), utc=True, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks)
def __local_datetime(self, local_dt: Optional[datetime.datetime] = None, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0) -> datetime.datetime:
return self.several_time_coming(dt=local_dt or self.basic_local_datetime(), utc=False, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks)
def __datetime(self, dt: Optional[datetime.datetime] = None, utc: Optional[bool] = True, timezone: Optional[str] = None) -> datetime.datetime:
return dt or (self.basic_utc_datetime() if utc else self.basic_local_datetime(timezone=timezone))
def __relativedelta(self, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0):
return relativedelta(years=years, months=months) + datetime.timedelta(days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks)
def __remove_ms_or_not(self, dt: datetime.datetime, ms: Optional[bool] = True) -> datetime.datetime:
return dt if ms else self.remove_microsecond(dt)
def __seconds_to_other(self, s: int, base: int = 0) -> int:
return int(s * (base or self.SECOND_MICROSECOND))
# OFFSET
def offset(self) -> int:
now_timestamp = time.time()
return datetime.datetime.fromtimestamp(now_timestamp) - datetime.datetime.utcfromtimestamp(now_timestamp)
# VALIDATE
def validate_string(self, string: str, format: Optional[str] = None) -> bool:
if not string:
return False
try:
time.strptime(string, self.format(format))
except ValueError:
return False
return True
# REPLACE
def remove_microsecond(self, dt: datetime.datetime) -> datetime.datetime:
return dt.replace(microsecond=0) if hasattr(dt, 'replace') else dt
# BASIC DATETIME
def basic_utc_datetime(self, ms: bool = True) -> datetime.datetime:
return self.__remove_ms_or_not(datetime.datetime.utcnow().replace(tzinfo=tz.UTC), ms=ms)
def basic_local_datetime(self, ms: bool = True, timezone: Optional[str] = None) -> datetime.datetime:
# In[1]: import time
#
# In[2]: time.localtime()
# Out[2]: time.struct_time(tm_year=2017, tm_mon=4, tm_mday=3, tm_hour=23, tm_min=19, tm_sec=39, tm_wday=0, tm_yday=93, tm_isdst=0)
#
# In[3]: time.localtime(time.time())
# Out[3]: time.struct_time(tm_year=2017, tm_mon=4, tm_mday=3, tm_hour=23, tm_min=19, tm_sec=40, tm_wday=0, tm_yday=93, tm_isdst=0)
#
# In[4]: time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
# Out[4]: '2017-04-03 23:19:57'
#
# In[5]: time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
# Out[5]: '2017-04-03 23:20:00'
return self.__remove_ms_or_not(self.__to_local_datetime(self.basic_utc_datetime(), self.timezone(timezone)), ms=ms)
def is_utc_datetime(self, dt: datetime.datetime) -> bool:
return dt.tzinfo == tz.UTC
def is_local_datetime(self, dt: datetime.datetime, local_tz: Optional[str] = None) -> bool:
"""
Check whether local datetime or not.
``local_tz`` indicates local tzinfo.
``local_dt`` is ``None``: get_localzone from system.
``local_dt`` is ``-1``: local tzinfo is ``None``.
"""
# In [1]: pytz.timezone('Asia/Shanghai')
# Out[1]: <DstTzInfo 'Asia/Shanghai' LMT+8:06:00 STD>
# In [2]: tc.local_datetime().tzinfo
# Out[2]: <DstTzInfo 'Asia/Shanghai' CST+8:00:00 STD>
# In [3]: pytz.timezone('Asia/Shanghai') == tc.local_datetime().tzinfo
# Out[3]: False
# In [4]: str(pytz.timezone('Asia/Shanghai')) == str(tc.local_datetime().tzinfo)
# Out[4]: True
return str(dt.tzinfo) == str(None if local_tz == -1 else tz.gettz(self.timezone(local_tz)))
def date_to_datetime(self, dt: datetime.date) -> datetime.datetime:
return datetime.datetime(dt.year, dt.month, dt.day)
def __to_utc_datetime(self, dt: datetime.datetime, timezone: Optional[str] = None) -> datetime.datetime:
if self.is_utc_datetime(dt):
return dt
try:
dt = self.make_naive(dt)
except ValueError:
pass
tzinfo = self.tzinfo(timezone)
local_dt = dt.replace(tzinfo=tzinfo)
return local_dt.astimezone(tz.UTC)
def __to_local_datetime(self, dt: datetime.datetime, timezone: Optional[str] = None) -> datetime.datetime:
tzname = self.timezone(timezone)
tzinfo = self.tzinfo(tzname=tzname)
if not dt.tzinfo:
return dt.replace(tzinfo=tzinfo)
if self.is_local_datetime(dt, local_tz=tzname):
return dt
utc_dt = dt.replace(tzinfo=tz.UTC)
return utc_dt.astimezone(tzinfo)
def to_datetime(self, value: TimeAnyT, timezone: Optional[str] = None, format: Optional[str] = None, idx: int = 0, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0, dttype: Optional[str] = None) -> Optional[datetime.date]:
if isinstance(value, datetime.datetime):
dt = value
elif isinstance(value, datetime.date):
dt = self.date_to_datetime(value)
elif isinstance(value, (str, bytes)):
dt = self.string_to_datetime(value, format)
else:
return None
if dttype == 'utc':
dt = self.__to_utc_datetime(dt, timezone=timezone)
elif dttype == 'local':
dt = self.__to_local_datetime(dt, timezone=timezone)
return dt + self.__relativedelta(years=years, months=months, days=days or idx, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks)
def to_utc_datetime(self, value: TimeAnyT, timezone: Optional[str] = None, format: Optional[str] = None, idx: int = 0, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0) -> datetime.datetime:
return self.to_datetime(value, timezone=timezone, format=format, idx=idx, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks, dttype='utc')
def to_local_datetime(self, value: TimeAnyT, timezone: Optional[str] = None, format: Optional[str] = None, idx: int = 0, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0) -> datetime.datetime:
return self.to_datetime(value, timezone=timezone, format=format, idx=idx, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks, dttype='local')
# DATETIME
def yesterday_utc_datetime(self, ms: bool = True) -> datetime.datetime:
return self.__remove_ms_or_not(self.several_days_ago(days=1), ms=ms)
def tomorrow_utc_datetime(self, ms: bool = True) -> datetime.datetime:
return self.__remove_ms_or_not(self.several_days_coming(days=1), ms=ms)
def yesterday_local_datetime(self, ms: bool = True, timezone: Optional[str] = None) -> datetime.datetime:
return self.__remove_ms_or_not(self.several_days_ago(utc=False, timezone=timezone, days=1), ms=ms)
def tomorrow_local_datetime(self, ms: bool = True, timezone: Optional[str] = None) -> datetime.datetime:
return self.__remove_ms_or_not(self.several_days_coming(utc=False, timezone=timezone, days=1), ms=ms)
def several_days_ago(self, dt: Optional[datetime.datetime] = None, utc: bool = True, ms: bool = True, timezone: Optional[str] = None, days: int = 0) -> datetime.datetime:
return self.__remove_ms_or_not(self.__datetime(dt, utc, timezone=timezone) - datetime.timedelta(days=days), ms=ms)
def several_days_coming(self, dt: Optional[datetime.datetime] = None, utc: bool = True, ms: bool = True, timezone: Optional[str] = None, days: int = 0) -> datetime.datetime:
return self.__remove_ms_or_not(self.__datetime(dt, utc, timezone=timezone) + datetime.timedelta(days=days), ms=ms)
def several_time_ago(self, dt: Optional[datetime.datetime] = None, utc: bool = True, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0) -> datetime.datetime:
return self.__remove_ms_or_not(self.__datetime(dt, utc, timezone=timezone) - self.__relativedelta(years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks), ms=ms)
def several_time_coming(self, dt: Optional[datetime.datetime] = None, utc: bool = True, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0) -> datetime.datetime:
return self.__remove_ms_or_not(self.__datetime(dt, utc, timezone=timezone) + self.__relativedelta(years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks), ms=ms)
def utc_datetime(self, value: TimeAnyNT = None, format: Optional[str] = None, utc: bool = True, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0, dt: TimeAnyNT = None) -> datetime.datetime:
value = value or dt
if isinstance(value, (str, bytes)):
value = self.utc_string_to_utc_datetime(value, format)
return self.several_time_coming(dt=value, utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks)
def local_datetime(self, value: TimeAnyNT = None, format: Optional[str] = None, utc: bool = False, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0, dt: TimeAnyNT = None) -> datetime.datetime:
value = value or dt
if isinstance(value, (str, bytes)):
value = self.string_to_local_datetime(value, format)
return self.several_time_coming(dt=value, utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks)
# DATE
def utc_date(self, value: TimeAnyNT = None, format: Optional[str] = None, utc: bool = True, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0) -> datetime.date:
if isinstance(value, (str, bytes)):
value = self.utc_string_to_utc_datetime(value, format)
return self.to_date(self.utc_datetime(value=value, utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks))
def local_date(self, value: TimeAnyNT = None, format: Optional[str] = None, utc: bool = False, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0) -> datetime.date:
if isinstance(value, (str, bytes)):
value = self.string_to_local_datetime(value, format)
return self.to_date(self.local_datetime(value=value, utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks))
def datetime_to_date(self, dt: datetime.datetime) -> datetime.date:
return dt.date()
def to_date(self, value: TimeAnyT, format: Optional[str] = None, idx: int = 0, years: int = 0, months: int = 0, days: int = 0, weeks: int = 0) -> Optional[datetime.date]:
if isinstance(value, datetime.datetime):
date = value.date()
elif isinstance(value, datetime.date):
date = value
elif isinstance(value, (str, bytes)):
date = self.string_to_date(value, format)
else:
return None
return date + self.__relativedelta(years=years, months=months, days=days or idx, weeks=weeks)
# def is_the_same_day(self, dt1: datetime.date, dt2: datetime.date) -> bool:
# return self.local_string(dt1, format=self.DATE_FORMAT) == self.local_string(dt2, format=self.DATE_FORMAT)
def is_the_same_day(self, value1: TimeAnyT, value2: TimeAnyT, format: Optional[str] = None, format1: Optional[str] = None, format2: Optional[str] = None) -> bool:
return self.to_date(value1, format=format1 or format) == self.to_date(value2, format=format2 or format)
# YEAR WEEK
def utc_yearweek(self, value: Union[datetime.datetime, datetime.date, None] = None, utc: bool = True, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0, local_dt: Optional[datetime.datetime] = None, utc_dt: Optional[datetime.datetime] = None, isuc: bool = False, mode: int = 3) -> Union[Week, ISOWeek]:
yearweek_format = self.MODE_yearweek_FORMAT.get(mode, self.YEARWEEK_FORMAT_ISO)
if yearweek_format != self.YEARWEEK_FORMAT_ISO:
return Week.fromstring(self.utc_string(value=value, format=yearweek_format, utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks, local_dt=local_dt, utc_dt=utc_dt, isuc=isuc))
return ISOWeek.withdate(self.utc_date(value, utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks))
def utc_isoyearweek(self, value: Union[datetime.datetime, datetime.date, None] = None, utc: bool = True, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0, local_dt: Optional[datetime.datetime] = None, utc_dt: Optional[datetime.datetime] = None, isuc: bool = False) -> ISOWeek:
return self.utc_yearweek(value, utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks, local_dt=local_dt, utc_dt=utc_dt, isuc=isuc, mode=3)
def local_yearweek(self, value: Union[datetime.datetime, datetime.date, None] = None, utc: bool = False, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0, local_dt: Optional[datetime.datetime] = None, utc_dt: Optional[datetime.datetime] = None, isuc: bool = False, mode: int = 3) -> Union[Week, ISOWeek]:
yearweek_format = self.MODE_yearweek_FORMAT.get(mode, self.YEARWEEK_FORMAT_ISO)
if yearweek_format != self.YEARWEEK_FORMAT_ISO:
return Week.fromstring(self.local_string(value=value, format=yearweek_format, utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks, local_dt=local_dt, utc_dt=utc_dt, isuc=isuc))
return ISOWeek.withdate(self.local_date(value, utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks))
def local_isoyearweek(self, value: Union[datetime.datetime, datetime.date, None] = None, utc: bool = False, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0, local_dt: Optional[datetime.datetime] = None, utc_dt: Optional[datetime.datetime] = None, isuc: bool = False) -> ISOWeek:
return self.local_yearweek(value, utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks, local_dt=local_dt, utc_dt=utc_dt, isuc=isuc, mode=3)
# WEEK
def utc_week(self, value: Union[datetime.datetime, datetime.date, None] = None, utc: bool = True, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0, local_dt: Optional[datetime.datetime] = None, utc_dt: Optional[datetime.datetime] = None, isuc: bool = False, mode: int = 3) -> int:
# week_format = self.MODE_WEEK_FORMAT.get(mode, self.WEEK_FORMAT_ISO)
# if week_format != self.WEEK_FORMAT_ISO:
# return int(self.utc_string(value=value, format=week_format, utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks, local_dt=local_dt, utc_dt=utc_dt, isuc=isuc))
# return ISOWeek.withdate(self.utc_date(value, utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks)).week
return self.utc_yearweek(value, utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks, local_dt=local_dt, utc_dt=utc_dt, isuc=isuc, mode=mode).week
def utc_isoweek(self, value: Union[datetime.datetime, datetime.date, None] = None, utc: bool = True, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0, local_dt: Optional[datetime.datetime] = None, utc_dt: Optional[datetime.datetime] = None, isuc: bool = False) -> int:
return self.utc_week(value, utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks, local_dt=local_dt, utc_dt=utc_dt, isuc=isuc, mode=3)
def local_week(self, value: Union[datetime.datetime, datetime.date, None] = None, utc: bool = False, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0, local_dt: Optional[datetime.datetime] = None, utc_dt: Optional[datetime.datetime] = None, isuc: bool = False, mode: int = 3) -> int:
# week_format = self.MODE_WEEK_FORMAT.get(mode, self.WEEK_FORMAT_ISO)
# if week_format != self.WEEK_FORMAT_ISO:
# return int(self.local_string(value=value, format=week_format, utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks, local_dt=local_dt, utc_dt=utc_dt, isuc=isuc))
# return ISOWeek.withdate(self.local_date(value, utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks)).week
return self.local_yearweek(value, utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks, local_dt=local_dt, utc_dt=utc_dt, isuc=isuc, mode=mode).week
def local_isoweek(self, value: Union[datetime.datetime, datetime.date, None] = None, utc: bool = False, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0, local_dt: Optional[datetime.datetime] = None, utc_dt: Optional[datetime.datetime] = None, isuc: bool = False) -> int:
return self.local_week(value, utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks, local_dt=local_dt, utc_dt=utc_dt, isuc=isuc, mode=3)
def to_week(self, value: TimeAnyT, idx: int = 0, mode: int = 3, format: Optional[str] = None) -> Optional[ISOWeek]:
date = self.to_date(value, format=format)
if not date:
return None
if mode != 3:
raise ValueError('to_week only support mode equals 3 nowadays')
return ISOWeek.withdate(date) + idx
def to_isoweek(self, value: TimeAnyT, idx: int = 0, format: Optional[str] = None) -> Optional[ISOWeek]:
return self.to_week(value, idx=idx, mode=3, format=format)
def weekdelta(self, value1: TimeAnyT, value2: TimeAnyT, mode: int = 3) -> int:
return self.to_week(value1, mode=mode) - self.to_week(value2, mode=mode)
def isoweekdelta(self, value1: TimeAnyT, value2: TimeAnyT) -> int:
return self.to_isoweek(value1) - self.to_isoweek(value2)
# STRING
# DATETIME_STRING
def datetime_to_unicode_string(self, dt: datetime.datetime, format: Optional[str] = None) -> str:
format = self.format(format)
# Refer: https://github.com/sphinx-doc/sphinx/blob/8ae43b9fd/sphinx/util/osutil.py#L164
# On Windows, time.strftime() and Unicode characters will raise UnicodeEncodeError.
# http://bugs.python.org/issue8304
try:
return dt.strftime(format)
except UnicodeEncodeError:
return dt.strftime(format.encode('unicode-escape').decode()).encode().decode('unicode-escape')
def datetime_to_string(self, dt: datetime.datetime, format: Optional[str] = None, isuc: bool = False) -> str:
if isuc:
return self.datetime_to_unicode_string(dt, format=format)
return dt.strftime(self.format(format))
def yesterday_utc_string(self, format: Optional[str] = None, ms: bool = True, isuc: bool = False) -> str:
return self.datetime_to_string(self.yesterday_utc_datetime(ms=ms), self.format(format), isuc=isuc)
def tomorrow_utc_string(self, format: Optional[str] = None, ms: bool = True, isuc: bool = False) -> str:
return self.datetime_to_string(self.tomorrow_utc_datetime(ms=ms), self.format(format), isuc=isuc)
def yesterday_local_string(self, format: Optional[str] = None, ms: bool = True, timezone: Optional[str] = None, isuc: bool = False) -> str:
return self.datetime_to_string(self.yesterday_local_datetime(ms=ms, timezone=timezone), self.format(format), isuc=isuc)
def tomorrow_local_string(self, format: Optional[str] = None, ms: bool = True, timezone: Optional[str] = None, isuc: bool = False) -> str:
return self.datetime_to_string(self.tomorrow_local_datetime(ms=ms, timezone=timezone), self.format(format), isuc=isuc)
def several_days_ago_string(self, dt: Optional[datetime.datetime] = None, format: Optional[str] = None, utc: bool = True, ms: bool = True, timezone: Optional[str] = None, days: int = 0, isuc: bool = False) -> str:
return self.datetime_to_string(self.several_days_ago(dt=dt, utc=utc, ms=ms, timezone=timezone, days=days), self.format(format), isuc=isuc)
def several_days_coming_string(self, dt: Optional[datetime.datetime] = None, format: Optional[str] = None, utc: bool = True, ms: bool = True, timezone: Optional[str] = None, days: int = 0, isuc: bool = False) -> str:
return self.datetime_to_string(self.several_days_coming(dt=dt, utc=utc, ms=ms, timezone=timezone, days=days), self.format(format), isuc=isuc)
def several_time_ago_string(self, dt: Optional[datetime.datetime] = None, format: Optional[str] = None, utc: bool = True, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0, isuc: bool = False) -> str:
return self.datetime_to_string(self.several_time_ago(dt=dt, utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks), self.format(format), isuc=isuc)
def several_time_coming_string(self, dt: Optional[datetime.datetime] = None, format: Optional[str] = None, utc: bool = True, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0, isuc: bool = False) -> str:
return self.datetime_to_string(self.several_time_coming(dt=dt, utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks), self.format(format), isuc=isuc)
def utc_string(self, value: TimeAnyNT = None, format: Optional[str] = None, utc: bool = True, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0, local_dt: Optional[datetime.datetime] = None, utc_dt: Optional[datetime.datetime] = None, isuc: bool = False, dt: TimeAnyNT = None, value_format: Optional[str] = None) -> str:
final_dt = value or dt or utc_dt
final_dt = self.utc_datetime(value=local_dt) if not final_dt and local_dt else final_dt
if isinstance(final_dt, (str, bytes)):
final_dt = self.utc_string_to_utc_datetime(final_dt, value_format)
return self.datetime_to_string(self.utc_datetime(value=final_dt, utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks), self.format(format), isuc=isuc)
def local_string(self, value: TimeAnyNT = None, format: Optional[str] = None, utc: bool = False, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0, local_dt: Optional[datetime.datetime] = None, utc_dt: Optional[datetime.datetime] = None, isuc: bool = False, dt: TimeAnyNT = None, value_format: Optional[str] = None) -> str:
final_dt = value or dt or local_dt
final_dt = self.__to_local_datetime(dt=utc_dt) if not final_dt and utc_dt else final_dt
if isinstance(final_dt, (str, bytes)):
final_dt = self.string_to_local_datetime(final_dt, value_format)
return self.datetime_to_string(self.local_datetime(value=final_dt, utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks), self.format(format), isuc=isuc)
def utc_datetime_string(self, value: TimeAnyNT = None, format: Optional[str] = None, utc: bool = True, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0, local_dt: Optional[datetime.datetime] = None, utc_dt: Optional[datetime.datetime] = None, isuc: bool = False, dt: TimeAnyNT = None, value_format: Optional[str] = None) -> str:
return self.utc_string(value=value or dt, format=self.format(format), utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks, local_dt=local_dt, utc_dt=utc_dt, isuc=isuc, value_format=value_format)
def local_datetime_string(self, value: TimeAnyNT = None, format: Optional[str] = None, utc: bool = False, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0, local_dt: Optional[datetime.datetime] = None, utc_dt: Optional[datetime.datetime] = None, isuc: bool = False, dt: TimeAnyNT = None, value_format: Optional[str] = None) -> str:
return self.local_string(value=value or dt, format=self.format(format), utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks, local_dt=local_dt, utc_dt=utc_dt, isuc=isuc, value_format=value_format)
def utc_isostring(self, value: TimeAnyNT = None, format: Optional[str] = None, utc: bool = True, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0, local_dt: Optional[datetime.datetime] = None, utc_dt: Optional[datetime.datetime] = None, isuc: bool = False, dt: TimeAnyNT = None, value_format: Optional[str] = None) -> str:
return self.utc_string(value=value or dt, format=self.isoformat(format), utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks, local_dt=local_dt, utc_dt=utc_dt, isuc=isuc, value_format=value_format)
def local_isostring(self, value: TimeAnyNT = None, format: Optional[str] = None, utc: bool = False, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0, local_dt: Optional[datetime.datetime] = None, utc_dt: Optional[datetime.datetime] = None, isuc: bool = False, dt: TimeAnyNT = None, value_format: Optional[str] = None) -> str:
return self.local_string(value=value or dt, format=self.isoformat(format), utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks, local_dt=local_dt, utc_dt=utc_dt, isuc=isuc, value_format=value_format)
def utc_datetime_isostring(self, value: TimeAnyNT = None, format: Optional[str] = None, utc: bool = True, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0, local_dt: Optional[datetime.datetime] = None, utc_dt: Optional[datetime.datetime] = None, isuc: bool = False, dt: TimeAnyNT = None, value_format: Optional[str] = None) -> str:
return self.utc_string(value=value or dt, format=self.isoformat(format), utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks, local_dt=local_dt, utc_dt=utc_dt, isuc=isuc, value_format=value_format)
def local_datetime_isostring(self, value: TimeAnyNT = None, format: Optional[str] = None, utc: bool = False, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0, local_dt: Optional[datetime.datetime] = None, utc_dt: Optional[datetime.datetime] = None, isuc: bool = False, dt: TimeAnyNT = None, value_format: Optional[str] = None) -> str:
return self.local_string(value=value or dt, format=self.isoformat(format), utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks, local_dt=local_dt, utc_dt=utc_dt, isuc=isuc, value_format=value_format)
# DATE_STRING
def utc_date_string(self, value: TimeAnyNT = None, format: Optional[str] = None, utc: bool = True, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0, local_dt: Optional[datetime.datetime] = None, utc_dt: Optional[datetime.datetime] = None, isuc: bool = False, dt: TimeAnyNT = None, value_format: Optional[str] = None) -> str:
return self.utc_string(value=value or dt, format=self.date_format(format), utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks, local_dt=local_dt, utc_dt=utc_dt, isuc=isuc, value_format=self.value_format(value or dt, value_format))
def local_date_string(self, value: TimeAnyNT = None, format: Optional[str] = None, utc: bool = False, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0, local_dt: Optional[datetime.datetime] = None, utc_dt: Optional[datetime.datetime] = None, isuc: bool = False, dt: TimeAnyNT = None, value_format: Optional[str] = None) -> str:
return self.local_string(value=value or dt, format=self.date_format(format), utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks, local_dt=local_dt, utc_dt=utc_dt, isuc=isuc, value_format=self.value_format(value or dt, value_format))
# WEEK_STRING
def utc_yearweek_string(self, dt: Optional[datetime.datetime] = None, utc: bool = True, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0, local_dt: Optional[datetime.datetime] = None, utc_dt: Optional[datetime.datetime] = None, isuc: bool = False, mode: int = 3) -> str:
return str(self.utc_yearweek(dt, utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks, local_dt=local_dt, utc_dt=utc_dt, isuc=isuc, mode=mode))
def local_yearweek_string(self, dt: Optional[datetime.datetime] = None, utc: bool = False, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0, local_dt: Optional[datetime.datetime] = None, utc_dt: Optional[datetime.datetime] = None, isuc: bool = False, mode: int = 3) -> str:
return str(self.local_yearweek(dt, utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks, local_dt=local_dt, utc_dt=utc_dt, isuc=isuc, mode=mode))
def utc_isoyearweek_string(self, dt: Optional[datetime.datetime] = None, utc: bool = True, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0, local_dt: Optional[datetime.datetime] = None, utc_dt: Optional[datetime.datetime] = None, isuc: bool = False) -> str:
return str(self.utc_isoyearweek(dt, utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks, local_dt=local_dt, utc_dt=utc_dt, isuc=isuc))
def local_isoyearweek_string(self, dt: Optional[datetime.datetime] = None, utc: bool = False, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0, local_dt: Optional[datetime.datetime] = None, utc_dt: Optional[datetime.datetime] = None, isuc: bool = False) -> str:
return str(self.local_isoyearweek(dt, utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks, local_dt=local_dt, utc_dt=utc_dt, isuc=isuc))
# WEEK_STRING
def utc_week_string(self, dt: Optional[datetime.datetime] = None, utc: bool = True, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0, local_dt: Optional[datetime.datetime] = None, utc_dt: Optional[datetime.datetime] = None, isuc: bool = False, mode: int = 3) -> str:
return str(self.utc_week(dt, utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks, local_dt=local_dt, utc_dt=utc_dt, isuc=isuc, mode=mode))
def local_week_string(self, dt: Optional[datetime.datetime] = None, utc: bool = False, ms: bool = True, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0, local_dt: Optional[datetime.datetime] = None, utc_dt: Optional[datetime.datetime] = None, isuc: bool = False, mode: int = 3) -> str:
return str(self.local_week(dt, utc=utc, ms=ms, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks, local_dt=local_dt, utc_dt=utc_dt, isuc=isuc, mode=mode))
# TIMESTAMP
def __micro_or_milli(self, s, micro: bool = False, milli: bool = False) -> int:
if micro:
return self.seconds_to_microseconds(s)
if milli:
return self.seconds_to_milliseconds(s)
return s
def utc_timestamp(self, utc_dt: Optional[datetime.datetime] = None, ms: bool = False, micro: bool = False, milli: bool = False, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0) -> int:
return self.__micro_or_milli(self.datetime_to_timestamp(self.__utc_datetime(utc_dt, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks), ms=ms), micro=micro, milli=milli)
def local_timestamp(self, local_dt: Optional[datetime.datetime] = None, ms: bool = False, micro: bool = False, milli: bool = False, timezone: Optional[str] = None, years: int = 0, months: int = 0, days: int = 0, seconds: int = 0, microseconds: int = 0, milliseconds: int = 0, minutes: int = 0, hours: int = 0, weeks: int = 0) -> int:
return self.__micro_or_milli(self.datetime_to_timestamp(self.__local_datetime(local_dt, timezone=timezone, years=years, months=months, days=days, seconds=seconds, microseconds=microseconds, milliseconds=milliseconds, minutes=minutes, hours=hours, weeks=weeks), ms=ms), micro=micro, milli=milli)
def datetime_to_timestamp(self, dt: Union[datetime.datetime, datetime.date], ms: bool = False) -> int:
# http://stackoverflow.com/questions/26161156/python-converting-string-to-timestamp-with-microseconds
# ``dt - epoch`` will raise ``TypeError: can't subtract offset-naive and offset-aware datetimes``
# Total seconds from ``1970-01-01 00:00:00``(utc or local)
# Different from definition of timestamp(时间戳是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总秒数)
stamp = self.structime_to_timestamp(dt.timetuple())
if not ms:
return stamp
return stamp + dt.microsecond / self.SECOND_MICROSECOND
def date_to_timestamp(self, dt: Union[datetime.datetime, datetime.date], ms: bool = False) -> int:
return self.datetime_to_timestamp(dt, ms=ms)
def structime_to_timestamp(self, structime: time.struct_time) -> int:
return int(time.mktime(structime))
def seconds_to_microseconds(self, s: int) -> int:
return self.__seconds_to_other(s, base=self.SECOND_MICROSECOND)
def seconds_to_milliseconds(self, s: int) -> int:
return self.__seconds_to_other(s, base=self.SECOND_MILLISECOND)
# STRING ==> DATE
def string_to_date(self, string: str, format: Optional[str] = None) -> Optional[datetime.date]:
format = self.value_format(string, format)
if not self.validate_string(string, format):
return None
return self.string_to_datetime(string, format).date()
def string_to_utc_date(self, string: str, format: Optional[str] = None) -> Optional[datetime.date]:
format = self.value_format(string, format)
if not self.validate_string(string, format):
return None
return self.string_to_utc_datetime(string, format).date()
def string_to_local_date(self, string: str, format: Optional[str] = None) -> Optional[datetime.date]:
format = self.value_format(string, format)
if not self.validate_string(string, format):
return None
return self.string_to_local_datetime(string, format).date()
def utc_string_to_utc_date(self, utc_string: str, format: Optional[str] = None) -> Optional[datetime.date]:
format = self.value_format(utc_string, format)
if not self.validate_string(utc_string, format):
return None
return self.utc_string_to_utc_datetime(utc_string, format).date()
def utc_string_to_local_date(self, utc_string: str, format: Optional[str] = None) -> Optional[datetime.date]:
format = self.value_format(utc_string, format)
if not self.validate_string(utc_string, format):
return None
return self.utc_string_to_local_datetime(utc_string, format).date()
# STRING ==> DATETIME
def string_to_datetime(self, string: str, format: Optional[str] = None) -> Optional[datetime.datetime]:
format = self.value_format(string, format)
if not self.validate_string(string, format):
return None
return datetime.datetime.strptime(string, format)
def string_to_utc_datetime(self, string: str, format: Optional[str] = None) -> Optional[datetime.datetime]:
format = self.value_format(string, format)
if not self.validate_string(string, format):
return None
return self.__to_utc_datetime(self.string_to_datetime(string, format))
def string_to_local_datetime(self, string: str, format: Optional[str] = None) -> Optional[datetime.datetime]:
format = self.value_format(string, format)
if not self.validate_string(string, format):
return None
return self.__to_local_datetime(self.string_to_datetime(string, format))
def utc_string_to_utc_datetime(self, utc_string: str, format: Optional[str] = None) -> Optional[datetime.datetime]:
format = self.value_format(utc_string, format)
if not self.validate_string(utc_string, format):
return None
return self.__to_utc_datetime(self.string_to_datetime(utc_string, format)) + self.offset()
def utc_string_to_local_datetime(self, utc_string: str, format: Optional[str] = None) -> Optional[datetime.datetime]:
format = self.value_format(utc_string, format)
if not self.validate_string(utc_string, format):
return None
return self.__to_local_datetime(self.string_to_datetime(utc_string, format)) + self.offset()
# STRING ==> TIMESTAMP
def string_to_timestamp(self, string: str, format: Optional[str] = None, ms: bool = False) -> Optional[int]:
return self.string_to_local_timestamp(string, format, ms=ms)
def string_to_utc_timestamp(self, string: str, format: Optional[str] = None, ms: bool = False) -> Optional[int]:
format = self.format(format)
if not self.validate_string(string, format):
return None
return self.datetime_to_timestamp(self.string_to_utc_datetime(string, format), ms=ms)
def string_to_local_timestamp(self, string: str, format: Optional[str] = None, ms: bool = False) -> Optional[int]:
format = self.format(format)
if not self.validate_string(string, format):
return None
return self.datetime_to_timestamp(self.string_to_local_datetime(string, format), ms=ms)
# TIMESTAMP ==> DATETIME
# local_stamp => utc_datetime - fromtimestamp + to_utc_datetime / utcfromtimestamp
# local_stamp => local_datetime - fromtimestamp
# utc_stamp => utc_datetime - fromtimestamp
# utc_stamp => local_datetime - fromtimestamp + to_local_datetime
def timestamp_to_datetime(self, stamp: int) -> datetime.datetime:
return datetime.datetime.fromtimestamp(stamp)
def timestamp_to_utc_datetime(self, stamp: int) -> datetime.datetime:
# return datetime.datetime.utcfromtimestamp(stamp)
return self.__to_utc_datetime(self.timestamp_to_datetime(stamp))
def timestamp_to_local_datetime(self, stamp: int) -> datetime.datetime:
return self.timestamp_to_datetime(stamp)
def utc_timestamp_to_utc_datetime(self, stamp: int) -> datetime.datetime:
# return self.make_aware(self.timestamp_to_datetime(stamp), timezone='UTC')
return self.make_aware(self.timestamp_to_datetime(stamp), timezone=self.timezone('UTC'))
def utc_timestamp_to_local_datetime(self, stamp: int) -> datetime.datetime:
return self.__to_local_datetime(self.timestamp_to_datetime(stamp))
# TIMESTAMP ==> AGE
# TIME_DELTA
def timestamp_delta(self, stamp1: int, stamp2: int, interval: Optional[int] = None) -> Dict[str, Any]:
delta = stamp1 - stamp2
abs_delta = abs(delta)
sign = abs_delta and delta // abs_delta
delta_seconds = abs_delta % 60
delta_minutes = abs_delta // 60 % 60
delta_hours = abs_delta // 3600 % 24
delta_days = abs_delta // 86400
delta_weeks = abs_delta // 604800
return {
'sign': sign,
'weeks': delta_weeks,
'days': delta_days,
'hours': delta_hours,
'minutes': delta_minutes,
'seconds': delta_seconds,
'total_seconds': abs_delta,
'delta': delta,
'count_down_seconds': abs(min(delta, 0)),
'interval': interval and abs_delta >= interval
}
def datetime_delta(self, dt1: Union[datetime.datetime, datetime.date], dt2: Union[datetime.datetime, datetime.date], interval: Optional[int] = None) -> Optional[Dict[str, Any]]:
return self.timestamp_delta(self.datetime_to_timestamp(dt1), self.datetime_to_timestamp(dt2), interval)
def date_delta(self, dt1: Union[datetime.datetime, datetime.date], dt2: Union[datetime.datetime, datetime.date], interval: Optional[int] = None) -> Optional[Dict[str, Any]]:
return self.datetime_delta(dt1, dt2, interval=interval)
def string_delta(self, string1: str, string2: str, interval: Optional[int] = None, format: Optional[str] = None, format1: Optional[str] = None, format2: Optional[str] = None) -> Optional[Dict[str, Any]]:
format = self.format(format)
if (not self.validate_string(string1, format1 or format)) or (not self.validate_string(string2, format2 or format)):
return None
return self.timestamp_delta(self.string_to_timestamp(string1, format1 or format), self.string_to_timestamp(string2, format2 or format), interval)
def delta(self, value1: TimeAnyT, value2: TimeAnyT, interval: Optional[int] = None, format: Optional[str] = None, format1: Optional[str] = None, format2: Optional[str] = None) -> Optional[Dict[str, Any]]:
if isinstance(value1, datetime.datetime):
value1 = self.datetime_to_timestamp(value1)
elif isinstance(value1, datetime.date):
value1 = self.date_to_timestamp(value1)
elif isinstance(value1, (str, bytes)):
value1 = self.string_to_timestamp(value1, format1 or format)
if isinstance(value2, datetime.datetime):
value2 = self.datetime_to_timestamp(value2)
elif isinstance(value2, datetime.date):
value2 = self.date_to_timestamp(value2)
elif isinstance(value2, (str, bytes)):
value2 = self.string_to_timestamp(value2, format2 or format)
return self.timestamp_delta(value1, value2, interval=interval)
# TIME_COUNT_DOWN
def timestamp_countdown(self, stamp: int, utc: bool = True) -> int:
return abs(min((self.utc_timestamp() if utc else self.local_timestamp()) - stamp, 0))
def datetime_countdown(self, dt: datetime.datetime) -> int:
return self.timestamp_countdown(self.datetime_to_timestamp(self.__to_utc_datetime(dt)))
def string_countdown(self, string: str, format: Optional[str] = None) -> Optional[int]:
format = self.format(format)
if not self.validate_string(string, format):
return None
return self.timestamp_countdown(self.string_to_utc_timestamp(string, format))
# MIDNIGHT
def utc_datetime_midnight(self, utc_dt: Optional[datetime.datetime] = None) -> datetime.datetime:
return (self.__utc_datetime(utc_dt)).replace(hour=0, minute=0, second=0, microsecond=0)
def utc_seconds_since_midnight(self, utc_dt: Optional[datetime.datetime] = None, seconds_cast_func: Callable[[Any], T] = float) -> T:
utc_dt = self.__utc_datetime(utc_dt)
return seconds_cast_func(self.total_seconds(utc_dt - self.utc_datetime_midnight(utc_dt)))
def local_datetime_midnight(self, local_dt: Optional[datetime.datetime] = None) -> datetime.datetime:
return (self.__local_datetime(local_dt)).replace(hour=0, minute=0, second=0, microsecond=0)
def local_seconds_since_midnight(self, local_dt: Optional[datetime.datetime] = None, seconds_cast_func: Callable[[Any], T] = float) -> T:
local_dt = self.__local_datetime(local_dt)
return seconds_cast_func(self.total_seconds(local_dt - self.local_datetime_midnight(local_dt)))
def datetime_midnight(self, dt: Optional[datetime.datetime] = None, utc: bool = False) -> datetime.datetime:
return self.utc_datetime_midnight(dt) if utc else self.local_datetime_midnight(dt)
def seconds_since_midnight(self, dt: Optional[datetime.datetime] = None, utc: bool = False, seconds_cast_func: Callable[[Any], T] = float) -> T:
return seconds_cast_func(self.utc_seconds_since_midnight(dt) if utc else self.local_seconds_since_midnight(dt))
def seconds_until_midnight(self, dt: Optional[datetime.datetime] = None, utc: bool = False, seconds_cast_func: Callable[[Any], T] = float) -> T:
return seconds_cast_func(86400 - self.seconds_since_midnight(dt=dt, utc=utc))
# AWARE vs. NAIVE
# By design, these four functions don't perform any checks on their arguments.
# The caller should ensure that they don't receive an invalid value like None.
def is_aware(self, value: datetime.datetime) -> bool:
"""
Determine if a given datetime.datetime is aware.
The concept is defined in Python's docs:
https://docs.python.org/library/datetime.html#datetime.tzinfo
Assuming value.tzinfo is either None or a proper datetime.tzinfo,
value.utcoffset() implements the appropriate logic.
"""
return value.utcoffset() is not None
def is_naive(self, value: datetime.datetime) -> bool:
"""
Determine if a given datetime.datetime is naive.
The concept is defined in Python's docs:
https://docs.python.org/library/datetime.html#datetime.tzinfo
Assuming value.tzinfo is either None or a proper datetime.tzinfo,
value.utcoffset() implements the appropriate logic.
"""
return value.utcoffset() is None
def make_aware(self, value: datetime.datetime, timezone: Optional[str] = None) -> datetime.datetime:
"""Make a naive datetime.datetime in a given time zone aware."""
tzinfo = self.tzinfo(timezone)
# Check that we won't overwrite the timezone of an aware datetime.
if self.is_aware(value):
raise ValueError('make_aware expects a naive datetime, got %s' % value)
# This may be wrong around DST changes!
return value.replace(tzinfo=tzinfo)
def make_naive(self, value: datetime.datetime, timezone: Optional[str] = None) -> datetime.datetime:
"""Make an aware datetime.datetime naive in a given time zone."""
tzinfo = self.tzinfo(timezone)
# Emulate the behavior of astimezone() on Python < 3.6.
if self.is_naive(value):
raise ValueError('make_naive() cannot be applied to a naive datetime')
return value.astimezone(tzinfo).replace(tzinfo=None)
# PAST vs. FUTURE
def is_past_time(self, value: Union[str, int, datetime.datetime], base_dt: Optional[datetime.datetime] = None, format: Optional[str] = None, utc: bool = True) -> Optional[bool]:
base_dt = base_dt or self.basic_utc_datetime()
if not value:
return None
if isinstance(value, datetime.datetime):
return (value if utc else self.__to_local_datetime(value)) < base_dt
if isinstance(value, (str, bytes)):
utc_dt = self.utc_string_to_utc_datetime(value, format=format) if utc else self.string_to_utc_datetime(value, format=format)
return utc_dt and utc_dt < base_dt
if isinstance(value, int):
stamp = self.datetime_to_timestamp(base_dt if utc else self.__to_local_datetime(base_dt), ms=True)
return value < stamp
return None
def is_future_time(self, value: Union[str, int, datetime.datetime], base_dt: Optional[datetime.datetime] = None, format: Optional[str] = None, utc: bool = True) -> Optional[bool]:
base_dt = base_dt or self.basic_utc_datetime()
if not value:
return None
if isinstance(value, datetime.datetime):
return (value if utc else self.__to_local_datetime(value)) > base_dt
if isinstance(value, (str, bytes)):
utc_dt = self.utc_string_to_utc_datetime(value, format=format) if utc else self.string_to_utc_datetime(value, format=format)
return utc_dt and utc_dt > base_dt
if isinstance(value, int):
stamp = self.datetime_to_timestamp(base_dt if utc else self.__to_local_datetime(base_dt), ms=True)
return value > stamp
return None
# YEAR/MONTH/DAY
def year(self, dt: Optional[datetime.datetime] = None, utc: bool = False, timezone: Optional[str] = None, idx: int = 0) -> int:
return self.__datetime(dt=self.several_time_coming(dt=dt, utc=utc, timezone=timezone, years=idx), utc=utc).year
def month(self, dt: Optional[datetime.datetime] = None, utc: bool = False, timezone: Optional[str] = None, idx: int = 0) -> int:
return self.__datetime(dt=self.several_time_coming(dt=dt, utc=utc, timezone=timezone, months=idx), utc=utc).month
def day(self, dt: Optional[datetime.datetime] = None, utc: bool = False, timezone: Optional[str] = None, idx: int = 0) -> int:
return self.__datetime(dt=self.several_time_coming(dt=dt, utc=utc, timezone=timezone, days=idx), utc=utc).day
def days_of_year(self, year: Optional[int] = None, dt: Optional[datetime.datetime] = None, idx: int = 0) -> int:
return 366 if calendar.isleap(year or self.year(dt, idx=idx)) else 365
def days_of_month(self, year: Optional[int] = None, month: Optional[int] = None, dt: Optional[datetime.datetime] = None, idx: int = 0) -> int:
return calendar.monthrange(year=(year or self.year(dt, idx=idx)), month=(month or self.month(dt, idx=idx)))[-1]
# OTHER
def total_seconds(self, td: datetime.timedelta, ms: bool = True) -> int:
"""Total seconds in the duration."""
if not ms:
return td.days * 86400 + td.seconds
return ((td.days * 86400 + td.seconds) * self.SECOND_MICROSECOND + td.microseconds) / self.SECOND_MICROSECOND
def date_range(self, start_date: Union[str, datetime.date], end_date: Union[str, datetime.date], include_end: bool = False, format: Optional[str] = None, start_date_format: Optional[str] = None, end_date_format: Optional[str] = None, return_type: str = 'date', return_format: Optional[str] = None) -> Callable:
if isinstance(start_date, str):
start_date = self.string_to_date(start_date, start_date_format or format or self.DATE_FORMAT)
if isinstance(end_date, str):
end_date = self.string_to_date(end_date, end_date_format or format or self.DATE_FORMAT)
if include_end:
end_date = end_date + datetime.timedelta(1)
if return_type in ['string', 'str']:
for n in range(int((end_date - start_date).days)):
yield self.datetime_to_string(start_date + datetime.timedelta(n), return_format or format or self.DATE_FORMAT)
else:
for n in range(int((end_date - start_date).days)):
yield start_date + datetime.timedelta(n)
def week_range(self, start_date: Union[str, datetime.date], end_date: Union[str, datetime.date], format: Optional[str] = None, start_date_format: Optional[str] = None, end_date_format: Optional[str] = None, return_type: str = 'isoweek', return_format: Optional[str] = None) -> Callable:
if isinstance(start_date, str):
start_date = self.string_to_date(start_date, start_date_format or format or self.DATE_FORMAT)
if isinstance(end_date, str):
end_date = self.string_to_date(end_date, end_date_format or format or self.DATE_FORMAT)
start_week = ISOWeek.withdate(start_date)
end_week = ISOWeek.withdate(end_date)
if return_type in ['string', 'str']:
for n in range(int(end_week - start_week) + 1):
current_week = start_week + n
yield {
'week': current_week.isoformat(),
'start': self.datetime_to_string(current_week.monday(), return_format or format or self.DATE_FORMAT),
'end': self.datetime_to_string(current_week.sunday(), return_format or format or self.DATE_FORMAT),
}
else:
for n in range(int(end_week - start_week) + 1):
yield start_week + n
def month_range(self, start_date: Union[str, datetime.date], end_date: Union[str, datetime.date], format: Optional[str] = None, start_date_format: Optional[str] = None, end_date_format: Optional[str] = None, return_type: str = 'date', return_format: Optional[str] = None) -> Callable:
if isinstance(start_date, str):
start_date = self.string_to_date(start_date, start_date_format or format or self.DATE_FORMAT)
if isinstance(end_date, str):
end_date = self.string_to_date(end_date, end_date_format or format or self.DATE_FORMAT)
start_month = Month.from_date(start_date)
end_month = Month.from_date(end_date)
if return_type in ['string', 'str']:
for n in range(int(end_month - start_month) + 1):
current_month = start_month + n
yield {
'month': str(current_month),
'start': self.datetime_to_string(current_month.start_date, return_format or format or self.DATE_FORMAT),
'end': self.datetime_to_string(current_month.end_date, return_format or format or self.DATE_FORMAT),
}
else:
for n in range(int(end_month - start_month) + 1):
yield start_month + n
def quarter_range(self, start_date: Union[str, datetime.date], end_date: Union[str, datetime.date], format: Optional[str] = None, start_date_format: Optional[str] = None, end_date_format: Optional[str] = None, return_type: str = 'date', return_format: Optional[str] = None) -> Callable:
if isinstance(start_date, str):
start_date = self.string_to_date(start_date, start_date_format or format or self.DATE_FORMAT)
if isinstance(end_date, str):
end_date = self.string_to_date(end_date, end_date_format or format or self.DATE_FORMAT)
start_quarter = Quarter.from_date(start_date)
end_quarter = Quarter.from_date(end_date)
if return_type in ['string', 'str']:
for n in range(int(end_quarter - start_quarter) + 1):
current_quarter = start_quarter + n
yield {
'quarter': current_quarter.isoformat(),
'start': self.datetime_to_string(current_quarter.start_date, return_format or format or self.DATE_FORMAT),
'end': self.datetime_to_string(current_quarter.end_date, return_format or format or self.DATE_FORMAT),
}
else:
for n in range(int(end_quarter - start_quarter) + 1):
yield start_quarter + n
daterange = date_range
weekrange = week_range
monthrange = month_range
quarterrange = quarter_range
def isoweekdaycount(self, start_date: Union[str, datetime.date], end_date: Union[str, datetime.date], isoweekday: int = 7, format: Optional[str] = None, start_date_format: Optional[str] = None, end_date_format: Optional[str] = None) -> int:
if isinstance(start_date, str):
start_date = self.string_to_date(start_date, start_date_format or format or self.DATE_FORMAT)
if isinstance(end_date, str):
end_date = self.string_to_date(end_date, end_date_format or format or self.DATE_FORMAT)
weeks = self.datetime_delta(start_date, end_date).get('weeks')
# datetime.datetime.now().isoweekday() # 返回1-7代表周一到周日当前时间所在本周第几天
# datetime.datetime.now().weekday() # 返回的0-6代表周一到周日
# 标准格式 %w 中1-6表示周一到周六0代表周日
start_isoweekday = start_date.isoweekday()
end_isoweekday = end_date.isoweekday()
if end_isoweekday >= start_isoweekday:
if start_isoweekday <= isoweekday <= end_isoweekday:
weeks += 1
else:
if start_isoweekday <= isoweekday or end_isoweekday >= isoweekday:
weeks += 1
return weeks
def between(self, value: TimeAnyT, start_value: TimeAnyT, end_value: TimeAnyT, timezone: Optional[str] = None, format: Optional[str] = None, start_format: Optional[str] = None, end_format: Optional[str] = None) -> bool:
value = self.to_datetime(value, timezone=timezone, format=format, dttype='utc')
start_value = self.to_datetime(start_value, timezone=timezone, format=format or start_format, dttype='utc')
end_value = self.to_datetime(end_value, timezone=timezone, format=format or end_format, dttype='utc')
if not start_value or not end_value:
raise ValueError('`start_value` and `end_value` should not empty')
if start_value > end_value:
start_value, end_value = end_value, start_value
return start_value <= value <= end_value
TC = tc = TimeConvert = TimeConvertTools()