Documentation Index
Fetch the complete documentation index at: https://mintlify.com/trustlessmatt/discord-exporter-bot/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The Discord Exporter Bot uses Eastern Time (America/New_York) for all timestamps in exports and digests. This provides consistent, location-aware timestamps with automatic daylight saving time (DST) handling using Python’s zoneinfo module.
Why Eastern Time?
The bot defaults to Eastern Time for several reasons:
- Team location: Designed for teams primarily in US Eastern timezone
- Business hours alignment: Digest generation at midnight ET matches US business context
- DST awareness: Automatically handles EST ↔ EDT transitions
- Consistency: Single timezone for all exports prevents confusion
- Configurable: Can be changed in the
Config class if needed
Timezone Conversion Function
The core conversion function is simple but powerful:
def convert_to_eastern(utc_time: datetime, tz: ZoneInfo) -> datetime:
"""Convert UTC datetime to target timezone (handles DST automatically)."""
if utc_time.tzinfo is None:
utc_time = utc_time.replace(tzinfo=timezone.utc)
return utc_time.astimezone(tz)
How it works:
- Check for timezone info: If the datetime is naive (no timezone), assume UTC
- Add UTC timezone: Attach
timezone.utc to the datetime
- Convert: Use
astimezone() to convert to target timezone
- DST automatic: Python’s
zoneinfo handles DST transitions automatically
Example Usage
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
# Discord provides UTC timestamps
utc_time = datetime(2026, 3, 4, 19, 30, 0, tzinfo=timezone.utc)
# Convert to Eastern Time
eastern_tz = ZoneInfo("America/New_York")
eastern_time = convert_to_eastern(utc_time, eastern_tz)
print(utc_time.isoformat())
# Output: 2026-03-04T19:30:00+00:00
print(eastern_time.isoformat())
# Output: 2026-03-04T14:30:00-05:00 (EST in winter)
# Output: 2026-03-04T15:30:00-04:00 (EDT in summer)
ZoneInfo Usage
The bot uses Python 3.9+ zoneinfo module for IANA timezone database support:
from zoneinfo import ZoneInfo
# In Config class (bot.py:30)
eastern_tz: ZoneInfo = field(default_factory=lambda: ZoneInfo("America/New_York"))
Advantages of ZoneInfo:
- IANA database: Uses official timezone data (same as Linux/macOS)
- DST automatic: Knows when to switch between EST and EDT
- Historical accuracy: Handles timezone rule changes over time
- No external dependencies: Built into Python 3.9+
Supported Timezone Names
# North America
ZoneInfo("America/New_York") # Eastern Time (ET)
ZoneInfo("America/Chicago") # Central Time (CT)
ZoneInfo("America/Denver") # Mountain Time (MT)
ZoneInfo("America/Los_Angeles") # Pacific Time (PT)
# Europe
ZoneInfo("Europe/London") # GMT/BST
ZoneInfo("Europe/Paris") # CET/CEST
# Asia
ZoneInfo("Asia/Tokyo") # JST (no DST)
ZoneInfo("Asia/Kolkata") # IST (no DST)
To change timezone, edit Config class:
# For Pacific Time
eastern_tz: ZoneInfo = field(default_factory=lambda: ZoneInfo("America/Los_Angeles"))
# For UTC (no conversion)
eastern_tz: ZoneInfo = field(default_factory=lambda: ZoneInfo("UTC"))
Automatic DST Handling
Daylight Saving Time transitions happen automatically without any code changes:
EST to EDT Transition (Spring Forward)
# March 9, 2026 at 2:00 AM EST → 3:00 AM EDT
# Before DST (1:59 AM EST)
utc_time = datetime(2026, 3, 9, 6, 59, 0, tzinfo=timezone.utc)
eastern = convert_to_eastern(utc_time, ZoneInfo("America/New_York"))
print(eastern) # 2026-03-09 01:59:00-05:00 (EST)
# After DST (3:00 AM EDT)
utc_time = datetime(2026, 3, 9, 7, 0, 0, tzinfo=timezone.utc)
eastern = convert_to_eastern(utc_time, ZoneInfo("America/New_York"))
print(eastern) # 2026-03-09 03:00:00-04:00 (EDT)
EDT to EST Transition (Fall Back)
# November 1, 2026 at 2:00 AM EDT → 1:00 AM EST
# Before DST ends (1:59 AM EDT)
utc_time = datetime(2026, 11, 1, 5, 59, 0, tzinfo=timezone.utc)
eastern = convert_to_eastern(utc_time, ZoneInfo("America/New_York"))
print(eastern) # 2026-11-01 01:59:00-04:00 (EDT)
# After DST ends (1:00 AM EST)
utc_time = datetime(2026, 11, 1, 6, 0, 0, tzinfo=timezone.utc)
eastern = convert_to_eastern(utc_time, ZoneInfo("America/New_York"))
print(eastern) # 2026-11-01 01:00:00-05:00 (EST)
Key points:
- The bot code never needs to know about DST transitions
ZoneInfo handles all offset calculations automatically
- Works correctly across DST boundaries in exports
- Historical accuracy for old messages
The bot uses different timestamp formats in different contexts:
# In serialize_message function (bot.py:116-152)
eastern_time = convert_to_eastern(message.created_at, config.eastern_tz)
return {
"timestamp": eastern_time.isoformat(),
"timestamp_utc": message.created_at.isoformat()
}
Output:
{
"timestamp": "2026-03-04T14:23:15-05:00",
"timestamp_utc": "2026-03-04T19:23:15+00:00"
}
Format breakdown:
2026-03-04 - Date (YYYY-MM-DD)
T - Time separator
14:23:15 - Time (HH:MM:SS)
-05:00 - UTC offset (EST is -5 hours)
-04:00 - UTC offset during EDT
# In prepare_transcript function (bot.py:268-284)
messages_text = "\n".join([
f"[{msg['timestamp'][:19]}] {msg['author']['display_name']}: {msg['content_clean']}"
for msg in non_bot_messages
])
Output:
[2026-03-04T14:23:15] John Doe: Finished the authentication refactor
Format: Truncated to 19 characters (removes timezone offset)
# In format_obsidian_document function (bot.py:341-374)
*Auto-generated at {datetime.now(config.eastern_tz).strftime('%I:%M %p ET')} from Discord*
Output:
Auto-generated at 12:00 AM ET from Discord
Auto-generated at 02:30 PM ET from Discord
Format string:
%I - 12-hour (01-12)
%M - Minutes (00-59)
%p - AM/PM
ET - Literal “ET” suffix
Every export includes timezone information:
# In perform_export function (bot.py:204-256)
export_data = {
"guild_name": guild.name,
"export_time_eastern": export_time_eastern.isoformat(),
"export_time_utc": export_time_utc.isoformat(),
"timezone": "America/New_York (Eastern Time - auto DST)",
"time_range_hours": hours,
"channels": {}
}
Example output:
{
"guild_name": "My Discord Server",
"export_time_eastern": "2026-03-04T14:30:00-05:00",
"export_time_utc": "2026-03-04T19:30:00+00:00",
"timezone": "America/New_York (Eastern Time - auto DST)",
"time_range_hours": 24
}
Why both timestamps:
- Eastern Time: For human consumption and local context
- UTC: For programmatic processing and international teams
- Dual format: Ensures no information loss
Scheduled Task Timing
The daily digest runs at midnight Eastern Time:
# From Config class (bot.py:39)
scheduled_hour: int = 0 # 12am ET
# From before_daily_digest function (bot.py:672-686)
async def before_daily_digest():
"""Wait until scheduled time to start the loop."""
await bot.wait_until_ready()
now = datetime.now(config.eastern_tz)
target_time = now.replace(hour=config.scheduled_hour, minute=0, second=0, microsecond=0)
if now.time() >= target_time.time():
target_time += timedelta(days=1)
wait_seconds = (target_time - now).total_seconds()
logger.info(f"⏰ Waiting {wait_seconds/3600:.1f} hours until first digest at {config.scheduled_hour}:00am ET")
await asyncio.sleep(wait_seconds)
How it works:
- Get current time in ET:
now = datetime.now(config.eastern_tz)
- Set target to midnight:
target_time = now.replace(hour=0, ...)
- If past midnight today, target tomorrow: Check if
now >= target_time
- Calculate wait time: Seconds until next midnight
- Sleep until midnight:
asyncio.sleep(wait_seconds)
DST behavior:
- On “spring forward” day: Digest runs at 12:00 AM EDT (1 hour earlier in UTC)
- On “fall back” day: Digest runs at 12:00 AM EST (1 hour later in UTC)
- Users always see “midnight ET” in their local timezone context
Message Timestamp Examples
Here’s how timestamps appear in different parts of the system:
In JSON Export
{
"message_id": "1234567890",
"author": {"name": "john_doe", "display_name": "John Doe", "id": "111222333", "bot": false},
"content": "Just deployed to production!",
"timestamp": "2026-03-04T14:23:15-05:00",
"timestamp_utc": "2026-03-04T19:23:15+00:00"
}
In Claude Transcript
[2026-03-04T14:23:15] John Doe: Just deployed to production!
[2026-03-04T14:25:42] Jane Smith: Great work!
[2026-03-04T14:30:18] Mike Johnson: Running smoke tests now
In Obsidian Digest
---
date: 2026-03-04
---
# Team Digest - 2026-03-04
[Content here]
---
*Auto-generated at 12:00 AM ET from Discord*
Changing the Timezone
To use a different timezone, modify the Config class:
# bot.py:30
@dataclass
class Config:
# ... other fields ...
# Change this line:
eastern_tz: ZoneInfo = field(default_factory=lambda: ZoneInfo("America/Los_Angeles"))
# For UTC (no conversion):
eastern_tz: ZoneInfo = field(default_factory=lambda: ZoneInfo("UTC"))
# For London:
eastern_tz: ZoneInfo = field(default_factory=lambda: ZoneInfo("Europe/London"))
Update these locations:
- Config class default (bot.py:30)
- Timezone string in export metadata (bot.py:220)
- Timestamp format strings in documentation
Timezone Validation
The bot doesn’t explicitly validate timezone names, but ZoneInfo will raise an exception if invalid:
try:
tz = ZoneInfo("Invalid/Timezone")
except ZoneInfoNotFoundError:
print("Timezone not found in IANA database")
Valid timezone list:
# On Linux/macOS
ls /usr/share/zoneinfo/
# In Python
import zoneinfo
print(zoneinfo.available_timezones())
ZoneInfo is efficient:
- Timezone objects are cached after first use
- Conversion is O(1) operation
- No external API calls or network requests
- Timezone data loaded from system database
No performance impact from:
- Converting thousands of message timestamps
- DST boundary calculations
- Historical date conversions