001/*
002 * Zmanim Java API
003 * Copyright (C) 2011-2020 Eliyahu Hershfeld
004 *
005 * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General
006 * Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option)
007 * any later version.
008 *
009 * This library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied
010 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
011 * details.
012 * You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to
013 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA,
014 * or connect to: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
015 */
016package com.kosherjava.zmanim.hebrewcalendar;
017
018import java.util.Calendar;
019import java.util.GregorianCalendar;
020
021/**
022 * This class calculates the Daf Yomi Bavli page (daf) for a given date. To calculate Daf Yomi Yerushalmi
023 * use the {@link YerushalmiYomiCalculator}. The library may cover Mishna Yomi etc. at some point in the future.
024 * 
025 * @author © Bob Newell (original C code)
026 * @author © Eliyahu Hershfeld 2011 - 2020
027 */
028public class YomiCalculator {
029
030        /**
031         * The start date of the first Daf Yomi Bavli cycle of September 11, 1923 / Rosh Hashana 5684.
032         */
033        private static final Calendar dafYomiStartDay = new GregorianCalendar(1923, Calendar.SEPTEMBER, 11);
034        /** The start date of the first Daf Yomi Bavli cycle in the Julian calendar. Used internally for claculations.*/
035        private static final int dafYomiJulianStartDay = getJulianDay(dafYomiStartDay);
036        /**
037         * The date that the pagination for the Daf Yomi <em>Maseches Shekalim</em> changed to use the commonly used Vilna
038         * Shas pagination from the no longer commonly available Zhitomir / Slavuta Shas used by Rabbi Meir Shapiro. 
039         */
040        private static final Calendar shekalimChangeDay = new GregorianCalendar(1975, Calendar.JUNE, 24);
041        
042        /** The Julian date that the cycle for Shekalim changed.
043         * @see #getDafYomiBavli(JewishCalendar) for details.
044         */
045        private static final int shekalimJulianChangeDay = getJulianDay(shekalimChangeDay);
046
047        /**
048         * Returns the <a href="http://en.wikipedia.org/wiki/Daf_yomi">Daf Yomi</a> <a
049         * href="http://en.wikipedia.org/wiki/Talmud">Bavli</a> {@link Daf} for a given date. The first Daf Yomi cycle
050         * started on Rosh Hashana 5684 (September 11, 1923) and calculations prior to this date will result in an
051         * IllegalArgumentException thrown. For historical calculations (supported by this method), it is important to note
052         * that a change in length of the cycle was instituted starting in the eighth Daf Yomi cycle beginning on June 24,
053         * 1975. The Daf Yomi Bavli cycle has a single masechta of the Talmud Yerushalmi - Shekalim as part of the cycle.
054         * Unlike the Bavli where the number of daf per masechta was standardized since the original <a
055         * href="http://en.wikipedia.org/wiki/Daniel_Bomberg">Bomberg Edition</a> published from 1520 - 1523, there is no
056         * uniform page length in the Yerushalmi. The early cycles had the Yerushalmi Shekalim length of 13 days following the
057         * <a href=
058         * "https://he.wikipedia.org/wiki/%D7%93%D7%A4%D7%95%D7%A1_%D7%A1%D7%9C%D7%90%D7%95%D7%95%D7%99%D7%98%D7%90">Slavuta/Zhytomyr</a>
059         * Shas used by <a href="http://en.wikipedia.org/wiki/Meir_Shapiro">Rabbi Meir Shapiro</a>. With the start of the eighth Daf Yomi
060         * cycle beginning on June 24, 1975 the length of the Yerushalmi Shekalim was changed from 13 to 22 daf to follow
061         * the <a href="https://en.wikipedia.org/wiki/Vilna_Edition_Shas">Vilna Shas</a> that is in common use today.
062         * 
063         * @param jewishCalendar
064         *            The JewishCalendar date for calculation. TODO: this can be changed to use a regular GregorianCalendar since
065         *            there is nothing specific to the JewishCalendar in this class.
066         * @return the {@link Daf}.
067         * 
068         * @throws IllegalArgumentException
069         *             if the date is prior to the September 11, 1923 start date of the first Daf Yomi cycle
070         */
071        public static Daf getDafYomiBavli(JewishCalendar jewishCalendar) {
072                /*
073                 * The number of daf per masechta. Since the number of blatt in Shekalim changed on the 8th Daf Yomi cycle
074                 * beginning on June 24, 1975 from 13 to 22, the actual calculation for blattPerMasechta[4] will later be
075                 * adjusted based on the cycle.
076                 */
077                int[] blattPerMasechta = { 64, 157, 105, 121, 22, 88, 56, 40, 35, 31, 32, 29, 27, 122, 112, 91, 66, 49, 90, 82,
078                                119, 119, 176, 113, 24, 49, 76, 14, 120, 110, 142, 61, 34, 34, 28, 22, 4, 9, 5, 73 };
079                Calendar calendar = jewishCalendar.getGregorianCalendar();
080
081                Daf dafYomi = null;
082                int julianDay = getJulianDay(calendar);
083                int cycleNo = 0;
084                int dafNo = 0;
085                if (calendar.before(dafYomiStartDay)) {
086                        // TODO: should we return a null or throw an IllegalArgumentException?
087                        throw new IllegalArgumentException(calendar + " is prior to organized Daf Yomi Bavli cycles that started on "
088                                        + dafYomiStartDay);
089                }
090                if (calendar.equals(shekalimChangeDay) || calendar.after(shekalimChangeDay)) {
091                        cycleNo = 8 + ((julianDay - shekalimJulianChangeDay) / 2711);
092                        dafNo = ((julianDay - shekalimJulianChangeDay) % 2711);
093                } else {
094                        cycleNo = 1 + ((julianDay - dafYomiJulianStartDay) / 2702);
095                        dafNo = ((julianDay - dafYomiJulianStartDay) % 2702);
096                }
097
098                int total = 0;
099                int masechta = -1;
100                int blatt = 0;
101
102                // Fix Shekalim for old cycles.
103                if (cycleNo <= 7) {
104                        blattPerMasechta[4] = 13;
105                } else {
106                        blattPerMasechta[4] = 22; // correct any change that may have been changed from a prior calculation
107                }
108                // Finally find the daf.
109                for (int j = 0; j < blattPerMasechta.length; j++) {
110                        masechta++;
111                        total = total + blattPerMasechta[j] - 1;
112                        if (dafNo < total) {
113                                blatt = 1 + blattPerMasechta[j] - (total - dafNo);
114                                // Fiddle with the weird ones near the end.
115                                if (masechta == 36) {
116                                        blatt += 21;
117                                } else if (masechta == 37) {
118                                        blatt += 24;
119                                } else if (masechta == 38) {
120                                        blatt += 32;
121                                }
122                                dafYomi = new Daf(masechta, blatt);
123                                break;
124                        }
125                }
126
127                return dafYomi;
128        }
129
130        /**
131         * Return the <a href="http://en.wikipedia.org/wiki/Julian_day">Julian day</a> from a Java Calendar.
132         * 
133         * @param calendar
134         *            The Java Calendar of the date to be calculated
135         * @return the Julian day number corresponding to the date
136         */
137        private static int getJulianDay(Calendar calendar) {
138                int year = calendar.get(Calendar.YEAR);
139                int month = calendar.get(Calendar.MONTH) + 1;
140                int day = calendar.get(Calendar.DAY_OF_MONTH);
141                if (month <= 2) {
142                        year -= 1;
143                        month += 12;
144                }
145                int a = year / 100;
146                int b = 2 - a + a / 4;
147                return (int) (Math.floor(365.25 * (year + 4716)) + Math.floor(30.6001 * (month + 1)) + day + b - 1524.5);
148        }
149}