Способы определения сезона по дате в языке программирования Python

Раздел: Python -> Время и даты

Основные подходы к определению времени года

Как быстро узнать время года только по номеру месяца?

Самый простой способ - сопоставить месяц сезону без учёта дня. Однако такой подход не различает переходные периоды (например, 31 мая - весна, а 1 июня - лето) и может давать ошибки на границах.

month = 5
if month in [12, 1, 2]:
    season = 'Зима'
elif month in [3, 4, 5]:
    season = 'Весна'
elif month in [6, 7, 8]:
    season = 'Лето'
else:
    season = 'Осень'
print(season)  # Весна

время создания python (время создания файла/объекта в python)

Проблема: такая логика не учитывает день, поэтому 31 мая будет весной, а 1 июня - летом, что чаще всего устраивает пользователей. Исключение - даты вблизи 21–22 марта, июня, сентября, декабря, где сезон по календарю может отличаться от метеорологического.
Решение: добавить проверку дня для первых месяцев сезонов (см. следующий вариант).

Как определить время года по дате с учётом дня месяца (метеорологический подход)?

Метеорологические сезоны обычно начинаются 1 марта, 1 июня, 1 сентября и 1 декабря. Этот метод корректен для большинства бытовых задач.

from datetime import datetime

def season(date_str, fmt="%Y-%m-%d"):
    dt = datetime.strptime(date_str, fmt)
    month = dt.month
    day = dt.day
    if month == 12 or month <= 2:
        return 'Зима'
    if month == 3:
        return 'Весна' if day >= 1 else 'Зима'
    if month == 6:
        return 'Лето' if day >= 1 else 'Весна'
    if month == 9:
        return 'Осень' if day >= 1 else 'Лето'
    return 'Осень'  # для остальных месяцев

print(season("2023-03-01"))  # Весна
print(season("2023-02-28"))  # Зима

время сообщения python (время сообщения (метка времени) в python)

Здесь проверка дня в месяцах 3, 6, 9 избыточна (всегда 1), но если потребуется более тонкое деление (например, весна с 1 марта), она работает. На практике можно обойтись без проверки дня, если мы уверены, что первый день сезона - 1 число.

Частая ошибка: путаница с индексами месяцев (декабрь - 12, январь - 1). В условии для зимы нужно охватывать и 12, и 1, и 2.
Совет: используйте кортежи для месяцев, чтобы код был более читаемым.

Как использовать библиотеку dateutil для работы с датами и сезонами?

Модуль dateutil.rrule позволяет создавать повторяющиеся события, но для определения сезона его можно применить косвенно, например, для нахождения следующего начала сезона. Однако проще использовать dateutil.parser для разбора дат.

from dateutil import parser
from datetime import datetime

def season_dateutil(date_str):
    dt = parser.parse(date_str)
    month = dt.month
    # та же логика, что в предыдущем примере
    if month in (12, 1, 2):
        return 'Зима'
    elif month in (3, 4, 5):
        return 'Весна'
    elif month in (6, 7, 8):
        return 'Лето'
    else:
        return 'Осень'

print(season_dateutil("2023-07-15"))  # Лето

дата и время в python (работа с датой и временем в python)

Ошибка: дата может быть в нестандартном формате; dateutil.parser пытается угадать, но иногда допускает неоднозначность (например, 01/02/2023 - январь или февраль?).
Решение: всегда указывать dayfirst=True или yearfirst=True для устранения неоднозначности.
Как определить астрономическое время года с помощью библиотеки ephem?

Астрономические сезоны определяются днями равноденствий и солнцестояний. Библиотека ephem вычисляет точные моменты перехода.

import ephem
from datetime import datetime, date

