X-Git-Url: https://wannabe.guru.org/gitweb/?a=blobdiff_plain;f=datetime_utils.py;h=7c5516b594b314aed141b4494e7eed5b820770f3;hb=eedcbd4f64af13ec2098508c3d839a60f7e9ffce;hp=db5b2b5e19ba8e74fdf192dc122e518e90ec6bf9;hpb=83c1e0d04fe2e78963c8b508e8b7d0ae03bfcb16;p=python_utils.git diff --git a/datetime_utils.py b/datetime_utils.py index db5b2b5..7c5516b 100644 --- a/datetime_utils.py +++ b/datetime_utils.py @@ -16,18 +16,87 @@ import constants logger = logging.getLogger(__name__) +def is_timezone_aware(dt: datetime.datetime) -> bool: + """See: https://docs.python.org/3/library/datetime.html + #determining-if-an-object-is-aware-or-naive + + >>> is_timezone_aware(datetime.datetime.now()) + False + + >>> is_timezone_aware(now_pacific()) + True + + """ + return ( + dt.tzinfo is not None and + dt.tzinfo.utcoffset(dt) is not None + ) + + +def is_timezone_naive(dt: datetime.datetime) -> bool: + return not is_timezone_aware(dt) + + def replace_timezone(dt: datetime.datetime, tz: datetime.tzinfo) -> datetime.datetime: """ - Replaces the timezone on a datetime object. + Replaces the timezone on a datetime object directly (leaving + the year, month, day, hour, minute, second, micro, etc... alone). + Note: this changes the instant to which this dt refers. >>> from pytz import UTC >>> d = now_pacific() >>> d.tzinfo.tzname(d)[0] # Note: could be PST or PDT 'P' + >>> h = d.hour >>> o = replace_timezone(d, UTC) >>> o.tzinfo.tzname(o) 'UTC' + >>> o.hour == h + True + + """ + return datetime.datetime( + dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond, + tzinfo=tz + ) + + +def replace_time_timezone(t: datetime.time, + tz: datetime.tzinfo) -> datetime.time: + """ + Replaces the timezone on a datetime.time directly without performing + any translation. + + >>> t = datetime.time(8, 15, 12, 0, pytz.UTC) + >>> t.tzname() + 'UTC' + + >>> t = replace_time_timezone(t, pytz.timezone('US/Pacific')) + >>> t.tzname() + 'US/Pacific' + + """ + return t.replace(tzinfo=tz) + + +def translate_timezone(dt: datetime.datetime, + tz: datetime.tzinfo) -> datetime.datetime: + """ + Translates dt into a different timezone by adjusting the year, month, + day, hour, minute, second, micro, etc... appropriately. The returned + dt is the same instant in another timezone. + + >>> from pytz import UTC + >>> d = now_pacific() + >>> d.tzinfo.tzname(d)[0] # Note: could be PST or PDT + 'P' + >>> h = d.hour + >>> o = translate_timezone(d, UTC) + >>> o.tzinfo.tzname(o) + 'UTC' + >>> o.hour == h + False """ return dt.replace(tzinfo=None).astimezone(tz=tz) @@ -42,9 +111,9 @@ def now() -> datetime.datetime: def now_pacific() -> datetime.datetime: """ - What time is it? Result in US/Pacifit time (PST/PDT) + What time is it? Result in US/Pacific time (PST/PDT) """ - return replace_timezone(now(), pytz.timezone("US/Pacific")) + return datetime.datetime.now(pytz.timezone("US/Pacific")) def date_to_datetime(date: datetime.date) -> datetime.datetime: @@ -64,6 +133,42 @@ def date_to_datetime(date: datetime.date) -> datetime.datetime: ) +def time_to_datetime_today(time: datetime.time) -> datetime.datetime: + """ + Given a time, returns that time as a datetime with a date component + set based on the current date. If the time passed is timezone aware, + the resulting datetime will also be (and will use the same tzinfo). + If the time is timezone naive, the datetime returned will be too. + + >>> t = datetime.time(13, 14, 0) + >>> d = now_pacific().date() + >>> dt = time_to_datetime_today(t) + >>> dt.date() == d + True + + >>> dt.time() == t + True + + >>> dt.tzinfo == t.tzinfo + True + + >>> dt.tzinfo == None + True + + >>> t = datetime.time(8, 15, 12, 0, pytz.UTC) + >>> t.tzinfo == None + False + + >>> dt = time_to_datetime_today(t) + >>> dt.tzinfo == None + False + + """ + now = now_pacific() + tz = time.tzinfo + return datetime.datetime.combine(now, time, tz) + + def date_and_time_to_datetime(date: datetime.date, time: datetime.time) -> datetime.datetime: """ @@ -652,6 +757,18 @@ def describe_duration(seconds: int, *, include_seconds = False) -> str: return descr +def describe_timedelta(delta: datetime.timedelta) -> str: + """ + Describe a duration represented by a timedelta object. + + >>> d = datetime.timedelta(1, 600) + >>> describe_timedelta(d) + '1 day, and 10 minutes' + + """ + return describe_duration(delta.total_seconds()) + + def describe_duration_briefly(seconds: int, *, include_seconds=False) -> str: """ Describe a duration briefly. @@ -685,6 +802,18 @@ def describe_duration_briefly(seconds: int, *, include_seconds=False) -> str: return descr.strip() +def describe_timedelta_briefly(delta: datetime.timedelta) -> str: + """ + Describe a duration represented by a timedelta object. + + >>> d = datetime.timedelta(1, 600) + >>> describe_timedelta_briefly(d) + '1d 10m' + + """ + return describe_duration_briefly(delta.total_seconds()) + + if __name__ == '__main__': import doctest doctest.testmod()