Способы определения сезона по дате в языке программирования 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)
Решение: добавить проверку дня для первых месяцев сезонов (см. следующий вариант).
Как определить время года по дате с учётом дня месяца (метеорологический подход)?
Метеорологические сезоны обычно начинаются 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 число.
Совет: используйте кортежи для месяцев, чтобы код был более читаемым.
Как использовать библиотеку 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)
Решение: всегда указывать
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)
Решение: использовать только для точных астрономических расчётов; для веб-приложений лучше метеорологический подход.
Как определить сезон для столбца дат в 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) для более сложной логики, но это медленнее.
Расширенные примеры и нюансы
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 ещё была зима.