001/*
002 * Zmanim Java API
003 * Copyright (C) 2011-2025 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 - 2025
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 calculations.*/
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         * Default constructor.
049         */
050        public YomiCalculator() {
051                // nothing here
052        }
053
054        /**
055         * Returns the <a href="http://en.wikipedia.org/wiki/Daf_yomi">Daf Yomi</a> <a
056         * href="http://en.wikipedia.org/wiki/Talmud">Bavli</a> {@link Daf} for a given date. The first Daf Yomi cycle
057         * started on Rosh Hashana 5684 (September 11, 1923) and calculations prior to this date will result in an
058         * IllegalArgumentException thrown. For historical calculations (supported by this method), it is important to note
059         * that a change in length of the cycle was instituted starting in the eighth Daf Yomi cycle beginning on June 24,
060         * 1975. The Daf Yomi Bavli cycle has a single masechta of the Talmud Yerushalmi - Shekalim as part of the cycle.
061         * Unlike the Bavli where the number of daf per masechta was standardized since the original <a
062         * href="http://en.wikipedia.org/wiki/Daniel_Bomberg">Bomberg Edition</a> published from 1520 - 1523, there is no
063         * uniform page length in the Yerushalmi. The early cycles had the Yerushalmi Shekalim length of 13 days following the
064         * <a href=
065         * "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>
066         * Shas used by <a href="http://en.wikipedia.org/wiki/Meir_Shapiro">Rabbi Meir Shapiro</a>. With the start of the eighth Daf Yomi
067         * cycle beginning on June 24, 1975, the length of the Yerushalmi Shekalim was changed from 13 to 22 daf to follow
068         * the <a href="https://en.wikipedia.org/wiki/Vilna_Edition_Shas">Vilna Shas</a> that is in common use today.
069         * 
070         * @param jewishCalendar
071         *            The JewishCalendar date for calculation. TODO: this can be changed to use a regular GregorianCalendar since
072         *            there is nothing specific to the JewishCalendar in this class.
073         * @return the {@link Daf}.
074         * 
075         * @throws IllegalArgumentException
076         *             if the date is prior to the September 11, 1923, the start date of the first Daf Yomi cycle.
077         */
078        public static Daf getDafYomiBavli(JewishCalendar jewishCalendar) {
079                /*
080                 * The number of daf per masechta. Since the number of blatt in Shekalim changed on the 8th Daf Yomi cycle
081                 * beginning on June 24, 1975, from 13 to 22, the actual calculation for blattPerMasechta[4] will later be
082                 * adjusted based on the cycle.
083                 */
084                int[] blattPerMasechta = { 64, 157, 105, 121, 22, 88, 56, 40, 35, 31, 32, 29, 27, 122, 112, 91, 66, 49, 90, 82,
085                                119, 119, 176, 113, 24, 49, 76, 14, 120, 110, 142, 61, 34, 34, 28, 22, 4, 9, 5, 73 };
086                Calendar calendar = jewishCalendar.getGregorianCalendar();
087
088                Daf dafYomi = null;
089                int julianDay = getJulianDay(calendar);
090                int cycleNo;
091                int dafNo;
092                if (calendar.before(dafYomiStartDay)) {
093                        // TODO: should we return a null or throw an IllegalArgumentException?
094                        throw new IllegalArgumentException(calendar + " is prior to organized Daf Yomi Bavli cycles that started on "
095                                        + dafYomiStartDay);
096                }
097                if (calendar.equals(shekalimChangeDay) || calendar.after(shekalimChangeDay)) {
098                        cycleNo = 8 + ((julianDay - shekalimJulianChangeDay) / 2711);
099                        dafNo = ((julianDay - shekalimJulianChangeDay) % 2711);
100                } else {
101                        cycleNo = 1 + ((julianDay - dafYomiJulianStartDay) / 2702);
102                        dafNo = ((julianDay - dafYomiJulianStartDay) % 2702);
103                }
104
105                int total = 0;
106                int masechta = -1;
107                int blatt;
108
109                // Fix Shekalim for old cycles.
110                if (cycleNo <= 7) {
111                        blattPerMasechta[4] = 13;
112                }
113                
114                // Finally find the daf.
115                for (int i : blattPerMasechta) {
116                        masechta++;
117                        total = total + i - 1;
118                        if (dafNo < total) {
119                                blatt = 1 + i - (total - dafNo);
120                                // Fiddle with the weird ones near the end.
121                                if (masechta == 36) {
122                                    blatt += 21;
123                                } else if (masechta == 37) {
124                                    blatt += 24;
125                                } else if (masechta == 38) {
126                                    blatt += 32;
127                                }
128                                dafYomi = new Daf(masechta, blatt);
129                                break;
130                        }
131                }
132
133                return dafYomi;
134        }
135
136        /**
137         * Return the <a href="http://en.wikipedia.org/wiki/Julian_day">Julian day</a> from a Java Calendar.
138         * 
139         * @param calendar
140         *            The Java Calendar of the date to be calculated
141         * @return the Julian day number corresponding to the date
142         */
143        private static int getJulianDay(Calendar calendar) {
144                int year = calendar.get(Calendar.YEAR);
145                int month = calendar.get(Calendar.MONTH) + 1;
146                int day = calendar.get(Calendar.DAY_OF_MONTH);
147                if (month <= 2) {
148                        year -= 1;
149                        month += 12;
150                }
151                int a = year / 100;
152                int b = 2 - a + a / 4;
153                return (int) (Math.floor(365.25 * (year + 4716)) + Math.floor(30.6001 * (month + 1)) + day + b - 1524.5);
154        }
155}