Ни для кого не секрет, что мы сейчас пользуемся григорианским календарём введённым после Октябрьской революции большевиками, но празднуем христианские праздники по юлианскому календарю, который отличается от григорианского на 13 дней. Но почему так происходит? Давайте разбираться.
Юлианский календарь округляет год до 365 дней, вводя раз в 4 года дополнительный день, называемый високосным годом, из-за чего год становится равным 366 дням (0.25 * 4 = 1). Но из-за этого также с годами накапливаются и лишние дни, из-за чего точность такого календаря падает. Примерно за 1600 лет (Юлий Цезарь создал свой календарь в 45 год до н.э.) их накопилось 10 и поэтому папа римский Григорий XIII ввёл новый календарь, исправляющий эту неточность.
Вместо обычных високосных годов кратных 4 он "отсеял" кратные 100, но также отнёс к високосным кратные 400. То есть:
1700, 1800, 1900 теперь уже не високосные как прежде
А 1600, 2000 високосные.
Но этот календарь поспешили принять не все христианские страны. До XVIII века этот календарь, например, не принимали даже протестанты с их не любовью к католикам, не говоря уже о православных для которых традиция была важнее всего (этой традицией была расчёт Пасхи), поэтому тут даже новоюлианский календарь, разработанный сербским астрономом здесь не очень помог поскольку из-за своей более высокой точности ещё дальше уходил от традиций.
Так есть ли решение из этой долгой проблемы человечества? Ответ есть! И это не более высокая точность календаря, поскольку они всё равно не вечны и могут повторить ошибку 2000 г, а баланс между традицией (юлианский календарь) и новшеством (григорианский календарь). Как достичь такого баланса? Всё просто:
Юлианский: т.е. 100 високосных на 400 лет.
Григорианский: т.е. 97 високосных на 400 лет (3 дня убираются).
Из этого следует что юлианский календарь ближе к сидерическому году (365.25636 дней), а григорианский к тропическому (365.24219 дней).
Поэтому я взял такое среднее арифметическое число между этими годами, чтобы оно получилось 365,2493
Поэтому исходя из простого уравнения календарь будет иметь вид:
Но его формула будет иметь более сложный вид:
Теперь проверим эту модель на практике:
class GregorianCalendar:
name = "Gregorian"
def is_leap_year(self, year: int) -> bool:
return (year % 4 == 0) and (year % 100 != 0 or year % 400 == 0)
def year_length(self, year: int) -> int:
return 366 if self.is_leap_year(year) else 365
class JulianCalendar:
name = "Julian"
def is_leap_year(self, year: int) -> bool:
return year % 4 == 0
def year_length(self, year: int) -> int:
return 366 if self.is_leap_year(year) else 365
class RationalCalendar:
name = "Custom (365.2493)"
def __init__(self, leap_days_per_cycle, cycle_length=400):
self.base_days = 365
self.cycle_length = cycle_length
self.leap_fraction = leap_days_per_cycle / cycle_length
def is_leap_year(self, year: int) -> bool:
prev = (year - 1) * self.leap_fraction
curr = year * self.leap_fraction
return int(curr) > int(prev)
def year_length(self, year: int) -> int:
return self.base_days + (1 if self.is_leap_year(year) else 0)
def average_year_length(self) -> float:
return sum(self.year_length(y) for y in range(1, self.cycle_length + 1)) / self.cycle_length
MONTHS = [
("January", 31),
("February", 28),
("March", 31),
("April", 30),
("May", 31),
("June", 30),
("July", 31),
("August", 31),
("September", 30),
("October", 31),
("November", 30),
("December", 31),
]
def generate_year(calendar, year: int):
months = []
leap = calendar.is_leap_year(year)
for name, days in MONTHS:
if name == "February" and leap:
months.append((name, days + 1))
else:
months.append((name, days))
return months
gregorian = GregorianCalendar()
julian = JulianCalendar()
custom = RationalCalendar(leap_days_per_cycle=99.72, epoch=1699)
print("Year\tGreg\tJulian\tMy")
for y in [2100,2200,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021,2022,2023,2024, 2025, 2000,1600,1700,1800,1900]:
print(y,'\t',
gregorian.is_leap_year(y),'\t',
julian.is_leap_year(y),'\t',
custom.is_leap_year(y))
Итого мы видим что календарь почему-то не совпадает ни с юлианским, ни с григорианским. Почему так происходит? Ему нужна эпоха, якорь. Вычисляем нужную эпоху по формуле.
где:
ref = юлианский / григорианский / смесь
epoch — целое число (обычно −2000…+2000)
def score_epoch(epoch, start=1600, end=2100):
cal = RationalCalendar(99.72, epoch=epoch)
score = 0
for y in range(start, end + 1):
ref = (is_julian_leap(y) + is_gregorian_leap(y)) / 2
score += abs(cal.is_leap(y) - ref)
return score
best = min(range(-2000, 2001), key=score_epoch)
print("Best epoch:", best)Получим
Best epoch: 1699
Это как раз то, что нам нужно. Ведь подставив её мы получим.
Year Greg Julian My
2024 True True True
2025 False False False
2000 True True True
1600 True True False
1700 False True False
1800 False True True
1900 False True TrueТеперь в годах есть "балансировка" от одного года к другому. Это как раз то что было мною задумано! Пасха на нём тоже вычисляется замечательно!
P.S. Конечно, я не астроном, а лишь программист, который немного увлекается математикой, поэтому мнение настоящих специалистов по этому поводу будет очень ценно. А я только лишь буду рад что хотя бы немного помог человечеству продвинутся в этом направлении, используя современные технологии для создания такого календаря, независимо оттого введёт кто-либо мой календарь или наоборот отвергнет.
Вот код который генерирует календарь на Python исходя из его алгоритма (версия 1)
import calendar
def is_julian_gregorian_leap(year):
return ((year % 4 == 0) or (year % 100 == 0)) and (year % 500 != 0)
#Григорианский: return ((year % 4 == 0) and (year % 100 != 0)) or (year % 400 == 0)
def generate_custom_calendar(year):
months_ru = [
"Январь", "Февраль", "Март", "Апрель", "Май", "Июнь",
"Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь"
]
days_ru = ["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"]
# Количество дней в месяцах при обычном или високосном году по вашему правилу
month_days = [31, 29 if is_julian_gregorian_leap(year) else 28, 31, 30, 31, 30,
31, 31, 30, 31, 30, 31]
# Для определения дня недели 1 января используем классический календарь (григорианский)
first_weekday = calendar.weekday(year, 1, 1) # 0 - Понедельник
print(f"Календарь (юлиано-григорианский) на {year} год\n")
for month_idx in range(12):
print(f"{months_ru[month_idx]} {year}")
print(" ".join(days_ru))
# Построение календаря месяца
day = 1
# Смещение первого дня месяца (зависит от первого дня года и даты начала месяца)
# Для упрощения считаем сдвиг - создадим список с пустыми днями
# Считаем день недели для 1 числа каждого месяца, поправляем вручную
# Определяем день недели для 1 числа текущего месяца
# Используем стандартный календарь для приближенного результата
wd = calendar.weekday(year, month_idx+1, 1)
# Печать строк недели
week = [" "] * ((wd + 6) % 7) # смещение, чтобы понедельник=0 (calendar понедельник=0)
while day <= month_days[month_idx]:
week.append(f"{day:2} ")
if len(week) == 7:
print("".join(week))
week = []
day += 1
if week:
print("".join(week))
print()
if __name__ == "__main__":
y = int(input("Введите год: "))
generate_custom_calendar(y)
Статья была обновлена. Большое спасибо Elijah_N за уточнение про то, что кроме тропического, оказывается, ещё существует и сидерический год!