// http://ethiopic.org/Calendars/EthiopicCalendar.java package org.ethiopic; public class EthiopicCalendar { /* ** ******************************************************************************** ** Era Definitions and Private Data ** ******************************************************************************** */ public static final int JD_EPOCH_OFFSET_AMETE_ALEM = -285019; // ዓ/ዓ public static final int JD_EPOCH_OFFSET_AMETE_MIHRET = 1723856; // ዓ/�? public static final int JD_EPOCH_OFFSET_COPTIC = 1824665; public static final int JD_EPOCH_OFFSET_GREGORIAN = 1721426; public static final int JD_EPOCH_OFFSET_UNSET = -1; private int jdOffset = JD_EPOCH_OFFSET_UNSET; private int year = -1; private int month = -1; private int day = -1; private boolean dateIsUnset = true; /* ** ******************************************************************************** ** Constructors ** ******************************************************************************** */ public EthiopicCalendar() { } public EthiopicCalendar(int year, int month, int day, int era) throws java.lang.ArithmeticException { this.set(year, month, day, era); } public EthiopicCalendar(int year, int month, int day) { this.set(year, month, day); } /* ** ******************************************************************************** ** Simple Setters and Getters ** ******************************************************************************** */ public void set(int year, int month, int day, int era) throws java.lang.ArithmeticException { this.year = year; this.month = month; this.day = day; this.setEra(era); this.dateIsUnset = false; } public void set(int year, int month, int day) { this.year = year; this.month = month; this.day = day; this.dateIsUnset = false; } public int getDay() { return day; } public int getMonth() { return month; } public int getYear() { return year; } public int getEra() { return jdOffset; } public int[] getDate() { int date[] = {year, month, day, jdOffset}; return date; } public void setEra(int era) throws java.lang.ArithmeticException { if ((JD_EPOCH_OFFSET_AMETE_ALEM == era) || (JD_EPOCH_OFFSET_AMETE_MIHRET == era)) { jdOffset = era; } else { throw (new java.lang.ArithmeticException("Unknown era: " + era + " must be either ዓ/ዓ or ዓ/�?.")); } } public boolean isEraSet() { return (JD_EPOCH_OFFSET_UNSET == jdOffset) ? false : true; } public void unsetEra() { jdOffset = JD_EPOCH_OFFSET_UNSET; } public void unset() { unsetEra(); year = -1; month = -1; day = -1; dateIsUnset = true; } public boolean isDateSet() { return (dateIsUnset) ? false : true; } /* ** ******************************************************************************** ** Conversion Methods To/From the Ethiopic & Gregorian Calendars ** ******************************************************************************** */ public int[] ethiopicToGregorian(int era) throws java.lang.ArithmeticException { if (!isDateSet()) { throw (new java.lang.ArithmeticException("Unset date.")); } return ethiopicToGregorian(this.year, this.month, this.day, era); } public int[] ethiopicToGregorian(int year, int month, int day, int era) throws java.lang.ArithmeticException { setEra(era); int[] date = ethiopicToGregorian(year, month, day); unsetEra(); return date; } public int[] ethiopicToGregorian() throws java.lang.ArithmeticException { if (dateIsUnset) { throw (new java.lang.ArithmeticException("Unset date.")); } return ethiopicToGregorian(this.year, this.month, this.day); } public int[] ethiopicToGregorian(int year, int month, int day) { if (!isEraSet()) { if (year <= 0) { setEra(JD_EPOCH_OFFSET_AMETE_ALEM); } else { setEra(JD_EPOCH_OFFSET_AMETE_MIHRET); } } int jdn = ethiopicToJDN(year, month, day); return jdnToGregorian(jdn); } public int[] gregorianToEthiopic() throws java.lang.ArithmeticException { if (dateIsUnset) { throw (new java.lang.ArithmeticException("Unset date.")); } return gregorianToEthiopic(this.year, this.month, this.day); } public int[] gregorianToEthiopic(int year, int month, int day) { int jdn = gregorianToJDN(year, month, day); return jdnToEthiopic(jdn, guessEraFromJDN(jdn)); } /* ** ******************************************************************************** ** Conversion Methods To/From the Julian Day Number ** ******************************************************************************** */ private static int nMonths = 12; private static int monthDays[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; private int quotient(long i, long j) { return (int) Math.floor((double) i / j); } private int mod(long i, long j) { return (int) (i - (j * quotient(i, j))); } private int guessEraFromJDN(int jdn) { return (jdn >= (JD_EPOCH_OFFSET_AMETE_MIHRET + 365)) ? JD_EPOCH_OFFSET_AMETE_MIHRET : JD_EPOCH_OFFSET_AMETE_ALEM; } private boolean isGregorianLeap(int year) { return (year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)); } public int[] jdnToGregorian(int j) { int r2000 = mod((j - JD_EPOCH_OFFSET_GREGORIAN), 730485); int r400 = mod((j - JD_EPOCH_OFFSET_GREGORIAN), 146097); int r100 = mod(r400, 36524); int r4 = mod(r100, 1461); int n = mod(r4, 365) + 365 * quotient(r4, 1460); int s = quotient(r4, 1095); int aprime = 400 * quotient((j - JD_EPOCH_OFFSET_GREGORIAN), 146097) + 100 * quotient(r400, 36524) + 4 * quotient(r100, 1461) + quotient(r4, 365) - quotient(r4, 1460) - quotient(r2000, 730484); int year = aprime + 1; int t = quotient((364 + s - n), 306); int month = t * (quotient(n, 31) + 1) + (1 - t) * (quotient((5 * (n - s) + 13), 153) + 1); /* int day = t * ( n - s - 31*month + 32 ) + ( 1 - t ) * ( n - s - 30*month - quotient((3*month - 2), 5) + 33 ) ; */ // int n2000 = quotient( r2000, 730484 ); n += 1 - quotient(r2000, 730484); int day = n; if ((r100 == 0) && (n == 0) && (r400 != 0)) { month = 12; day = 31; } else { monthDays[2] = (isGregorianLeap(year)) ? 29 : 28; for (int i = 1; i <= nMonths; ++i) { if (n <= monthDays[i]) { day = n; break; } n -= monthDays[i]; } } int out[] = {year, month, day}; return out; } public int gregorianToJDN(int year, int month, int day) { int s = quotient(year, 4) - quotient(year - 1, 4) - quotient(year, 100) + quotient(year - 1, 100) + quotient(year, 400) - quotient(year - 1, 400); int t = quotient(14 - month, 12); int n = 31 * t * (month - 1) + (1 - t) * (59 + s + 30 * (month - 3) + quotient((3 * month - 7), 5)) + day - 1; int j = JD_EPOCH_OFFSET_GREGORIAN + 365 * (year - 1) + quotient(year - 1, 4) - quotient(year - 1, 100) + quotient(year - 1, 400) + n; return j; } public int[] jdnToEthiopic(int jdn) { return (isEraSet()) ? jdnToEthiopic(jdn, jdOffset) : jdnToEthiopic(jdn, guessEraFromJDN(jdn)); } public int[] jdnToEthiopic(int jdn, int era) { long r = mod((jdn - era), 1461); long n = mod(r, 365) + 365 * quotient(r, 1460); int year = 4 * quotient((jdn - era), 1461) + quotient(r, 365) - quotient(r, 1460); int month = quotient(n, 30) + 1; int day = mod(n, 30) + 1; return new int[]{year, month, day}; } public int ethiopicToJDN() throws java.lang.ArithmeticException { if (dateIsUnset) { throw (new java.lang.ArithmeticException("Unset date.")); } return ethiopicToJDN(this.year, this.month, this.day); } /** * Computes the Julian day number of the given Coptic or Ethiopic date. This * method assumes that the JDN epoch offset has been set. This method is * called by copticToGregorian and ethiopicToGregorian which will set the * jdn offset context. * * @param year a year in the Ethiopic calendar * @param month a month in the Ethiopic calendar * @param date a date in the Ethiopic calendar * * @return The Julian Day Number (JDN) */ private int ethCopticToJDN(int year, int month, int day, int era) { int jdn = (era + 365) + 365 * (year - 1) + quotient(year, 4) + 30 * month + day - 31; return jdn; } public int ethiopicToJDN(int year, int month, int day) { return (isEraSet()) ? ethCopticToJDN(year, month, day, jdOffset) : ethCopticToJDN(year, month, day, JD_EPOCH_OFFSET_AMETE_MIHRET); } public int ethiopicToJDN(int era) throws java.lang.ArithmeticException { return ethiopicToJDN(year, month, day, era); } public int ethiopicToJDN(int year, int month, int day, int era) throws java.lang.ArithmeticException { return ethCopticToJDN(year, month, day, era); } /* ** ******************************************************************************** ** Methods for the Coptic Calendar ** ******************************************************************************** */ public int[] copticToGregorian() throws java.lang.ArithmeticException { if (dateIsUnset) { throw (new java.lang.ArithmeticException("Unset date.")); } return copticToGregorian(this.year, this.month, this.day); } public int[] copticToGregorian(int year, int month, int day) { setEra(JD_EPOCH_OFFSET_COPTIC); int jdn = ethiopicToJDN(year, month, day); return jdnToGregorian(jdn); } public int[] gregorianToCoptic() throws java.lang.ArithmeticException { if (dateIsUnset) { throw (new java.lang.ArithmeticException("Unset date.")); } return gregorianToCoptic(this.year, this.month, this.day); } public int[] gregorianToCoptic(int year, int month, int day) { setEra(JD_EPOCH_OFFSET_COPTIC); int jdn = gregorianToJDN(year, month, day); return jdnToEthiopic(jdn); } public int copticToJDN(int year, int month, int day) { return ethCopticToJDN(year, month, day, JD_EPOCH_OFFSET_COPTIC); } }