Skip to content

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 ZoneInfo import
  • Lines 64-84: Added get_ticker_info() function to fetch company name and exchange timezone
  • Lines 87-97: Modified get_company_name() to use get_ticker_info()
  • Lines 589-608: Fixed timezone handling for sell_time_dt and exit_time_dt
  • Now parses earnings date as date object first
  • Creates timezone-aware datetimes with exchange timezone or fallback to Eastern
  • Properly constructs datetimes with tzinfo parameter

2. scripts/next_friday_calc.py

  • Line 13: Added ZoneInfo import
  • Lines 79-100: Added get_ticker_info() function
  • Lines 102-110: Modified get_company_name() to use get_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

  1. Accurate Time Tracking: Times are now stored in the actual exchange timezone
  2. Proper DST Handling: ZoneInfo automatically handles daylight saving time transitions
  3. Easy Conversion: ISO format with timezone allows easy conversion to user's local time
  4. International Support: Works correctly for stocks on different exchanges (NYSE, LSE, etc.)
  5. 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

  1. Consider caching exchange timezone lookups to reduce yfinance API calls
  2. Add logging to track when fallback timezone is used
  3. Consider adding a market_timezone column to the database for audit trail
  4. Add validation to ensure sell_time < exit_time in the same timezone