Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calculating lunar/lunisolar holidays in Python

Any calendar nuts here? I've been looking for information on how to calculate the holidays of the current year that occur irregularly in the Gregorian Calendar. Typically this happens because the holiday is based on an older lunar calendar.

I've googled ad nauseum and made some progress but not able to finish. If anyone has sample code in a modern language that describes their calculation, I'd be very grateful. I'd prefer python or one of the C* languages.

My progress so far:

Complete:

  • Easter: can be found with python-dateutil.
  • Hanukkah, and other Hebrew calendar holidays can be found using holiday.py from python date_util*s* .

Close:

  • Ramadan: date_utils has an islamic calendar function in a module, which I tried and get mostly correct, but not entirely accurate results. (It wants to convert everything to and from julian dates, I'm not sure why).


    import calendar_util as cu        # from date utils; no jokes from .br
    #
    # year = int(sys.argv1) or datetime.now().year
    tj = cu.gregorian_to_jd(year, 1, 1)   # this year in julian
    ti = cu.jd_to_islamic(tj)             # this year in islamic cal
    rj = cu.islamic_to_jd(ti[0], 9, 1)    # first day of ramadan in julian
    #
    print cu.jd_to_gregorian(rj)          # first day of ramadan in greg
    # output: (2011, 8, 1)                # correct

I get results one-day-off in 2014, 2016, as confirmed by this site, although it mentions "North American sightings" a bit vague for my taste. Would appreciate pointers to increase accuracy, such as if Jan 1 hits some yearly cut off date. Islamic years are shorter and therefore arrive earlier every year.

Nada:

  • Chinese New Year: I haven't found anything but this discussion where the answer is not revealed.

To SO's credit I've found a link to this book for $30, but would rather not pay and dig that much for a few lines of code. In the discussion on Amazon, it mentions that the routines are all in Emacs calendar. However I've never even installed Emacs, so if it is the only code available, hopefully someone can help me dig it out and simplify it to give me Ramadan/Chinese NY for the current year.

Not sure if it is useful here, but sun and moon events are available via pyephem, which I'm already using. Maybe it can be of use also.

like image 721
Gringo Suave Avatar asked Jun 28 '11 07:06

Gringo Suave


2 Answers

Even though I do not have code for you I can provide information which may get you some progress.

First, Julian Date makes sense as the basis for calculating lunar and lunisolar dates because it ticks in SI seconds as measured on the geoid. Formulae for predicting the number of SI seconds between these events can thereby be easily correlated. http://en.wikipedia.org/wiki/Julian_day

Lunisolar holidays, and planning the Hebrew calendar in general, depend on the correlation of the moon's phase and the solar weekday. In the Hebrew and Islamic calendars, the day begins at sunset. So if the moon reaches 100% phase near sunset, that can affect when the holidays occur and how the calendar is planned into the future. But what time is sunset? That depends not only on the longitude but also on the latitude of observation. Therefore you must plan lunisolar calendars and holidays from a particular location on the geoid. http://en.wikipedia.org/wiki/Jewish_calendar#Principles

These factors led to the use of simplified ecclesiastical systems, of which there are several in wide use in the world, for the "computus" of the date of Easter, used instead of direct astronomical observation. http://en.wikipedia.org/wiki/Computus

The requirements of the traditional Islamic calendar, being defined in terms of astronomical observations including sunset, will therefore need to be tied to a particular location on the geoid if you intend to predict holiday dates and time intervals in SI seconds indefinitely into the past or future. This could be the reason for the remarks on "North American sightings" and the errors in future holiday dates that you mentioned; if the observation of moon phase vs solar day is made in North America, rather than say Mecca, the calendar will be planned much differently.

One inevitable and unpredictable factor in the prediction of any astronomical ephemera, and therefore lunar or lunisolar calendar events, is the change in the Earth's rotation over time. There is a simple formula for estimating this change, but the actual is measured using signals from extremely distant celestial objects. http://en.wikipedia.org/wiki/Leap_second Due to this uncertainty, and to the sensitivity of certain calendars to the correlation between lunar phase and solar ephemeris, there is always a horizon in the (distant?) future for these calendars, past which the sequence of dates is unknowable.

PyEphem looks like a very cool package and I will check it out myself. If its built-in logic does not meet your needs you are in for quite a time working out how to predict celestial events indefinitely into the future. Here's a taste: http://en.wikipedia.org/wiki/Precession_of_the_equinoxes#Values

Again, I don't have any specific code for you, but I hope this information is not repeating what you already know and is helpful to you.

like image 181
wberry Avatar answered Sep 27 '22 18:09

wberry


I have coded both of these, using existing libraries.

Here is the code for Ramadan:

from convertdate import islamic

def ramadan(year):
    islamic_year = islamic.from_gregorian(year, 1, 1)[0]
    result = islamic.to_gregorian(islamic_year, 9, 1)
    if result[0] < year:
        result = islamic.to_gregorian(islamic_year+1, 9, 1)
    elif result[0] > year:
        result = islamic.to_gregorian(islamic_year-1, 9, 1)
    return date(*result)

Here is the code for Chinese New Year:

from lunardate import LunarDate

_yot = ('Rat', 'Ox', 'Tiger', 'Rabbit', 'Dragon', 'Snake', 'Horse', 'Goat', 'Monkey', 'Rooster', 'Dog', 'Pig')

def year_of_the(year):
    yr = (year - 2020) % len(_yot)
    return _yot[yr]

def chinese_new_year(year):
    """Returns a tuple of the (date, year_of_the: str)"""
    return (LunarDate(year, 1, 1).toSolarDate(), year_of_the(year))

I opened an enhancement request for convertdate to add them: https://github.com/fitnr/convertdate/issues/24

like image 24
snoopyjc Avatar answered Sep 27 '22 18:09

snoopyjc