001/*
002 * Zmanim Java API
003 * Copyright (C) 2017 - 2023 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/**
023 * This class calculates the <a href="https://en.wikipedia.org/wiki/Jerusalem_Talmud">Talmud Yerusalmi</a> <a href=
024 * "https://en.wikipedia.org/wiki/Daf_Yomi">Daf Yomi</a> page ({@link Daf}) for the a given date.
025 * 
026 * @author &copy; elihaidv
027 * @author &copy; Eliyahu Hershfeld 2017 - 2023
028 */
029public class YerushalmiYomiCalculator {
030        
031        /**
032         * The start date of the first Daf Yomi Yerushalmi cycle of February 2, 1980 / 15 Shevat, 5740.
033         */
034        private final static Calendar DAF_YOMI_START_DAY = new GregorianCalendar(1980, Calendar.FEBRUARY, 2);
035        /** The number of milliseconds in a day. */
036        private final static int DAY_MILIS = 1000 * 60 * 60 * 24;
037        /** The number of pages in the Talmud Yerushalmi.*/
038        private final static int WHOLE_SHAS_DAFS = 1554;
039        /** The number of pages per <em>masechta</em> (tractate).*/
040        private final static int[] BLATT_PER_MASECHTA = { 
041                        68, 37, 34, 44, 31, 59, 26, 33, 28, 20, 13, 92, 65, 71, 22, 22, 42, 26, 26, 33, 34, 22,
042                        19, 85, 72, 47, 40, 47, 54, 48, 44, 37, 34, 44, 9, 57, 37, 19, 13};
043
044        /**
045         * Returns the <a href="https://en.wikipedia.org/wiki/Daf_Yomi">Daf Yomi</a>
046         * <a href="https://en.wikipedia.org/wiki/Jerusalem_Talmud">Yerusalmi</a> page ({@link Daf}) for a given date.
047         * The first Daf Yomi cycle started on 15 Shevat (Tu Bishvat), 5740 (February, 2, 1980) and calculations
048         * prior to this date will result in an IllegalArgumentException thrown. A null will be returned on Tisha B'Av or
049         * Yom Kippur.
050         *
051         * @param calendar
052         *            the calendar date for calculation
053         * @return the {@link Daf} or null if the date is on Tisha B'Av or Yom Kippur.
054         *
055         * @throws IllegalArgumentException
056         *             if the date is prior to the February 2, 1980, the start of the first Daf Yomi Yerushalmi cycle
057         */
058        public static Daf getDafYomiYerushalmi(JewishCalendar calendar) {
059                
060                Calendar nextCycle = new GregorianCalendar();
061                Calendar prevCycle = new GregorianCalendar();
062                Calendar requested = calendar.getGregorianCalendar();
063                int masechta = 0;
064                Daf dafYomi = null;
065
066                // There isn't Daf Yomi on Yom Kippur or Tisha B'Av.
067                if ( calendar.getYomTovIndex() == JewishCalendar.YOM_KIPPUR ||
068                                calendar.getYomTovIndex() == JewishCalendar.TISHA_BEAV ) {
069                        return null;
070                }
071                
072                
073                if (requested.before(DAF_YOMI_START_DAY)) {
074                        throw new IllegalArgumentException(requested + " is prior to organized Daf Yomi Yerushalmi cycles that started on "
075                                        + DAF_YOMI_START_DAY);
076                }
077                
078                // Start to calculate current cycle. init the start day
079                nextCycle.setTime(DAF_YOMI_START_DAY.getTime());
080                
081                // Go cycle by cycle, until we get the next cycle
082                while (requested.after(nextCycle)) {
083                        prevCycle.setTime(nextCycle.getTime());
084                        
085                        // Adds the number of whole shas dafs. and the number of days that not have daf.
086                        nextCycle.add(Calendar.DAY_OF_MONTH, WHOLE_SHAS_DAFS);
087                        nextCycle.add(Calendar.DAY_OF_MONTH, getNumOfSpecialDays(prevCycle, nextCycle));                
088                }
089                
090                // Get the number of days from cycle start until request.
091                int dafNo = (int)(getDiffBetweenDays(prevCycle, requested));
092                
093                // Get the number of special day to subtract
094                int specialDays = getNumOfSpecialDays(prevCycle, requested);
095                int total = dafNo - specialDays;
096                                
097                // Finally find the daf.
098                for (int j = 0; j < BLATT_PER_MASECHTA.length; j++) {
099                        
100                        if (total < BLATT_PER_MASECHTA[j]) {
101                                dafYomi = new Daf(masechta, total + 1);
102                                break;
103                        }
104                        total -= BLATT_PER_MASECHTA[j];
105                        masechta++;
106                }
107
108                return dafYomi;
109        }
110        
111        /**
112         * Return the number of special days (Yom Kippur and Tisha Beav) That there is no Daf in this days.
113         * From the last given number of days until given date
114         * 
115         * @param start start date to calculate
116         * @param end end date to calculate
117         * @return the number of special days
118         */
119        private static int getNumOfSpecialDays(Calendar start, Calendar end) {
120                
121                // Find the start and end Jewish years
122                int startYear = new JewishCalendar(start).getJewishYear();
123                int endYear = new JewishCalendar(end).getJewishYear();
124                
125                // Value to return
126                int specialDays = 0;
127                
128                //Instant of special Dates
129                JewishCalendar yom_kippur = new JewishCalendar(5770, 7, 10);
130                JewishCalendar tisha_beav = new JewishCalendar(5770, 5, 9);
131
132                // Go over the years and find special dates
133                for (int i = startYear; i <= endYear; i++) {
134                        yom_kippur.setJewishYear(i);
135                        tisha_beav.setJewishYear(i);
136                        
137                        if (isBetween(start, yom_kippur.getGregorianCalendar(), end)) {
138                                specialDays++;
139                        }
140                        if (isBetween(start, tisha_beav.getGregorianCalendar(), end)) {
141                                specialDays++;
142                        }
143                }
144                
145                return specialDays;
146        }
147
148        /**
149         * Return if the date is between two dates
150         * 
151         * @param start the start date
152         * @param date the date being compared
153         * @param end the end date
154         * @return if the date is between the start and end dates
155         */
156        private static boolean isBetween( Calendar start, Calendar date, Calendar end ) {
157                return start.before(date) && end.after(date);
158        }
159        
160        /**
161         * Return the number of days between the dates passed in
162         * @param start the start date
163         * @param end the end date
164         * @return the number of days between the start and end dates
165         */
166        private static long getDiffBetweenDays(Calendar start, Calendar end) {
167                return  ( end.getTimeInMillis() - start.getTimeInMillis()) / DAY_MILIS;
168        }
169}