3 """Utilities related to dates and times and datetimes."""
8 from typing import NewType
14 logger = logging.getLogger(__name__)
17 def now_pst() -> datetime.datetime:
18 return datetime.datetime.now(tz=pytz.timezone("US/Pacific"))
21 def now() -> datetime.datetime:
22 return datetime.datetime.now()
25 def datetime_to_string(
26 dt: datetime.datetime,
28 date_time_separator=" ",
29 include_timezone=True,
30 include_dayname=False,
32 include_fractional=False,
35 """A nice way to convert a datetime into a string."""
39 fstring = f"%Y/%b/%d{date_time_separator}"
49 if include_fractional:
53 return dt.strftime(fstring).strip()
56 def timestamp() -> str:
57 """Return a timestamp for now in Pacific timezone."""
58 ts = datetime.datetime.now(tz=pytz.timezone("US/Pacific"))
59 return datetime_to_string(ts, include_timezone=True)
63 dt: datetime.datetime,
66 include_fractional=False,
67 include_timezone=False,
70 """A nice way to convert a datetime into a time (only) string."""
81 if include_fractional:
85 return dt.strftime(fstring).strip()
88 def seconds_to_timedelta(seconds: int) -> datetime.timedelta:
89 """Convert a delta in seconds into a timedelta."""
90 return datetime.timedelta(seconds=seconds)
93 MinuteOfDay = NewType("MinuteOfDay", int)
96 def minute_number(hour: int, minute: int) -> MinuteOfDay:
97 """Convert hour:minute into minute number from start of day."""
98 return MinuteOfDay(hour * 60 + minute)
101 def datetime_to_minute_number(dt: datetime.datetime) -> MinuteOfDay:
102 """Convert a datetime into a minute number (of the day)"""
103 return minute_number(dt.hour, dt.minute)
106 def minute_number_to_time_string(minute_num: MinuteOfDay) -> str:
107 """Convert minute number from start of day into hour:minute am/pm string."""
108 hour = minute_num // 60
109 minute = minute_num % 60
118 return f"{hour:2}:{minute:02}{ampm}"
121 def parse_duration(duration: str) -> int:
122 """Parse a duration in string form."""
124 m = re.search(r'(\d+) *d[ays]*', duration)
126 seconds += int(m.group(1)) * 60 * 60 * 24
127 m = re.search(r'(\d+) *h[ours]*', duration)
129 seconds += int(m.group(1)) * 60 * 60
130 m = re.search(r'(\d+) *m[inutes]*', duration)
132 seconds += int(m.group(1)) * 60
133 m = re.search(r'(\d+) *s[econds]*', duration)
135 seconds += int(m.group(1))
139 def describe_duration(age: int) -> str:
140 """Describe a duration."""
141 days = divmod(age, constants.SECONDS_PER_DAY)
142 hours = divmod(days[1], constants.SECONDS_PER_HOUR)
143 minutes = divmod(hours[1], constants.SECONDS_PER_MINUTE)
147 descr = f"{int(days[0])} days, "
151 descr = descr + f"{int(hours[0])} hours, "
153 descr = descr + "1 hour, "
155 descr = descr + "and "
157 descr = descr + "1 minute"
159 descr = descr + f"{int(minutes[0])} minutes"
163 def describe_duration_briefly(age: int) -> str:
164 """Describe a duration briefly."""
165 days = divmod(age, constants.SECONDS_PER_DAY)
166 hours = divmod(days[1], constants.SECONDS_PER_HOUR)
167 minutes = divmod(hours[1], constants.SECONDS_PER_MINUTE)
171 descr = f"{int(days[0])}d "
173 descr = descr + f"{int(hours[0])}h "
174 descr = descr + f"{int(minutes[0])}m"