Вход: UTC-дата/время (обычно из GPS)

Выход: локальное время (часы, минуты, секунды) и флаг летнего времени.

def is_leap_year(year):

Год високосный?

return (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0)

def days_in_month(year, month):

Количество дней в месяце

if month == 2:
return 29 if is_leap_year(year) else 28
if month in (1, 3, 5, 7, 8, 10, 12):
return 31
return 30

def date_to_ordinal(year, month, day):

Переводим дату в "номер дня" с 0001‑01‑01 (как в datetime.toordinal)

Чтобы можно было узнать день недели.

0001‑01‑01 считаем днём 1.

n = day
for m in range(1, month):
n += days_in_month(year, m)

Грубая формула количества дней в предыдущих годах:

year - 1 полных лет

y = year - 1
n += y * 365 + y // 4 - y // 100 + y // 400
return n

def weekday(year, month, day):

День недели: 0=понедельник, ..., 6=воскресенье

В Python datetime.toordinal().weekday() даёт ровно это.

ord_day = date_to_ordinal(year, month, day)
return (ord_day - 1) % 7

def last_sunday(year, month):

Находим последнюю воскресенье в месяце

d = days_in_month(year, month)
while weekday(year, month, d) != 6: # 6 = воскресенье
d -= 1
return d

def is_summer_time_ukraine(year, month, day, hour, minute, second):
"""
Возвращает True, если в Украине действовало летнее время (UTC+3),
и False — если зимнее (UTC+2).

Правило:

  • до конца 2024 года включительно: стандарт Европа (последнее воскресенье марта/октября);[web:25][web:34][web:22]
  • с 2025 года и далее летнего времени НЕТ (всегда UTC+2).[web:14][web:15][web:17][web:36]

"""

С 2025 года Украина остаётся на постоянном UTC+2, без летнего времени.

if year >= 2025:
return False

Переходы до отмены:

начало DST: последняя воскресенье марта в 03:00 по UTC (подгоняем под обычные EU‑правила)

конец DST: последняя воскресенье октября в 04:00 по UTC (примерно так для EET/EEST).[web:25][web:34]

найдём даты переходов для этого года

start_day = last_sunday(year, 3)
end_day = last_sunday(year, 10)

Сравниваем "момент времени" по UTC с точками перехода.

Приведём всё к кортежу (month, day, hour, minute, second) для сравнения.

current = (month, day, hour, minute, second)
start = (3, start_day, 3, 0, 0) # 03:00 в марте
end = (10, end_day, 4, 0, 0) # 04:00 в октябре

Летнее время действует между start (включительно) и end (исключая end).

return current >= start and current < end

def utc_to_kyiv(utc_year, utc_month, utc_day, utc_hour, utc_minute, utc_second):
"""
Переводит UTC в киевское время.
Возвращает (year, month, day, hour, minute, second, is_summer).

is_summer = True -> летнее время (UTC+3)
is_summer = False -> зимнее время (UTC+2)
"""

is_summer = is_summer_time_ukraine(
utc_year, utc_month, utc_day, utc_hour, utc_minute, utc_second
)

offset_hours = 3 if is_summer else 2

Добавляем сдвиг к UTC

total_seconds = utc_hour * 3600 + utc_minute * 60 + utc_second
total_seconds += offset_hours * 3600

Нормализуем день/дату

Шаг 1: сдвиг по дням

extra_days = 0
while total_seconds >= 24 * 3600:
total_seconds -= 24 * 3600
extra_days += 1
while total_seconds < 0:
total_seconds += 24 * 3600
extra_days -= 1

Шаг 2: добавляем extra_days к дате

year = utc_year
month = utc_month
day = utc_day

вперёд

while extra_days > 0:
dim = days_in_month(year, month)
if day < dim:
day += 1
extra_days -= 1
else:
day = 1
month += 1
if month > 12:
month = 1
year += 1
extra_days -= 1

назад

while extra_days < 0:
if day > 1:
day -= 1
extra_days += 1
else:
month -= 1
if month < 1:
month = 12
year -= 1
day = days_in_month(year, month)
extra_days += 1

Разбираем обратно время

hour = total_seconds // 3600
minute = (total_seconds % 3600) // 60
second = total_seconds % 60

return year, month, day, hour, minute, second, is_summer

-------------------------------

Пример использования с GPS‑UTC

-------------------------------

def gps_utc_to_kyiv(date_str, time_str):
"""
date_str: 'DDMMYY' или 'YYYYMMDD' — подстрой под свой формат
time_str: 'HHMMSS'
Тут пример для формата NMEA GPRMC: DDMMYY и HHMMSS.
"""

Разбор NMEA‑формата DDMMYY

ВНИМАНИЕ: подстрой под то, что у тебя реально в коде!

day = int(date_str[0:2])
month = int(date_str[2:4])
year = 2000 + int(date_str[4:6]) # NMEA даёт год как 2 цифры

hour = int(time_str[0:2])
minute = int(time_str[2:4])
second = int(time_str[4:6])

return utc_to_kyiv(year, month, day, hour, minute, second)

Если хочешь просто сейчас:

if __name__ == "__main__":

пример: UTC 27.10.2024 00:30:00 (ещё лето, перед переводом)

y, m, d, hh, mm, ss, summer = utc_to_kyiv(2024, 10, 27, 0, 30, 0)
print("Kyiv:", y, m, d, hh, mm, ss, "summer:", summer)

[file-name 000342_2026-02-16_10-33-48.txt]