diff --git a/main.py b/main.py index dfadba4..52ab8c6 100644 --- a/main.py +++ b/main.py @@ -84,6 +84,47 @@ def calculate_x_spacing(text, scale=1): return (128 - (scaled_text * 8)) // 2 +def is_dst(year, month, day, hour_utc, weekday): + """ + Returns True if UTC time falls within US/Canada Daylight Saving Time: + - Starts: 2nd Sunday in March at 02:00 local (07:00 UTC for UTC-5) + - Ends: 1st Sunday in November at 02:00 local (06:00 UTC for UTC-4) + Note: weekday is 0=Monday ... 6=Sunday. + """ + # 1. Months completely inside or outside DST + if month < 3 or month > 11: + return False + if 3 < month < 11: + return True + + # 2. March (Starts 2nd Sunday) + # The 2nd Sunday always falls between March 8 and March 14 + if month == 3: + # Find the day of the 2nd Sunday + second_sunday = 14 - ((weekday - day + 14) % 7) + if day < second_sunday: + return False + elif day > second_sunday: + return True + else: + # On the transition day itself, 02:00 EST is 07:00 UTC + return hour_utc >= 7 + + # 3. November (Ends 1st Sunday) + # The 1st Sunday always falls between Nov 1 and Nov 7 + if month == 11: + first_sunday = 7 - ((weekday - day + 7) % 7) + if day < first_sunday: + return True + elif day > first_sunday: + return False + else: + # On the transition day itself, 02:00 EDT (01:00 EST) is 06:00 UTC + return hour_utc < 6 + + return False + + # --- Main Loop --- DAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] TIME_Y = 12 # Keep vertical placement consistent @@ -91,14 +132,25 @@ TIME_Y = 12 # Keep vertical placement consistent last_second = -1 while True: - # 1. Compute time - local_epoch = time.time() + (Config.UTC_OFFSET * 3600) - year, month, day, hour_24, minute, second, weekday, _ = time.localtime(local_epoch) + # 1. Fast, single-integer read from RTC + current_sec = time.time() - # 2. Only redraw if the second has actually changed - if second != last_second: - last_second = second + # 2. Gate everything behind the 1-second boundary + if current_sec != last_second: + last_second = current_sec + # Unpack UTC once per second + utc_year, utc_month, utc_day, utc_hour, _, _, utc_weekday, _ = time.gmtime(current_sec) + + # Run DST check + dst_offset = 1 if is_dst(utc_year, utc_month, utc_day, utc_hour, utc_weekday) else 0 + total_offset = Config.UTC_OFFSET + dst_offset + + # Compute local wall-clock components + local_epoch = current_sec + (total_offset * 3600) + year, month, day, hour_24, minute, second, weekday, _ = time.localtime(local_epoch) + + # --- String formatting and OLED rendering --- sep = ":" if second % 2 == 0 else " " if Config.USE_24_HOUR: @@ -121,5 +173,5 @@ while True: oled.show() - # 3. Sleep in small increments to catch the exact second boundary with minimal latency - time.sleep_ms(20) + # Fast poll to catch the next second boundary immediately + time.sleep_ms(25)