# -*- coding: utf-8 -*- # This file is part of convertdate. # http://github.com/fitnr/convertdate # Licensed under the MIT license: # http://opensource.org/licenses/MIT # Copyright (c) 2016, fitnr from math import trunc from . import gregorian from .utils import jwday, monthcalendarhelper EPOCH = 347995.5 HEBREW_YEAR_OFFSET = 3760 # Hebrew months NISAN = 1 IYYAR = 2 SIVAN = 3 TAMMUZ = 4 AV = 5 ELUL = 6 TISHRI = 7 HESHVAN = 8 KISLEV = 9 TEVETH = 10 SHEVAT = 11 ADAR = 12 VEADAR = 13 def leap(year): # Is a given Hebrew year a leap year ? return (((year * 7) + 1) % 19) < 7 def year_months(year): '''How many months are there in a Hebrew year (12 = normal, 13 = leap)''' if leap(year): return 13 else: return 12 def delay_1(year): '''Test for delay of start of new year and to avoid''' # Sunday, Wednesday, and Friday as start of the new year. months = trunc(((235 * year) - 234) / 19) parts = 12084 + (13753 * months) day = trunc((months * 29) + parts / 25920) if ((3 * (day + 1)) % 7) < 3: day += 1 return day def delay_2(year): '''Check for delay in start of new year due to length of adjacent years''' last = delay_1(year - 1) present = delay_1(year) next_ = delay_1(year + 1) if next_ - present == 356: return 2 elif present - last == 382: return 1 else: return 0 def year_days(year): '''How many days are in a Hebrew year ?''' return to_jd(year + 1, 7, 1) - to_jd(year, 7, 1) def month_days(year, month): '''How many days are in a given month of a given year''' if month > 13: raise ValueError("Incorrect month index") # First of all, dispose of fixed-length 29 day months if month in (IYYAR, TAMMUZ, ELUL, TEVETH, VEADAR): return 29 # If it's not a leap year, Adar has 29 days if month == ADAR and not leap(year): return 29 # If it's Heshvan, days depend on length of year if month == HESHVAN and (year_days(year) % 10) != 5: return 29 # Similarly, Kislev varies with the length of year if month == KISLEV and (year_days(year) % 10) == 3: return 29 # Nope, it's a 30 day month return 30 def to_jd(year, month, day): months = year_months(year) jd = EPOCH + delay_1(year) + delay_2(year) + day + 1 if month < 7: for mon in range(7, months + 1): jd += month_days(year, mon) for mon in range(1, month): jd += month_days(year, mon) else: for mon in range(7, month): jd += month_days(year, mon) return int(jd) + 0.5 def from_jd(jd): jd = trunc(jd) + 0.5 count = trunc(((jd - EPOCH) * 98496.0) / 35975351.0) year = count - 1 i = count while jd >= to_jd(i, 7, 1): i += 1 year += 1 if jd < to_jd(year, 1, 1): first = 7 else: first = 1 month = i = first while jd > to_jd(year, i, month_days(year, i)): i += 1 month += 1 day = int(jd - to_jd(year, month, 1)) + 1 return (year, month, day) def to_jd_gregorianyear(gregorianyear, hebrew_month, hebrew_day): # gregorian year is either 3760 or 3761 years less than hebrew year # we'll first try 3760 if conversion to gregorian isn't the same # year that was passed to this method, then it must be 3761. for y in (gregorianyear + HEBREW_YEAR_OFFSET, gregorianyear + HEBREW_YEAR_OFFSET + 1): jd = to_jd(y, hebrew_month, hebrew_day) gd = gregorian.from_jd(jd) if gd[0] == gregorianyear: break else: gd = None if not gd: # should never occur, but just incase... raise ValueError("Could not determine gregorian year") # tuple: (y, m, d) return (gd[0], gd[1], gd[2]) def from_gregorian(year, month, day): return from_jd(gregorian.to_jd(year, month, day)) def to_gregorian(year, month, day): return gregorian.from_jd(to_jd(year, month, day)) def monthcalendar(year, month): start_weekday = jwday(to_jd(year, month, 1)) monthlen = month_days(year, month) return monthcalendarhelper(start_weekday, monthlen)