001/*
002 * Zmanim Java API
003 * Copyright (C) 2017 - 2021 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 - 2021
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_MASSECTA = { 
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.
049         * 
050         * @param calendar
051         *            the calendar date for calculation
052         * @return the {@link Daf}.
053         * 
054         * @throws IllegalArgumentException
055         *             if the date is prior to the February 2, 1980, the start of the first Daf Yomi Yerushalmi cycle
056         */
057        public static Daf getDafYomiYerushalmi(JewishCalendar calendar) {
058                
059                Calendar nextCycle = new GregorianCalendar();
060                Calendar prevCycle = new GregorianCalendar();
061                Calendar requested = calendar.getGregorianCalendar();
062                int masechta = 0;
063                Daf dafYomi = null;
064                
065                // There isn't Daf Yomi in Yom Kippur and Tisha Beav.
066                if ( calendar.getYomTovIndex() == JewishCalendar.YOM_KIPPUR ||
067                         calendar.getYomTovIndex() == JewishCalendar.TISHA_BEAV ) {
068                        return new Daf(39,0);
069                }
070                
071                
072                if (requested.before(DAF_YOMI_START_DAY)) {
073                        throw new IllegalArgumentException(requested + " is prior to organized Daf Yomi Yerushlmi cycles that started on "
074                                        + DAF_YOMI_START_DAY);
075                }
076                
077                // Start to calculate current cycle. init the start day
078                nextCycle.setTime(DAF_YOMI_START_DAY.getTime());
079                
080                // Go cycle by cycle, until we get the next cycle
081                while (requested.after(nextCycle)) {
082                        prevCycle.setTime(nextCycle.getTime());
083                        
084                        // Adds the number of whole shas dafs. and the number of days that not have daf.
085                        nextCycle.add(Calendar.DAY_OF_MONTH, WHOLE_SHAS_DAFS);
086                        nextCycle.add(Calendar.DAY_OF_MONTH, getNumOfSpecialDays(prevCycle, nextCycle));                
087                }
088                
089                // Get the number of days from cycle start until request.
090                int dafNo = (int)(getDiffBetweenDays(prevCycle, requested));
091                
092                // Get the number of special day to subtract
093                int specialDays = getNumOfSpecialDays(prevCycle, requested);
094                int total = dafNo - specialDays;
095                                
096                // Finally find the daf.
097                for (int j = 0; j < BLATT_PER_MASSECTA.length; j++) {
098                        
099                        if (total <= BLATT_PER_MASSECTA[j]) {
100                                dafYomi = new Daf(masechta, total + 1);
101                                break;
102                        }
103                        total -= BLATT_PER_MASSECTA[j];
104                        masechta++;
105                }
106
107                return dafYomi;
108        }
109        
110        /**
111         * Return the number of special days (Yom Kippur and Tisha Beav) That there is no Daf in this days.
112         * From the last given number of days until given date
113         * 
114         * @param start start date to calculate
115         * @param end end date to calculate
116         * @return the number of special days
117         */
118        private static int getNumOfSpecialDays(Calendar start, Calendar end) {
119                
120                // Find the start and end Jewish years
121                int startYear = new JewishCalendar(start).getJewishYear();
122                int endYear = new JewishCalendar(end).getJewishYear();
123                
124                // Value to return
125                int specialDays = 0;
126                
127                //Instant of special Dates
128                JewishCalendar yom_kippur = new JewishCalendar(5770, 7, 10);
129                JewishCalendar tisha_beav = new JewishCalendar(5770, 5, 9);
130
131                // Go over the years and find special dates
132                for (int i = startYear; i <= endYear; i++) {
133                        yom_kippur.setJewishYear(i);
134                        tisha_beav.setJewishYear(i);
135                        
136                        if (isBetween(start, yom_kippur.getGregorianCalendar(), end)) {
137                                specialDays++;
138                        }
139                        if (isBetween(start, tisha_beav.getGregorianCalendar(), end)) {
140                                specialDays++;
141                        }
142                }
143                
144                return specialDays;
145        }
146
147        /**
148         * Return if the date is between two dates
149         * 
150         * @param start the start date
151         * @param date the date being compared
152         * @param end the end date
153         * @return if the date is between the start and end dates
154         */
155        private static boolean isBetween( Calendar start, Calendar date, Calendar end ) {
156                return start.before(date) && end.after(date);
157        }
158        
159        /**
160         * Return the number of days between the dates passed in
161         * @param start the start date
162         * @param end the end date
163         * @return the number of days between the start and end dates
164         */
165        private static long getDiffBetweenDays(Calendar start, Calendar end) {
166                return  ( end.getTimeInMillis() - start.getTimeInMillis()) / DAY_MILIS;
167        }
168}