+def now_pst() -> datetime.datetime:
+ return replace_timezone(now(), pytz.timezone("US/Pacific"))
+
+
+def date_to_datetime(date: datetime.date) -> datetime.datetime:
+ return datetime.datetime(
+ date.year,
+ date.month,
+ date.day,
+ 0, 0, 0, 0
+ )
+
+
+def date_and_time_to_datetime(date: datetime.date,
+ time: datetime.time) -> datetime.datetime:
+ return datetime.datetime(
+ date.year,
+ date.month,
+ date.day,
+ time.hour,
+ time.minute,
+ time.second,
+ time.millisecond
+ )
+
+
+def datetime_to_date_and_time(
+ dt: datetime.datetime
+) -> Tuple[datetime.date, datetime.time]:
+ return (dt.date(), dt.timetz())
+
+
+def datetime_to_date(dt: datetime.datetime) -> datetime.date:
+ return datetime_to_date_and_time(dt)[0]
+
+
+def datetime_to_time(dt: datetime.datetime) -> datetime.time:
+ return datetime_to_date_and_time(dt)[1]
+
+
+# An enum to represent units with which we can compute deltas.
+class TimeUnit(enum.Enum):
+ MONDAYS = 0
+ TUESDAYS = 1
+ WEDNESDAYS = 2
+ THURSDAYS = 3
+ FRIDAYS = 4
+ SATURDAYS = 5
+ SUNDAYS = 6
+ SECONDS = 7
+ MINUTES = 8
+ HOURS = 9
+ DAYS = 10
+ WORKDAYS = 11
+ WEEKS = 12
+ MONTHS = 13
+ YEARS = 14
+
+ @classmethod
+ def is_valid(cls, value: Any):
+ if type(value) is int:
+ return value in cls._value2member_map_
+ elif type(value) is TimeUnit:
+ return value.value in cls._value2member_map_
+ elif type(value) is str:
+ return value in cls._member_names_
+ else:
+ print(type(value))
+ return False
+
+
+def n_timeunits_from_base(
+ count: int,
+ unit: TimeUnit,
+ base: datetime.datetime
+) -> datetime.datetime:
+ assert TimeUnit.is_valid(unit)
+ if count == 0:
+ return base
+
+ # N days from base
+ if unit == TimeUnit.DAYS:
+ timedelta = datetime.timedelta(days=count)
+ return base + timedelta
+
+ # N workdays from base
+ elif unit == TimeUnit.WORKDAYS:
+ if count < 0:
+ count = abs(count)
+ timedelta = datetime.timedelta(days=-1)
+ else:
+ timedelta = datetime.timedelta(days=1)
+ skips = holidays.US(years=base.year).keys()
+ while count > 0:
+ old_year = base.year
+ base += timedelta
+ if base.year != old_year:
+ skips = holidays.US(years=base.year).keys()
+ if (
+ base.weekday() < 5 and
+ datetime.date(base.year,
+ base.month,
+ base.day) not in skips
+ ):
+ count -= 1
+ return base
+
+ # N weeks from base
+ elif unit == TimeUnit.WEEKS:
+ timedelta = datetime.timedelta(weeks=count)
+ base = base + timedelta
+ return base
+
+ # N months from base
+ elif unit == TimeUnit.MONTHS:
+ month_term = count % 12
+ year_term = count // 12
+ new_month = base.month + month_term
+ if new_month > 12:
+ new_month %= 12
+ year_term += 1
+ new_year = base.year + year_term
+ return datetime.datetime(
+ new_year,
+ new_month,
+ base.day,
+ base.hour,
+ base.minute,
+ base.second,
+ base.microsecond,
+ )
+
+ # N years from base
+ elif unit == TimeUnit.YEARS:
+ new_year = base.year + count
+ return datetime.datetime(
+ new_year,
+ base.month,
+ base.day,
+ base.hour,
+ base.minute,
+ base.second,
+ base.microsecond,
+ )
+
+ # N weekdays from base (e.g. 4 wednesdays from today)
+ direction = 1 if count > 0 else -1
+ count = abs(count)
+ timedelta = datetime.timedelta(days=direction)
+ start = base
+ while True:
+ dow = base.weekday()
+ if dow == unit and start != base:
+ count -= 1
+ if count == 0:
+ return base
+ base = base + timedelta
+
+
+def get_format_string(
+ *,
+ date_time_separator=" ",
+ include_timezone=True,
+ include_dayname=False,
+ use_month_abbrevs=False,
+ include_seconds=True,
+ include_fractional=False,
+ twelve_hour=True,