def astro_season(input_date):
    year = input_date.year
    # Дата весеннего равноденствия
    vernal_equinox = ephem.next_vernal_equinox(str(year))
    # Летнее солнцестояние
    summer_solstice = ephem.next_summer_solstice(vernal_equinox)
    # Осеннее равноденствие
    autumn_equinox = ephem.next_autumnal_equinox(summer_solstice)
    # Зимнее солнцестояние
    winter_solstice = ephem.next_winter_solstice(autumn_equinox)
    
    dates = {
        'Весна': ephem.localtime(vernal_equinox).date(),
        'Лето': ephem.localtime(summer_solstice).date(),
        'Осень': ephem.localtime(autumn_equinox).date(),
        'Зима': ephem.localtime(winter_solstice).date()
    }
    
    # Сортируем даты для сравнения
    sorted_seasons = sorted(dates.items(), key=lambda x: x[1])
    current = input_date
    for season_name, start_date in sorted_seasons:
        if current < start_date:
            # Если до первого старта, то предыдущий сезон (зима предыдущего года)
            if season_name == 'Весна':
                return 'Зима'
            break
        else:
            # Ищем последний сезон, который начался
            pass
    # Если прошли все, то зима следующего года? Нужна более точная логика.
    # Упрощённо: после дня зимнего солнцестояния до весеннего равноденствия - зима.
    if current >= dates['Зима']:
        return 'Зима'
    elif current >= dates['Осень']:
        return 'Осень'
    elif current >= dates['Лето']:
        return 'Лето'
    elif current >= dates['Весна']:
        return 'Весна'
    else:
        return 'Зима'

print(astro_season(date(2023, 3, 20)))  # может быть Зима или Весна - зависит от точного времени равноденствия

модуль время python (модуль time в python)

Проблема: ephem может быть избыточен для большинства приложений, требует установки и зависит от системных часов. Кроме того, результат зависит от временной зоны.
Решение: использовать только для точных астрономических расчётов; для веб-приложений лучше метеорологический подход.
Как определить сезон для столбца дат в pandas?

Если данные находятся в DataFrame, удобно использовать векторизованные операции.

import pandas as pd

df = pd.DataFrame({
    'date': pd.date_range('2023-01-01', periods=365, freq='D')
})

def get_season(month):
    if month in (12, 1, 2):
        return 'Зима'
    elif month in (3,4,5):
        return 'Весна'
    elif month in (6,7,8):
        return 'Лето'
    else:
        return 'Осень'

df['season'] = df['date'].dt.month.map(get_season)
print(df.head())

Python определить время (определение времени в python)

        date season
0 2023-01-01   Зима
1 2023-01-02   Зима
2 2023-01-03   Зима
3 2023-01-04   Зима
4 2023-01-05   Зима
Ошибка: при использовании map с Series нужно передавать функцию, принимающую одно значение. Если требуется день, можно создать кортеж (month, day) и применить функцию к строке.
Решение: использовать df.apply(lambda row: ..., axis=1) для более сложной логики, но это медленнее.
- Python datetime время (работа с datetime в python)
- Python 3 время (работа с временем в python 3)
- Python функция времени (функции для работы со временем в python)

Расширенные примеры и нюансы

1. Универсальная функция с поддержкой разных форматов

Пример
from datetime import datetime

def get_season(date_input, fmt=None):
    """
    Возвращает название сезона для переданной даты.
    date_input: строка, datetime или date
    fmt: формат строки (если не указан, используется %Y-%m-%d)
    """
    dt = None
    if isinstance(date_input, str):
        if fmt is None:
            fmt = "%Y-%m-%d"
        dt = datetime.strptime(date_input, fmt)
    elif isinstance(date_input, datetime):
        dt = date_input
    elif hasattr(date_input, 'month'):  # object with .month attribute
        dt = date_input
    else:
        raise ValueError("Неверный тип даты")
    
    month = dt.month
    day = dt.day if hasattr(dt, 'day') else 1
    
    # метеорологические сезоны (начиная с 1 числа)
    if (month == 12) or (month <= 2):
        return 'Зима'
    elif (month == 3) or (month == 4) or (month == 5):
        return 'Весна'
    elif (month == 6) or (month == 7) or (month == 8):
        return 'Лето'
    else:
        return 'Осень'

