Functional Programming with datetime, and Omni-Timezone Discord Timestamps
I define various useful datetime utilities with the help of fastcore's L and map, and functools' partial. Then I extend that to generate Discord timestamps, which localize Unix timestamps to the reader's timezone.
from datetime import datetime
from fastcore.utils import *
from functools import partial
from zoneinfo import ZoneInfo
Overview
I've been finding it helpful to use datetime with fastcore's L and map, and functools' partial.
I shared some useful functions here to print my colleagues' current times, and generate Discord timestamps which localize Unix timestamps to the user's timezone.
Converting the Current Time
We start off by doing things the classic way using Python stdlib datetime, which is actually quite good.
datetime.now()
datetime.datetime(2025, 2, 3, 10, 42, 15, 930074)
now()
can take a timezone:
dt = datetime.now(tz=ZoneInfo('Europe/London'))
dt
datetime.datetime(2025, 2, 3, 10, 42, 16, 763220, tzinfo=zoneinfo.ZoneInfo(key='Europe/London'))
nyc = dt.astimezone(ZoneInfo('America/New_York'))
nyc
datetime.datetime(2025, 2, 3, 5, 42, 16, 763220, tzinfo=zoneinfo.ZoneInfo(key='America/New_York'))
Let's make a list of timezones:
tzs = L('America/Los_Angeles', 'America/Chicago', 'America/New_York', 'Europe/London', 'Europe/Istanbul', 'Australia/Brisbane',)
tzs
(#6) ['America/Los_Angeles','America/Chicago','America/New_York','Europe/London','Europe/Istanbul','Australia/Brisbane']
Now we map that timezone list to a "time in that timezone" function:
def time_in_tz(tz:str, dt:datetime|None=None) -> str:
if dt is None: dt = datetime.now()
dt = dt.astimezone(ZoneInfo(tz))
return f"{tz:20} {dt:%H:%M}"
tzs.map(time_in_tz).map(print)
America/Los_Angeles 02:42
America/Chicago 04:42
America/New_York 05:42
Europe/London 10:42
Europe/Istanbul 13:42
Australia/Brisbane 20:42
(#6) [None,None,None,None,None,None]
Converting a Future Time With Timezone
To get a time in the future for a particular timezone:
tomorrow_3pm_est = datetime.now(ZoneInfo('America/New_York')).replace(hour=15, minute=0, second=0, microsecond=0) + timedelta(days=1)
This also works and is more readable:
datetime.fromisoformat('2025-01-30 15:00').replace(tzinfo=ZoneInfo('America/New_York'))
datetime.datetime(2025, 1, 30, 15, 0, tzinfo=zoneinfo.ZoneInfo(key='America/New_York'))
current_times = partial(time_in_tz, dt=tomorrow_3pm_est)
tzs.map(current_times).map(print)
America/Los_Angeles 12:00
America/Chicago 14:00
America/New_York 15:00
Europe/London 20:00
Europe/Istanbul 23:00
Australia/Brisbane 06:00
(#6) [None,None,None,None,None,None]
Adding Discord Timestamp
A Discord timestamp looks like <t:1738094460:F> where:
- 1738094460 is a Unix timestamp
- F is long date/time like "Tuesday, January 28, 2025 2:20 PM"
When you put one of those into a Discord message, it automatically shows in each user's local timezone.
f"<t:{int(dt.timestamp())}:f>"
'<t:1738579336:f>'
def print_discord_time(dt:datetime|None=None) -> None:
if dt is None: dt = datetime.now()
print(f"{"Discord":20} <t:{int(dt.timestamp())}:f>")
print_discord_time()
Discord <t:1738579349:f>
print_discord_time(tomorrow_3pm_est)
Discord <t:1738699200:f>
Printing It All
def print_times(dt:datetime|None=None) -> None:
if dt is None: dt = datetime.now()
tzs = L('America/Los_Angeles', 'America/Chicago', 'America/New_York', 'Europe/London', 'Europe/Istanbul', 'Australia/Brisbane')
tzs.map(time_in_tz).map(print)
print_discord_time(dt)
print_times()
America/Los_Angeles 02:42
America/Chicago 04:42
America/New_York 05:42
Europe/London 10:42
Europe/Istanbul 13:42
Australia/Brisbane 20:42
Discord <t:1738579355:f>
Next Steps
Create a Time Converter tool: a FastTag I can use to convert time whenever I need to.