Timezone Fix Summary
Issue
The sell_time and exit_time fields in the earnings recommendations were not recording the correct market timezone. All timestamps were being stored as GMT+0 (naive datetimes without timezone information), which meant:
- Times were not adjusted for the actual exchange timezone (NYSE/NASDAQ = Eastern, LSE = London, etc.)
- Database queries and time comparisons were unreliable
- Users couldn't accurately determine when to enter/exit trades in their local timezone
Root Cause
Two scripts were creating naive datetime objects (without timezone info) and either:
1. Not attaching timezone information at all
2. Attempting to attach timezone info but with None values (when yfinance didn't return timezone data)
The problematic pattern:
earnings_dt = datetime.strptime(earnings_date_str, "%Y-%m-%d") # Naive datetime
sell_time_dt = (earnings_dt - timedelta(days=1)).replace(
hour=15, minute=45, second=0, microsecond=0
) # Still naive
# No timezone attached -> saved to DB as GMT+0
Solution
Fixed all three scripts to:
1. Fetch the exchange timezone from yfinance using ticker.info["exchangeTimezoneName"]
2. Default to America/New_York (US/Eastern) if exchange timezone is unavailable (most US stocks trade on NYSE/NASDAQ)
3. Create timezone-aware datetime objects from the start using the tzinfo parameter
4. Properly save to database with timezone information preserved via .isoformat()
The corrected pattern:
# Get exchange timezone
ticker_info = get_ticker_info(symbol)
exchange_tz = ticker_info["exchange_tz"]
# Default to Eastern if not available
tz = exchange_tz if exchange_tz else ZoneInfo("America/New_York")
# Create timezone-aware datetime from scratch
sell_date = earnings_date_parsed - timedelta(days=1)
sell_time_dt = datetime(
sell_date.year, sell_date.month, sell_date.day,
15, 45, 0, 0, tzinfo=tz # Timezone-aware from creation
)
Files Modified
1. scripts/go_hunting.py
- Lines 14, 82: Added
ZoneInfoimport - Lines 64-84: Added
get_ticker_info()function to fetch company name and exchange timezone - Lines 87-97: Modified
get_company_name()to useget_ticker_info() - Lines 589-608: Fixed timezone handling for
sell_time_dtandexit_time_dt - Now parses earnings date as
dateobject first - Creates timezone-aware datetimes with exchange timezone or fallback to Eastern
- Properly constructs datetimes with
tzinfoparameter
2. scripts/next_friday_calc.py
- Line 13: Added
ZoneInfoimport - Lines 79-100: Added
get_ticker_info()function - Lines 102-110: Modified
get_company_name()to useget_ticker_info() - Lines 685-717: Fixed timezone handling
- Gets both company name and exchange timezone in one call (optimization)
- Creates timezone-aware datetimes with proper timezone
3. tests/test_datetime_format.py
- Completely rewritten to test timezone handling
- Now tests multiple timezones (Eastern, Central, London)
- Shows ISO format with timezone offsets
- Demonstrates UTC conversion
4. tests/test_timezone_handling.py (NEW)
- Comprehensive test suite for timezone handling
- Tests timezone-aware datetime creation
- Tests fallback to Eastern timezone
- Tests different exchange timezones
- Tests ISO format preservation of timezone info
- All tests pass ✓
Database Impact
The earnings_recommendations table's sell_time and exit_time columns now store:
Before (incorrect):
sell_time: 2026-02-10T15:45:00+00:00 # Always GMT+0, regardless of exchange
exit_time: 2026-02-11T09:45:00+00:00
After (correct):
sell_time: 2026-02-10T15:45:00-05:00 # Eastern Time for NYSE/NASDAQ stocks
exit_time: 2026-02-11T09:45:00-05:00
sell_time: 2026-02-10T15:45:00+00:00 # GMT for LSE stocks
exit_time: 2026-02-11T09:45:00+00:00
Benefits
- Accurate Time Tracking: Times are now stored in the actual exchange timezone
- Proper DST Handling: ZoneInfo automatically handles daylight saving time transitions
- Easy Conversion: ISO format with timezone allows easy conversion to user's local time
- International Support: Works correctly for stocks on different exchanges (NYSE, LSE, etc.)
- Database Queries: Can now reliably query by time ranges accounting for timezones
Testing
All timezone handling tests pass:
uv run python tests/test_timezone_handling.py
# ✓ ALL TESTS PASSED
uv run python tests/test_datetime_format.py
# ✓ Date/time formatting with timezone support working correctly!
Migration Notes
Important: Existing records in the database have incorrect timezones (GMT+0). To fix historical data:
-- This is approximate and assumes all stocks are on Eastern time
-- More sophisticated migration would need to look up each ticker's exchange
UPDATE earnings_recommendations
SET
sell_time = sell_time AT TIME ZONE 'UTC' AT TIME ZONE 'America/New_York',
exit_time = exit_time AT TIME ZONE 'UTC' AT TIME ZONE 'America/New_York'
WHERE sell_time IS NOT NULL
AND exit_time IS NOT NULL
AND EXTRACT(TIMEZONE FROM sell_time) = 0; -- Only update GMT+0 records
Recommendation: Consider the historical data as approximate and prioritize new data going forward.
Future Improvements
- Consider caching exchange timezone lookups to reduce yfinance API calls
- Add logging to track when fallback timezone is used
- Consider adding a
market_timezonecolumn to the database for audit trail - Add validation to ensure sell_time < exit_time in the same timezone