# Примеры вызова
print(get_season("2023-12-31"))  # Зима
print(get_season(datetime(2023, 6, 20)))  # Лето
print(get_season("31.12.2023", "%d.%m.%Y"))  # Зима
Зима
Лето
Зима

2. Обработка списка дат с выводом в DataFrame

Пример
import pandas as pd
from datetime import datetime

dates = ["2023-02-28", "2023-03-01", "2023-06-01", "2023-09-01", "2023-11-30"]
df = pd.DataFrame({'date_str': dates})
df['date'] = pd.to_datetime(df['date_str'])
df['season'] = df['date'].apply(get_season)
print(df)
    date_str       date season
0  2023-02-28 2023-02-28   Зима
1  2023-03-01 2023-03-01  Весна
2  2023-06-01 2023-06-01   Лето
3  2023-09-01 2023-09-01   Осень
4  2023-11-30 2023-11-30   Осень

3. Учёт високосного года (29 февраля)

Пример
# 29 февраля существует, и это всегда зима (метеорологическая)
print(get_season("2024-02-29"))  # Зима
# Проверка границ марта в високосный год
print(get_season("2024-03-01"))  # Весна
Зима
Весна

4. Астрономический сезон с точностью до часа

Пример
import ephem
from datetime import datetime, timezone

# Используем UTC, чтобы избежать часовых поясов
def astro_season_utc(dt):
    year = dt.year
    v = ephem.next_vernal_equinox(str(year))
    s = ephem.next_summer_solstice(v)
    a = ephem.next_autumnal_equinox(s)
    w = ephem.next_winter_solstice(a)
    
    # Преобразуем ephem-даты в datetime (UTC)
    v_dt = ephem.localtime(v).replace(tzinfo=timezone.utc)
    s_dt = ephem.localtime(s).replace(tzinfo=timezone.utc)
    a_dt = ephem.localtime(a).replace(tzinfo=timezone.utc)
    w_dt = ephem.localtime(w).replace(tzinfo=timezone.utc)
    
    if dt < v_dt:
        return 'Зима'
    elif dt < s_dt:
        return 'Весна'
    elif dt < a_dt:
        return 'Лето'
    elif dt < w_dt:
        return 'Осень'
    else:
        return 'Зима'

test = datetime(2023, 6, 21, 14, 57, tzinfo=timezone.utc)  # около летнего солнцестояния
print(astro_season_utc(test))
Лето

5. Определение сезона с помощью сдвига месяца (альтернативный подход)

Пример
def season_shift(dt):
    # Сдвиг на 2 месяца (март = 5, апрель = 6 и т.д.)
    # Декабрь (12) + 1 = 13 -> 13 % 12 = 1 -> зима
    # Не очень наглядно, но компактно
    return ['Зима', 'Весна', 'Лето', 'Осень'][((dt.month + 1) % 12) // 3]

dates_test = [datetime(2023, 1, 1), datetime(2023, 4, 1), datetime(2023, 7, 1), datetime(2023, 10, 1)]
for d in dates_test:
    print(d.date(), season_shift(d))
2023-01-01 Зима
2023-04-01 Весна
2023-07-01 Лето
2023-10-01 Осень

Важно:

этот метод работает, только если считать началом сезона 1 число соответствующего месяца; для других границ требуется корректировка.

6. Работа с часовыми поясами при астрономическом подходе

Пример
import ephem
from datetime import datetime, timezone, timedelta

# Если даты в локальном времени, нужно привести к UTC
def astro_season_local(dt_local, utc_offset_hours=3):
    dt_utc = dt_local - timedelta(hours=utc_offset_hours)
    return astro_season_utc(dt_utc)

# Пример: Москва (UTC+3) 20 марта 2023 10:00
local_dt = datetime(2023, 3, 20, 10, 0)
print(astro_season_local(local_dt, 3))  # может быть Весна или Зима в зависимости от точного времени

Результат может отличаться от метеорологического - астрономическая весна 2023 года наступила 20 марта в 21:24 UTC, поэтому в Москве в 10:00 ещё была зима.

Определение времени года по дате в Python - comments

En
Python время года (python)