001/*
002 * Zmanim Java API
003 * Copyright © 2004-2026 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.
007 *
008 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
009 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
010 * details.
011 * You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to
012 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA,
013 * or connect to: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
014 */
015package com.kosherjava.zmanim;
016
017import java.time.Duration;
018import java.time.Instant;
019import java.time.LocalDate;
020import java.util.Objects;
021
022import com.kosherjava.zmanim.hebrewcalendar.JewishCalendar;
023import com.kosherjava.zmanim.util.AstronomicalCalculator;
024import com.kosherjava.zmanim.util.GeoLocation;
025
026/**
027 * The ZmanimCalendar is a specialized calendar that can calculate sunrise, sunset and Jewish <em>zmanim</em> (religious times)
028 * for prayers and other Jewish religious duties. This class contains the main functionality of the <em>Zmanim</em> library. For
029 * a much more extensive list of <em>zmanim</em>, use the {@link ComprehensiveZmanimCalendar} that extends this class. See
030 * documentation for the {@link ComprehensiveZmanimCalendar} and {@link AstronomicalCalendar} for simple examples on using the API. 
031 * <strong>Elevation based <em>zmanim</em> (even sunrise and sunset) should not be used <em>lekula</em> without the guidance
032 * of a <em>posek</em></strong>. According to Rabbi Dovid Yehudah Bursztyn in his <a href="https://www.worldcat.org/oclc/1158574217"
033 * >Zmanim Kehilchasam, 7th edition</a> chapter 2, section 7 (pages 181-182) and section 9 (pages 186-187), no <em>zmanim</em>
034 * besides sunrise and sunset should use elevation. However, Rabbi Yechiel Avrahom Zilber in the <a href=
035 * "https://hebrewbooks.org/51654">Birur Halacha Vol. 6</a> Ch. 58 Pages <a href=
036 * "https://hebrewbooks.org/pdfpager.aspx?req=51654&amp;pgnum=42">34</a> and <a href=
037 * "https://hebrewbooks.org/pdfpager.aspx?req=51654&amp;pgnum=50">42</a> is of the opinion that elevation should be accounted for
038 * in <em>zmanim</em> calculations. Related to this, Rabbi Yaakov Karp in <a href="https://www.worldcat.org/oclc/919472094"
039 * >Shimush Zekeinim</a>, Ch. 1, page 17 states that obstructing horizons should be factored into <em>zmanim</em> calculations. The
040 * setting defaults to false (elevation will not be used for <em>zmanim</em> calculations besides sunrise and sunset), unless the
041 * setting is changed to true in {@link #setUseElevation(boolean)}. This will impact sunrise and sunset-based <em>zmanim</em> such as
042 * {@link #getSunrise()}, {@link #getSunset()}, {@link #getSofZmanShmaGRA()}, <em>alos</em>-based
043 * <em>zmanim</em> such as {@link #getSofZmanShmaMGA72Minutes()} that are based on a fixed offset of sunrise or sunset and
044 * <em>zmanim</em> based on a percentage of the day such as {@link ComprehensiveZmanimCalendar#getSofZmanShmaMGA90MinutesZmanis()}
045 * that are based on sunrise and sunset. Even when set to true it will not impact <em>zmanim</em> that are a degree-based offset of
046 * sunrise and sunset, such as {@link ComprehensiveZmanimCalendar#getSofZmanShmaMGA16Point1Degrees()} or {@link
047 * ComprehensiveZmanimCalendar#getSofZmanShmaBaalHatanya()} since these <em>zmanim</em> are not linked to sunrise or sunset times
048 * (the calculations are based on the astronomical definition of sunrise and sunset calculated in a vacuum with the solar radius
049 * above the horizon), and are therefore not impacted by the use of elevation. For additional information on the <em>halachic</em>
050 * impact of elevation on <em>zmanim</em> see:
051 * <ul>
052 * <li><a href="https://www.nli.org.il/en/books/NNL_ALEPH002542826/NLI">Zmanei Halacha Lema'aseh</a> 4th edition by <a href=
053 * "http://beinenu.com/rabbis/%D7%94%D7%A8%D7%91-%D7%99%D7%93%D7%99%D7%93%D7%99%D7%94-%D7%9E%D7%A0%D7%AA">Rabbi Yedidya Manat</a>.
054 * See section 1, pages 11-12 for a very concise write-up, with details in section 2, pages 37 - 63 and 133 - 151.</li>
055 * <li><a href="https://www.worldcat.org/oclc/1158574217">Zmanim Kehilchasam</a> 7th edition, by Rabbi Dovid Yehuda Burstein,  vol 1,
056 * chapter 2, pages 95 - 188.</li>
057 * <li><a href="https://www.worldcat.org/oclc/36089452">Hazmanim Bahalacha</a> by Rabbi Chaim Benish , perek 7, pages 53 - 63.</li>
058 * </ul>
059 * 
060 * <p><b>Note:</b> It is important to read the technical notes on top of the {@link AstronomicalCalculator} documentation
061 * before using this code.
062 * <p>I would like to thank <a href="https://www.worldcat.org/search?q=au%3AShakow%2C+Yaakov">Rabbi Yaakov Shakow</a>, the
063 * author of Luach Ikvei Hayom who spent a considerable amount of time reviewing, correcting and making suggestions on the
064 * documentation in this library.
065 * <h2>Disclaimer:</h2> I did my best to get accurate results, but please double-check before relying on these
066 * <em>zmanim</em> for <em>halacha lema'aseh</em>.
067 * 
068 * @author © Eliyahu Hershfeld 2004 - 2026
069 */
070public class ZmanimCalendar extends AstronomicalCalendar {
071        
072        /**
073         * Is elevation factored in for some <em>zmanim</em> (see {@link #isUseElevation()} for additional information).
074         * @see #isUseElevation()
075         * @see #setUseElevation(boolean)
076         */
077        private boolean useElevation;
078
079        /**
080         * Is elevation above sea level calculated for times besides sunrise and sunset. According to Rabbi Dovid Yehuda Bursztyn in
081         * his <a href="https://www.worldcat.org/oclc/659793988">Zmanim Kehilchasam (second edition published in 2007)</a> chapter 2
082         * (pages 186-187) no <em>zmanim</em> besides sunrise and sunset should use elevation. However, Rabbi Yechiel Avrahom Zilber
083         * in the <a href="https://hebrewbooks.org/51654">Birur Halacha Vol. 6</a> Ch. 58 Pages <a href=
084         * "https://hebrewbooks.org/pdfpager.aspx?req=51654&amp;pgnum=42">34</a> and <a href=
085         * "https://hebrewbooks.org/pdfpager.aspx?req=51654&amp;pgnum=50">42</a> is of the opinion that elevation should be accounted
086         * for in <em>zmanim</em> calculations. Related to this, Rabbi Yaakov Karp in <a href=
087         * "https://www.worldcat.org/oclc/919472094">Shimush Zekeinim</a>, Ch. 1, page 17 states that obstructing horizons should be
088         * factored into <em>zmanim</em> calculations.The setting defaults to false (elevation will not be used for <em>zmanim</em>
089         * calculations), unless the setting is changed to true in {@link #setUseElevation(boolean)}. This will impact sunrise and
090         * sunset based <em>zmanim</em> such as  {@link #getSofZmanShmaGRA()}, alos based <em>zmanim</em> such as {@link
091         * getSofZmanShmaMGA72Minutes()} that are based on a fixed offset of sunrise or sunset and <em>zmanim</em> based on a
092         * percentage of the day such as {@link ComprehensiveZmanimCalendar#getSofZmanShmaMGA90MinutesZmanis()} that are based on
093         * sunrise and sunset. It will not impact <em>zmanim</em> that are a degree based offset of sunrise and sunset, such as {@link
094         * ComprehensiveZmanimCalendar#getSofZmanShmaMGA16Point1Degrees()} or {@link
095         * ComprehensiveZmanimCalendar#getSofZmanShmaBaalHatanya()}.
096         * 
097         * @return if the use of elevation is active
098         * 
099         * @see #setUseElevation(boolean)
100         */
101        public boolean isUseElevation() {
102                return useElevation;
103        }
104
105        /**
106         * Sets whether elevation above sea level is factored into <em>zmanim</em> calculations for times besides sunrise and sunset.
107         * See {@link #isUseElevation()} for more details. 
108         * @see #isUseElevation()
109         * 
110         * @param useElevation set to true to use elevation in <em>zmanim</em> calculations
111         */
112        public void setUseElevation(boolean useElevation) {
113                this.useElevation = useElevation;
114        }
115        
116        /**
117         * Is astronomical <em>chatzos</em> used for <em>zmanim</em> calculations. The default value of <code>true</code> will
118         * keep the standard astronomical <em>chatzos</em> calculation, while setting it to <code>false</code> will use half of
119         * a solar day / night calculation for <em>chatzos</em>.
120         * @see #isUseAstronomicalChatzos()
121         * @see #isUseAstronomicalChatzosForOtherZmanim()
122         * @see #setUseAstronomicalChatzos(boolean)
123         * @see #getChatzosHayom()
124         * @see #getChatzosHalayla()
125         * @see #getSunTransit()
126         * @see #getChatzosHayomAsHalfDay()
127         */
128        private boolean useAstronomicalChatzos = true;
129        
130        /**
131         * Is {@link #getSunTransit() astronomical <em>chatzos</em>} used for {@link #getChatzosHayom()} and {@link #getChatzosHalayla()}
132         * for enhanced accuracy. For example as the day lengthens, the second half of the day is longer than the first and astronomical
133         * <em>chatzos</em> would be a drop earlier than half of the time between sunrise and sunset.
134         *  
135         * @todo In the future, if this is set to true, the following may change to enhance accuracy. {@link #getSofZmanShmaGRA()
136         * <em>Sof zman Shma</em> GRA} would be calculated as 3 <em>shaos zmaniyos</em> after sunrise, but the <em>shaos
137         * zmaniyos</em> would be calculated a a 6th of the time between sunrise and <em>chatzos</em>, as opposed to a 12th of the
138         * time between sunrise and sunset. {@link #getMinchaGedolaGRA() <em>mincha gedola</em>} will be calculated as half a
139         * <em>shaah zmanis</em> of afternoon hours (a 6th of the time between <em>chatzos</em> and sunset after astronomical
140         * <em>chatzos</em> as opposed to 6.5 <em>shaos zmaniyos</em> after sunrise. {@link #getPlagHaminchaGRA() <em>Plag
141         * hamincha</em>} would be calculated as 4.75 <em>shaos zmaniyos</em> after astronomical <em>chatzos</em> as opposed to 10.75
142         * <em>shaos zmaniyos</em> after sunrise. Etc.
143         * 
144         * @return if the use of astronomical <em>chatzos</em> is active.
145         * @see #isUseAstronomicalChatzos()
146         * @see #setUseAstronomicalChatzos(boolean)
147         * @see #getChatzosHayom()
148         * @see #getChatzosHalayla()
149         * @see #getSunTransit()
150         * @see #getChatzosHayomAsHalfDay()
151         * @see #isUseAstronomicalChatzosForOtherZmanim()
152         */
153        public boolean isUseAstronomicalChatzos() {
154                return useAstronomicalChatzos;
155        }
156
157        /**
158         * Sets if astronomical <em>chatzos</em> should be used in calculations of other <em>zmanim</em> for enhanced accuracy.
159         * @param useAstronomicalChatzos set to true to use astronomical in <em>chatzos</em> in <em>zmanim</em> calculations.
160         * @see #isUseAstronomicalChatzos()
161         * @see #getChatzosHayom()
162         * @see #getChatzosHalayla()
163         * @see #getSunTransit()
164         * @see #getChatzosHayomAsHalfDay()
165         * @see #setUseAstronomicalChatzosForOtherZmanim(boolean)
166         */
167        public void setUseAstronomicalChatzos(boolean useAstronomicalChatzos) {
168                this.useAstronomicalChatzos = useAstronomicalChatzos;
169        }
170        
171        /**
172         * Is astronomical <em>chatzos</em> used for <em>zmanim</em> calculations besides <em>chatzos</em> itself for enhanced
173         * accuracy. The default value of <code>false</code> will keep the standard start to end of day calculations, while setting
174         * it to <code>true</code> will use half of a solar day calculation for <em>zmanim</em>.
175         * @see #isUseAstronomicalChatzosForOtherZmanim()
176         * @see #setUseAstronomicalChatzosForOtherZmanim(boolean)
177         * @see #isUseAstronomicalChatzos()
178         * @see #setUseAstronomicalChatzos(boolean)
179         * @see #getChatzosHayom()
180         * @see #getChatzosHalayla()
181         */
182        private boolean useAstronomicalChatzosForOtherZmanim = false;
183        
184        /**
185         * Is astronomical <em>chatzos</em> used for <em>zmanim</em> calculations besides <em>chatzos</em> itself for enhanced
186         * accuracy. For example as the day is lengthening (as we approach spring season), the second half of the day is longer than
187         * the first and astronomical <em>chatzos</em> would be a drop earlier than half of the time between sunrise and sunset.
188         * Conversely, the second half of the day would be shorter in the autumn as the days start getting shorter.
189         * 
190         * @todo In the future, if this is set to true, the following may change to enhance accuracy. {@link #getSofZmanShmaGRA()
191         * <em>Sof zman Shma</em> GRA} would be calculated as 3 <em>shaos zmaniyos</em> after sunrise, but the <em>shaos
192         * zmaniyos</em> would be calculated a a 6th of the time between sunrise and <em>chatzos</em>, as opposed to a 12th of the
193         * time between sunrise and sunset. {@link #getMinchaGedolaGRA() <em>mincha gedola GRA</em>} will be calculated as half a
194         * <em>shaah zmanis</em> of afternoon hours (a 6th of the time between <em>chatzos</em> and sunset after astronomical
195         * <em>chatzos</em> as opposed to 6.5 <em>shaos zmaniyos</em> after sunrise. {@link #getPlagHaminchaGRA() <em>Plag
196         * hamincha</em>} would be calculated as 4.75 <em>shaos zmaniyos</em> after astronomical <em>chatzos</em> as opposed to 10.75
197         * <em>shaos zmaniyos</em> after sunrise. Etc.
198         * 
199         * @return if the use of astronomical <em>chatzos</em> is active.
200         * @see #setUseAstronomicalChatzosForOtherZmanim(boolean)
201         * @see #isUseAstronomicalChatzos()
202         * @see #setUseAstronomicalChatzos(boolean)
203         */
204        public boolean isUseAstronomicalChatzosForOtherZmanim() {
205                return useAstronomicalChatzosForOtherZmanim;
206        }
207
208        /**
209         * Sets if astronomical <em>chatzos</em> should be used in calculations of other <em>zmanim</em> for enhanced accuracy.
210         * @param useAstronomicalChatzosForOtherZmanim set to true to use astronomical in <em>chatzos</em> in <em>zmanim</em> calculations.
211         * @see #isUseAstronomicalChatzos()
212         */
213        public void setUseAstronomicalChatzosForOtherZmanim(boolean useAstronomicalChatzosForOtherZmanim) {
214                this.useAstronomicalChatzosForOtherZmanim = useAstronomicalChatzosForOtherZmanim;
215        }
216
217        /**
218         * The zenith of 16.1° below geometric zenith (90°).
219         * @see #getAlos16Point1Degrees()
220         * @see ComprehensiveZmanimCalendar#getTzais16Point1Degrees()
221         * @see ComprehensiveZmanimCalendar#getSofZmanShmaMGA16Point1Degrees()
222         * @see ComprehensiveZmanimCalendar#getSofZmanTfilaMGA16Point1Degrees()
223         * @see ComprehensiveZmanimCalendar#getMinchaGedola16Point1Degrees()
224         * @see ComprehensiveZmanimCalendar#getMinchaKetana16Point1Degrees()
225         * @see ComprehensiveZmanimCalendar#getPlagHamincha16Point1Degrees()
226         * 
227         * @see ComprehensiveZmanimCalendar#getSofZmanShmaAlos16Point1DegreesToTzaisGeonim7Point083Degrees()
228         * @see ComprehensiveZmanimCalendar#getPlagAlos16Point1DegreesToTzaisGeonim7Point083Degrees()
229         * @see ComprehensiveZmanimCalendar#getSofZmanShmaAlos16Point1ToSunset()
230         * @see ComprehensiveZmanimCalendar#getShaahZmanis16Point1Degrees()
231         * @see ComprehensiveZmanimCalendar#getShaahZmanisAlos16Point1DegreesToTzaisGeonim3Point7Degrees()
232         * @see ComprehensiveZmanimCalendar#getShaahZmanisAlos16Point1DegreesToTzaisGeonim3Point8Degrees()
233         * @see ComprehensiveZmanimCalendar#getShaahZmanisAlos16Point1DegreesToTzaisGeonim7Point083Degrees()
234         * 
235         */
236        protected static final double ZENITH_16_POINT_1 = GEOMETRIC_ZENITH + 16.1;
237
238        /**
239         * The zenith of 8.5° below geometric zenith (90°).
240         * @see #getTzaisGeonim8Point5Degrees()
241         */
242        protected static final double ZENITH_8_POINT_5 = GEOMETRIC_ZENITH + 8.5;
243        
244        /**
245         * The zenith of 1.583° below {@link AstronomicalCalendar#GEOMETRIC_ZENITH} (90°).
246         * @see #getSunriseBaalHatanya()
247         * @see #getSunsetBaalHatanya()
248         */
249        protected static final double ZENITH_1_POINT_583 = GEOMETRIC_ZENITH + 1.583;
250
251        /**
252         * The default <em>Shabbos</em> candle lighting offset is 18 minutes. This can be changed via the
253         * {@link #setCandleLightingOffset(double)} and retrieved by the {@link #getCandleLightingOffset()}.
254         */
255        private double candleLightingOffset = 18;
256        
257        /**
258         * This method will return {@link #getSeaLevelSunrise() sea level sunrise} if {@link #isUseElevation()} is false (the default),
259         * or elevation adjusted {@link #getSunrise()} if it is true. This allows relevant <em>zmanim</em> in this and
260         * extending classes (such as the {@link ComprehensiveZmanimCalendar}) to automatically adjust to the elevation setting.
261         * 
262         * @return {@link #getSeaLevelSunrise()} if {@link #isUseElevation()} is false (the default), or elevation adjusted
263         *         {@link #getSunrise()} if it is true.
264         * @see #getSunrise()
265         */
266        protected Instant getSunriseBasedOnElevationSetting() {
267                if (isUseElevation()) {
268                        return super.getSunrise();
269                }
270                return getSeaLevelSunrise();
271        }
272        
273        /**
274         * This method will return {@link #getSeaLevelSunset() sea level sunset} if {@link #isUseElevation()} is false (the default), or
275         * elevation adjusted {@link #getSunset()} if it is true. This allows relevant <em>zmanim</em>in this and extending classes (such
276         * as the {@link ComprehensiveZmanimCalendar}) to automatically adjust to the elevation setting.
277         * 
278         * @return {@link #getSeaLevelSunset()} if {@link #isUseElevation()} is false (the default), or elevation adjusted
279         *         {@link #getSunset()} if it is true.
280         * @see #getSunset()
281         */
282        protected Instant getSunsetBasedOnElevationSetting() {
283                if (isUseElevation()) {
284                        return super.getSunset();
285                }
286                return getSeaLevelSunset();
287        }
288
289        /**
290         * A method that returns <em>tzais</em> (nightfall) when the sun is {@link ZENITH_8_POINT_5 8.5°} below the {@link
291         * AstronomicalCalendar#GEOMETRIC_ZENITH} (90°) after {@link #getSunset() sunset}, a time that Rabbi Meir Posen in his the <em><a
292         * href="https://www.worldcat.org/oclc/29283612">Ohr Meir</a></em> calculated that 3 small stars are visible, which is later than
293         * the required 3 medium stars. This calculation is based on the sun's position below the horizon 36 minutes after {@link
294         * getSeaLevelSunrise() sunset} in Jerusalem <a href="https://kosherjava.com/2022/01/12/equinox-vs-equilux-zmanim-calculations/"
295         * >around the equinox / equilux</a>, which is 8.5° below {@link AstronomicalCalendar#GEOMETRIC_ZENITH}.
296         * 
297         * @return The <code>Instant</code> of nightfall. If the calculation can't be computed such as northern and southern locations
298         *         even south of the Arctic Circle and north of the Antarctic Circle where the sun may not reach low enough below the
299         *         horizon for this calculation, a <code>null</code> will be returned. See detailed explanation on top of the {@link
300         *         AstronomicalCalendar} documentation.
301         * ComprehensiveZmanimCalendar#getTzaisGeonim8Point5Degrees() that returns an identical time to this generic <em>tzais</em>
302         */
303        public Instant getTzaisGeonim8Point5Degrees() {
304                return getSunsetOffsetByDegrees(ZENITH_8_POINT_5);
305        }
306
307        /**
308         * Returns <em>alos</em> (dawn) based on the time when the sun is {@link ZENITH_16_POINT_1 16.1°} below the eastern {@link
309         * AstronomicalCalendar#GEOMETRIC_ZENITH} before {@link #getSunrise() sunrise}. This is based on the calculation that the time
310         * between dawn and sunrise (and sunset to nightfall) is 72 minutes, the time that is takes to walk 4 <a href=
311         * "https://en.wikipedia.org/wiki/Biblical_mile">mil</a> at 18 minutes a mil (<a href="https://en.wikipedia.org/wiki/Maimonides"
312         * >Rambam</a> and others). The sun's position below the horizon 72 minutes before {@link #getSunrise() sunrise} in Jerusalem on
313         * the <a href="https://kosherjava.com/2022/01/12/equinox-vs-equilux-zmanim-calculations/">around the equinox / equilux</a> is
314         * 16.1° below {@link AstronomicalCalendar#GEOMETRIC_ZENITH}.
315         * 
316         * @see ComprehensiveZmanimCalendar#getTzais16Point1Degrees()
317         * 
318         * @return The <code>Instant</code> of dawn. If the calculation can't be computed such as northern and southern
319         *         locations even south of the Arctic Circle and north of the Antarctic Circle where the sun may not reach
320         *         low enough below the horizon for this calculation, a <code>null</code> will be returned. See detailed
321         *         explanation on top of the {@link AstronomicalCalendar} documentation.
322         */
323        public Instant getAlos16Point1Degrees() {
324                return getSunriseOffsetByDegrees(ZENITH_16_POINT_1);
325        }
326
327        /**
328         * Method to return <em>alos</em> (dawn) calculated as 72 minutes before {@link #getSunrise() sunrise} or {@link
329         * #getSeaLevelSunrise() sea level sunrise} (depending on the {@link #isUseElevation()} setting). This time
330         * is based on the time to walk the distance of 4 <a href="https://en.wikipedia.org/wiki/Biblical_mile">mil</a> at 18 minutes a mil. The
331         * 72-minute time (but not the concept of fixed minutes) is based on the opinion that the time of the <em>Neshef</em>
332         * (twilight between dawn and sunrise) does not vary by the time of year or location but depends on the time it takes
333         * to walk the distance of 4 mil.
334         * 
335         * @return the <code>Instant</code> representing the time. If the calculation can't be computed such as in the Arctic
336         *         Circle where there is at least one day a year where the sun does not rise, and one where it does not set,
337         *         a <code>null</code> will be returned. See detailed explanation on top of the {@link AstronomicalCalendar}
338         *         documentation.
339         */
340        public Instant getAlos72Minutes() {
341                return getTimeOffset(getSunriseBasedOnElevationSetting(), Duration.ofMinutes(-72));
342        }
343        
344        /**
345         * This method returns {@link #getSunTransit() Astronomical <em>chatzos hayom</em>} if the
346         * {@link com.kosherjava.zmanim.util.AstronomicalCalculator calculator} class used supports it and
347         * {@link #isUseAstronomicalChatzos() isUseAstronomicalChatzos()} is set to <em>true</em> or the {@link #getChatzosHayomAsHalfDay()
348         * halfway point between sunrise and sunset} if it does not support it, or it is not configured to use it. There are currently
349         * two {@link com.kosherjava.zmanim.util.AstronomicalCalculator calculators} available in the API, the default {@link
350         * com.kosherjava.zmanim.util.NOAACalculator NOAA calculator} and the {@link com.kosherjava.zmanim.util.SunTimesCalculator USNO
351         * calculator}. The USNO calculator calculates <em>chatzos</em> as halfway between sunrise and sunset (identical to six <em>shaos
352         * zmaniyos</em> after sunrise), while the NOAACalculator calculates it more accurately as {@link #getSunTransit() astronomical
353         * <em>chatzos</em>}. See <a href="https://kosherjava.com/2020/07/02/definition-of-chatzos/">The Definition of <em>Chatzos</em></a>
354         * for a detailed explanation of the ways to calculate <em>Chatzos</em>. Since half-day <em>chatzos</em> can be <code>null</code> in
355         * the Arctic on a day when either sunrise or sunset did not happen and astronomical <em>chatzos</em> can be calculated even in the
356         * Arctic, if half-day <em>chatzos</em> calculates as <code>null</code> and astronomical <em>chatzos</em> is supported by the
357         * calculator, astronomical <em>chatzos</em> will be returned to avoid returning a <code>null</code>.
358         * 
359         * @see #getSunTransit()
360         * @see #getChatzosHayomAsHalfDay()
361         * @see #isUseAstronomicalChatzos()
362         * @see #setUseAstronomicalChatzos(boolean)
363         * @return the <code>Instant</code> of <em>chatzos</em>. If the calculation can't be computed such as in the Arctic Circle
364         *         where there is at least one day where the sun does not rise, and one where it does not set, and the calculator does not
365         *         support astronomical calculations (that will never report a <code>null</code>) a <code>null</code> will be returned.
366         *         See detailed explanation on top of the {@link AstronomicalCalendar} documentation.
367         */
368        public Instant getChatzosHayom() {
369                if (isUseAstronomicalChatzos()) {
370                        return getSunTransit(); // can be null if the calculator does not support astronomical chatzos
371                } else {
372                        Instant halfDayChatzos = getChatzosHayomAsHalfDay();
373                        if (halfDayChatzos == null) {
374                                return getSunTransit(); // can be null if the calculator does not support astronomical chatzos
375                        } else {
376                                return halfDayChatzos;
377                        }
378                }
379        }
380        
381        /**
382         * A method that returns <em>chatzos halayla</em> at the <b>end of the day</b> (the last <em>zman</em> of the day
383         * returned by the calendar, that may actually be after midnight of the day it is being calculated for). For example, if
384         * calculating it for the date of <em>Erev Pesach</em>, the calculation will be for <em>Lail Pesach</em> to allow you to use the
385         * <em>zman</em> as <em>sof zman achilas afikoman</em>. {@link #getSolarMidnight() Astronomical <em>chatzos halayla</em>} will be
386         * returned if the {@link com.kosherjava.zmanim.util.AstronomicalCalculator calculator} class used supports it and {@link
387         * isUseAstronomicalChatzos() isUseAstronomicalChatzos()} is set to <em>true</em>. Otherwise the {@link #getChatzos(Instant,
388         * Instant) halfway point} between sunset and the following day's sunrise, if it does not support it, or it is not configured to
389         * use it. There are currently two {@link com.kosherjava.zmanim.util.AstronomicalCalculator calculators} available in the API,
390         * the default {@link com.kosherjava.zmanim.util.NOAACalculator NOAA calculator} and the {@link
391         * com.kosherjava.zmanim.util.SunTimesCalculator USNO calculator}. The USNO calculator calculates <em>chatzos halayla</em> as
392         * halfway between sunset and the following day's sunrise (identical to six <em>shaos zmaniyos</em> after sunset), while the
393         * NOAACalculator calculates it more accurately as {@link #getSolarMidnight() astronomical <em>chatzos halayla</em>}. See <a href=
394         * "https://kosherjava.com/2020/07/02/definition-of-chatzos/">The Definition of <em>Chatzos</em></a> for a detailed explanation
395         * of the ways to calculate <em>Chatzos</em>. Since half-night <em>chatzos</em> can be <code>null</code> in the Arctic on a day
396         * when either sunset or the following day's sunrise did not happen and astronomical <em>chatzos halayla</em> can be calculated
397         * even in the Arctic, if half-day <em>chatzos</em> calculates as <code>null</code> and astronomical <em>chatzos</em> is
398         * supported by the calculator, astronomical <em>chatzos</em> will be returned to avoid returning a <code>null</code>.
399         * 
400         * @see #getSolarMidnight()
401         * @see #getChatzos(Instant, Instant)
402         * @see #isUseAstronomicalChatzos()
403         * @see #setUseAstronomicalChatzos(boolean)
404         * @return the <code>Instant</code> of <em>chatzos halayla</em> at the <b>end of the current day</b>. If the calculation can't be
405         *         computed such as in the Arctic Circle where there is at least one day where the sun does not rise, and one where it
406         *         does not set, and the calculator does not support astronomical calculations (that will never report a
407         *         <code>null</code>) a <code>null</code> will be returned. See detailed explanation on top of the
408         *         {@link AstronomicalCalendar} documentation.
409         */
410        public Instant getChatzosHalayla() {
411                if (isUseAstronomicalChatzos()) {
412                        return getSolarMidnight(); // can be null if the calculator does not support astronomical chatzos
413                } else {
414                        ZmanimCalendar clonedCalendar = (ZmanimCalendar)this.clone();
415                        clonedCalendar.setLocalDate(getLocalDate().plusDays(1));
416                        Instant halfNightChatzos = getChatzos(getSeaLevelSunset(), clonedCalendar.getSeaLevelSunrise());
417                        if (halfNightChatzos == null) {
418                                return getSolarMidnight(); // can be null if the calculator does not support astronomical chatzos
419                        } else {
420                                return halfNightChatzos;
421                        }
422                }
423        }
424        
425        /**
426         * A method that returns <em>chatzos</em> (<em>hayom</em> or <em>layla</em>) calculated as halfway between the begin and end
427         * times passed in. If sunrise and sunset (or sunset and the following sunrise for <em>chatzos halayla</em>) are passed in,
428         * the <em>zman</em> returned will be close to, but not exactly, when the Sun is <a href=
429         * "https://en.wikipedia.org/wiki/Transit_%28astronomy%29">transiting</a> the <a href=
430         * "https://en.wikipedia.org/wiki/Meridian_%28astronomy%29">celestial meridian</a> due to changes in declination (the
431         * lengthening or shortening day). See <a href="https://kosherjava.com/2020/07/02/definition-of-chatzos/">The Definition of
432         * <em>Chatzos</em></a> for a detailed explanation of the ways to calculate <em>Chatzos</em>. This method is a convenience
433         * method that calls the parent class's {@link #getSunTransit(Instant, Instant)}. A practical example of using this would be
434         * calculating <em>chatzos halayla</em> for the end of <em>zman achilas afikoman</em> on <em>Pesach</em> night, where
435         * <a href="https://en.wikipedia.org/wiki/Shlomo_Zalman_Auerbach">Rav Shlomo Zalman Auerbach</a> in the Halichos Shlomo,
436         * Moadim Nisan - Av, ch. 9, no. 44, pages 289-292, questioned the common practice of considering <em>chatzos halayla</em> as
437         * astronomical <em>chatzos halayla</em>, and felt that for this it should be at <em>chatzi halayla</em>, halfway between sunset
438         * and <em>alos</em> or <em>bedieved</em> from an early <em>tzais</em> that is lower (degree-wise) than <em>alos</em> the next
439         * morning, thus making this <em>zman</em> significantly earlier. <a href="https://en.wikipedia.org/wiki/Moshe_Sternbuch">Rav
440         * Moshe Sternbuch</a> in <a href="https://hebrewbooks.org/pdfpager.aspx?req=69091&pgnum=15">Teshuvos Vehanhagos v. VII, ch 1, p.
441         * 3</a>, agreed to this calculation for <em>chatzos halayla</em> on <em>Pesach</em> night.
442         * 
443         * @param begin the beginning of day or night for calculating <em>chatzos</em>. For <em>chatzos hayom</em>, this can be sea level
444         *         sunrise, or any arbitrary sunrise or <em>alos</em> used as the beginning of the day, and for <em>chatzos halayla</em>
445         *         this can be sea level sunset, or any arbitrary sunset or <em>tzais</em> used as the beginning of the night.
446         * @param end the end of day or night for calculating <em>chatzos</em>. For <em>chatzos hayom</em>, this can be sea level sunset,
447         *         or any arbitrary sunset or <em>tzais</em> used as the end of day, and for <em>chatzos halayla</em> this can be sea
448         *         level sunrise, or any arbitrary sunrise or <em>alos</em> used as the end of the night.
449         * @return the <code>Instant</code> representing <em>chatzos</em>. If the calculation can't be computed such as in the
450         *         Arctic Circle where there is at least one day a year where the sun does not rise, and one where it does
451         *         not set, <code>null</code> will be returned. See detailed explanation on top of the page.
452         * @see #getSunTransit(Instant, Instant)
453         * @see #getChatzosHayomAsHalfDay()
454         * @see #getChatzosHayom()
455         * @see #getChatzosHalayla()
456         */
457        public Instant getChatzos(Instant begin, Instant end) {
458                return getSunTransit(begin, end);
459        }
460        
461        /**
462         * Returns <em>chatzos</em> calculated as halfway between sunrise and sunset. Many are of the opinion that
463         * <em>chatzos</em> is calculated as the midpoint between {@link #getSeaLevelSunrise() sea level sunrise} and
464         * {@link #getSeaLevelSunset() sea level sunset}, despite it not being the most accurate way to calculate it. A day
465         * starting at <em>alos</em> and ending at <em>tzais</em> using the same time or degree offset will also return
466         * the same time. In reality due to lengthening or shortening of day, this is not necessarily the exact midpoint of
467         * the day, but it is very close. This method allows you to use the NOAACalculator and still calculate <em>chatzos
468         * </em> as six <em>shaos zmaniyos</em> after sunrise. There are currently two {@link
469         * com.kosherjava.zmanim.util.AstronomicalCalculator calculators} available in the API, the {@link
470         * com.kosherjava.zmanim.util.NOAACalculator} and the {@link com.kosherjava.zmanim.util.SunTimesCalculator}.
471         * The SunTimesCalculator calculates <em>chatzos</em> as halfway between sunrise and sunset (and of six <em>shaos
472         * zmaniyos</em>), while the NOAACalculator calculates it as astronomical <em>chatzos</em> that is slightly more
473         * accurate. This method allows you to use the NOAACalculator and still calculate <em>chatzos</em> as six <em>shaos
474         * zmaniyos</em> after sunrise. See <a href="https://kosherjava.com/2020/07/02/definition-of-chatzos/">The Definition
475         * of <em>Chatzos</em></a> for a detailed explanation of the ways to calculate <em>Chatzos</em>.
476         *
477         * @return the <code>Instant</code> of the latest <em>chatzos</em>. If the calculation can't be computed such
478         *         as in the Arctic Circle where there is at least one day a year where the sun does not rise, and one where
479         *         it does not set, a <code>null</code> will be returned. See detailed explanation on top of the
480         *         {@link AstronomicalCalendar} documentation.
481         * @see com.kosherjava.zmanim.util.NOAACalculator#getUTCNoon(LocalDate, GeoLocation)
482         * @see com.kosherjava.zmanim.util.SunTimesCalculator#getUTCNoon(LocalDate, GeoLocation)
483         * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCNoon(LocalDate, GeoLocation)
484         * @see #getSunTransit(Instant, Instant)
485         * @see #getChatzosHayom()
486         * @see #getChatzosHalayla()
487         * @see #getChatzos(Instant, Instant)
488         * @see #getSunTransit()
489         * @see #isUseAstronomicalChatzos()
490         */
491        public Instant getChatzosHayomAsHalfDay() {
492                return getChatzos(getSeaLevelSunrise(), getSeaLevelSunset());
493        }
494
495        /**
496         * A generic method for calculating the latest <em>zman krias shema</em> (time to recite shema in the morning) that is 3 *
497         * <em>shaos zmaniyos</em> (temporal hours) after the start of the day, calculated using the start and end of the day passed
498         * to this method. The time from the start of day to the end of day are divided into 12 <em>shaos zmaniyos</em> (temporal
499         * hours), and the latest <em>zman krias shema</em> is calculated as 3 of those <em>shaos zmaniyos</em> after the beginning of
500         * the day. If {@link #isUseAstronomicalChatzosForOtherZmanim()} is <code>true</code>, the 3 <em>shaos zmaniyos</em> will be
501         * based on 1/6 of the time between sunrise and {@link #getSunTransit() astronomical <em>chatzos</em>}. As an example, passing
502         * {@link #getSunrise() sunrise} and {@link #getSunset() sunset} or {@link #getSeaLevelSunrise() sea level
503         * sunrise} and {@link #getSeaLevelSunset() sea level sunset} to this method (or {@link #getSunriseBasedOnElevationSetting()} and
504         * {@link #getSunsetBasedOnElevationSetting()} that is driven off the {@link #isUseElevation()} setting) will return <em>sof zman
505         * krias shema</em> according to the opinion of the <a href="https://en.wikipedia.org/wiki/Vilna_Gaon">GRA</a>. In cases where
506         * the start and end dates are not synchronous such as in {@link ComprehensiveZmanimCalendar
507         * #getSofZmanShmaAlos16Point1ToTzaisGeonim7Point083Degrees()} <code>false</code> should be passed to the synchronous parameter
508         * to ensure that {@link #isUseAstronomicalChatzosForOtherZmanim()} will not be used.
509         * 
510         * @param startOfDay the start of day for calculating <em>zman krias shema</em>. This can be sunrise or any <em>alos</em> passed
511         *         to this method.
512         * @param endOfDay the end of day for calculating <em>zman krias shema</em>. This can be sunset or any <em>tzais</em> passed to
513         *         this method.
514         * @param synchronous If the <em>zman</em> has a synchronous start and end of the day. If this is <code>false</code>, using a
515         *         {@link #isUseAstronomicalChatzosForOtherZmanim()} makes no sense and will be ignored even if set to true, since by
516         *         definition <em>chatzos</em> will not be the middle of the day for the <em>zman</em>.
517         * @see #isUseAstronomicalChatzosForOtherZmanim()
518         * @return the <code>Instant</code> of the latest <em>zman shema</em> based on the start and end of day times passed to this
519         *         method. If the calculation can't be computed such as in the Arctic Circle where there is at least one day
520         *         a year where the sun does not rise, and one where it does not set, a <code>null</code> will be returned. See
521         *         detailed explanation on top of the {@link AstronomicalCalendar} documentation.
522         */
523        public Instant getSofZmanShma(Instant startOfDay, Instant endOfDay, boolean synchronous) {
524                if (isUseAstronomicalChatzosForOtherZmanim() && synchronous) {
525                        return getHalfDayBasedZman(startOfDay, getChatzosHayom(), 3);
526                } else {
527                        return getShaahZmanisBasedZman(startOfDay, endOfDay, 3);
528                }
529        }
530        
531        /**
532         * A generic method for calculating the latest <em>zman krias shema</em> that calls {@link #getSofZmanShma(Instant, Instant, boolean)}
533         * passing <code>false</code> to the synchronous parameter since there is no way to know if the start and end of the day are 
534         * synchronous. Passing true when they are not synchronous is too much of a risk. See information on that method for more details.
535         * @param startOfDay the start of day for calculating <em>zman krias shema</em>. This can be sunrise or any <em>alos</em> passed
536         *         to this method.
537         * @param endOfDay the end of day for calculating <em>zman krias shema</em>. This can be sunset or any <em>tzais</em> passed to
538         *         this method.
539         * @return the <code>Instant</code> of the latest <em>zman shema</em> based on the start and end of day times passed to this
540         *         method. If the calculation can't be computed such as in the Arctic Circle where there is at least one day
541         *         a year where the sun does not rise, and one where it does not set, a <code>null</code> will be returned. See
542         *         detailed explanation on top of the {@link AstronomicalCalendar} documentation.
543         * @see #getSofZmanShma(Instant, Instant, boolean)
544         */
545        public Instant getSofZmanShma(Instant startOfDay, Instant endOfDay) {
546                return getSofZmanShma(startOfDay, endOfDay, false);
547        }
548
549        /**
550         * This method returns the latest <em>zman krias shema</em> (time to recite shema in the morning) that is 3 *
551         * {@link #getShaahZmanisGRA() <em>shaos zmaniyos</em>} (solar hours) after {@link #getSunrise() sunrise} or
552         * {@link #getSeaLevelSunrise() sea level sunrise} (depending on the {@link #isUseElevation()} setting), according
553         * to the <a href="https://en.wikipedia.org/wiki/Vilna_Gaon">GRA</a>. 
554         *  The day is calculated from {@link #getSeaLevelSunrise() sea level sunrise} to {@link #getSeaLevelSunset() sea level
555         *  sunset} or from {@link #getSunrise() sunrise} to {@link #getSunset() sunset} (depending on the
556         *  {@link #isUseElevation()} setting).
557         * 
558         * @see #getSofZmanShma(Instant, Instant)
559         * @see #getShaahZmanisGRA()
560         * @see #isUseElevation()
561         * @see ComprehensiveZmanimCalendar#getSofZmanShmaBaalHatanya()
562         * @return the <code>Instant</code> of the latest <em>zman shema</em> according to the GRA. If the calculation can't be
563         *         computed such as in the Arctic Circle where there is at least one day a year where the sun does not rise,
564         *         and one where it does not set, a <code>null</code> will be returned. See the detailed explanation on top
565         *         of the {@link AstronomicalCalendar} documentation.
566         */
567        public Instant getSofZmanShmaGRA() {
568                return getSofZmanShma(getSunriseBasedOnElevationSetting(), getSunsetBasedOnElevationSetting(), true);
569        }
570
571        /**
572         * This method returns the latest <em>zman krias shema</em> (time to recite Shema in the morning) according to the opinion of
573         * the <a href="https://en.wikipedia.org/wiki/Avraham_Gombiner">Magen Avraham (MGA)</a> based on <em>alos</em> being {@link
574         * getAlos72Minutes() 72} minutes before {@link #getSunrise() sunrise}. This time is 3 {@link
575         * getShaahZmanis72Minutes() <em>shaos zmaniyos</em>} (solar hours) after {@link #getAlos72Minutes() dawn} based on the opinion
576         * of the MGA that the day is calculated from a {@link #getAlos72Minutes() dawn} of 72 minutes before sunrise to
577         * {@link #getTzais72Minutes() nightfall} of 72 minutes after sunset. This returns the time of 3 * {@link
578         * getShaahZmanis72Minutes()} after {@link #getAlos72Minutes() dawn}.
579         * 
580         * @return the <code>Instant</code> of the latest <em>zman krias shema</em>. If the calculation can't be computed such
581         *         as in the Arctic Circle where there is at least one day a year where the sun does not rise, and one where
582         *         it does not set, a <code>null</code> will be returned. See detailed explanation on top of the
583         *         {@link AstronomicalCalendar} documentation.
584         * @see #isUseAstronomicalChatzosForOtherZmanim()
585         * @see #getShaahZmanis72Minutes()
586         * @see #getAlos72Minutes()
587         * @see #getSofZmanShmaMGA72Minutes()
588         * @see #getSofZmanShma(Instant, Instant, boolean)
589         */
590        public Instant getSofZmanShmaMGA72Minutes() {
591                return getSofZmanShma(getAlos72Minutes(), getTzais72Minutes(), true);
592        }
593
594        /**
595         * This method returns the <em>tzais</em> (nightfall) based on the opinion of <em>Rabbeinu Tam</em> that <em>tzais hakochavim</em>
596         * is calculated as 72 minutes after sunset, the time it takes to walk 4 <a href=
597         * "https://en.wikipedia.org/wiki/Biblical_mile">mil</a> at 18 minutes a mil. According to the <a href=
598         * "https://en.wikipedia.org/wiki/Samuel_Loew">Machtzis Hashekel</a> in Orach Chaim 235:3, the <a href=
599         * "https://en.wikipedia.org/wiki/Joseph_ben_Meir_Teomim">Pri Megadim</a> in Orach Chaim 261:2 (see the Biur Halacha) and others
600         * (see Hazmanim Bahalacha 17:3 and 17:5) the 72 minutes are standard clock minutes any time of the year in any location.
601         * Depending on the {@link #isUseElevation()} setting, a 72-minute offset from  either {@link #getSunset() sunset} or
602         * {@link #getSeaLevelSunset() sea level sunset} is used.
603         * 
604         * @return the <code>Instant</code> representing 72 minutes after sunset. If the calculation can't be
605         *         computed such as in the Arctic Circle where there is at least one day a year where the sun does not rise,
606         *         and one where it does not set, a <code>null</code> will be returned See detailed explanation on top of the
607         *         {@link AstronomicalCalendar} documentation.
608         * @see ComprehensiveZmanimCalendar#getTzais16Point1Degrees()
609         */
610        public Instant getTzais72Minutes() {
611                return getTimeOffset(getSunsetBasedOnElevationSetting(), Duration.ofMinutes(72));
612        }
613
614        /**
615         * A method to return candle lighting time, calculated as {@link #getCandleLightingOffset()} minutes before {@link
616         * #getSeaLevelSunset() sea level sunset}. This will return the time for any day of the week, since it can be used to calculate
617         * candle lighting time for <em>Yom Tov</em> (mid-week holidays) as well. Elevation adjustments are intentionally not performed
618         * by this method, but you can calculate it by passing the elevation adjusted sunset to {@link #getTimeOffset(Instant, Duration)}.
619         * 
620         * @return candle lighting time. If the calculation can't be computed such as in the Arctic Circle where there is at
621         *         least one day a year where the sun does not rise, and one where it does not set, a <code>null</code> will
622         *         be returned. See detailed explanation on top of the {@link AstronomicalCalendar} documentation.
623         * @see #getSeaLevelSunset()
624         * @see #getCandleLightingOffset()
625         * @see #setCandleLightingOffset(double)
626         */
627        public Instant getCandleLighting() {
628                long totalNanos = (long) (-getCandleLightingOffset() * MINUTE_NANOS);
629                return getTimeOffset(getSeaLevelSunset(), Duration.ofNanos(totalNanos));
630        }
631
632        /**
633         * A generic method for calculating the latest <em>zman tfilah</em> (time to recite the morning prayers) that is 4 * <em>shaos
634         * zmaniyos</em> (temporal hours) after the start of the day, calculated using the start and end of the day passed to this method.
635         * The time from the start of day to the end of day are divided into 12 <em>shaos zmaniyos</em> (temporal hours), and <em>sof
636         * zman tfila</em> is calculated as 4 of those <em>shaos zmaniyos</em> after the beginning of the day. As an example, passing
637         * {@link #getSunriseBasedOnElevationSetting()} and {@link #getSunsetBasedOnElevationSetting()} (depends on the {@link
638         * #isUseElevation()} elevation setting) to this method will return <em>zman tfilah</em> according to the opinion of the <a href=
639         * "https://en.wikipedia.org/wiki/Vilna_Gaon">GRA</a>. This method's synchronous parameter indicates if the start and end of day
640         * for the calculation are synchronous, having the same offset. This is typically the case, but some <em>zmanim</em> calculations
641         * are based on a start and end at different offsets from the real start and end of the day, such as starting the day at
642         * <em>alos</em> and an ending it at <em>tzais Geonim</em> or some other variant. If the day is not synchronous a {@link
643         * #getHalfDayBasedZman(Instant, Instant, double) half-day based calculations} will be bypassed. It would be illogical to use a
644         * half-day based calculation that start/end at <em>chatzos</em> when the two "halves" of the day are not equal, and the halfway
645         * point between them is not at <em>chatzos</em>.
646         * 
647         * @param startOfDay the start of day for calculating <em>zman tfilah</em>. This can be sunrise or any <em>alos</em> passed
648         *         to this method.
649         * @param endOfDay the end of day for calculating <em>zman tfilah</em>. This can be sunset or any <em>tzais</em> passed
650         *         to this method.
651         * @param synchronous If the <em>zman</em> has a synchronous start and end of the day. If this is <code>false</code>, using a
652         *         {@link #isUseAstronomicalChatzosForOtherZmanim()} makes no sense and will be ignored even if set to true, since by
653         *         definition <em>chatzos</em> will not be the middle of the day for the <em>zman</em>.
654         * @return the <code>Instant</code> of the latest <em>zman tfilah</em> based on the start and end of day times passed to this
655         *         method. If the calculation can't be computed such as in the Arctic Circle where there is at least one day a year where
656         *         the sun does not rise, and one where it does not set, a <code>null</code> will be returned. See detailed explanation
657         *         on top of the {@link AstronomicalCalendar} documentation.
658         */
659        public Instant getSofZmanTfila(Instant startOfDay, Instant endOfDay, boolean synchronous) {
660                if (isUseAstronomicalChatzosForOtherZmanim() && synchronous) {
661                        return getHalfDayBasedZman(startOfDay, getChatzosHayom(), 4);
662                } else {
663                        return getShaahZmanisBasedZman(startOfDay, endOfDay, 4);
664                }
665        }
666        
667        /**
668         * A generic method for calculating <em>sof zman biur chametz</em> or the latest time one is allowed burning
669         * <em>chametz</em> on <em>Erev Pesach</em> that is 5 * <em>shaos zmaniyos</em> (temporal hours) after the start of the
670         * day, calculated using the start and end of the day passed to this method. If the date is not <em>erev Pesach</em>, a
671         * null will be returned. The time from the start of day to the end of day is divided into 12 <em>shaos zmaniyos</em>
672         * (temporal hours), and <em>sof zman biur chametz</em> is calculated as 5 of those <em>shaos zmaniyos</em> after the
673         * beginning of the day. As an example, passing {@link #getSunrise() sunrise} and {@link #getSunset() sunset} to this
674         * method will return <em>sof zman biur chametz</em> according to the opinion of the <a href=
675         * "https://en.wikipedia.org/wiki/Vilna_Gaon">GRA</a>. This method's synchronous parameter indicates if the start
676         * and end of day for the calculation are synchronous, having the same offset. This is typically the case, but some
677         * <em>zmanim</em> calculations are based on a start and end at different offsets from the real start and end of the day,
678         * such as starting the day at <em>alos</em> and an ending it at <em>tzais Geonim</em> or some other variant. If the day
679         * is not synchronous a {@link #getHalfDayBasedZman(Instant, Instant, double) half-day based calculations} will be bypassed.
680         * It would be illogical to use a half-day based calculation that start/end at <em>chatzos</em> when the two "halves" of
681         * the day are not equal, and the halfway point between them is not at <em>chatzos</em>.
682         * 
683         * @param startOfDay the start of day for calculating <em>sof zman biur chametz</em>. This can be sunrise or any <em>alos</em>
684         *         passed to this method.
685         * @param endOfDay the end of day for calculating <em>sof zman biur chametz</em>. This can be sunset or any <em>tzais</em>
686         *         passed to this method.
687         * @param synchronous If the <em>zman</em> has a synchronous start and end of the day. If this is <code>false</code>, using a
688         *         {@link #isUseAstronomicalChatzosForOtherZmanim()} makes no sense and will be ignored even if set to true, since by
689         *         definition <em>chatzos</em> will not be the middle of the day for the <em>zman</em>.
690         * @return the <code>Instant</code> of the <em>sof zman biur chametz</em> based on the start and end of day times passed
691         *         to this method. If the date is not <em>Erev Pesach</em> or if the calculation can't be computed such as in the
692         *         Arctic Circle where there is at least one day a year where the sun does not rise, and one where it does not set,
693         *         a <code>null</code> will be returned. See detailed explanation on top of the {@link AstronomicalCalendar}
694         *         documentation.
695         */
696        public Instant getSofZmanBiurChametz(Instant startOfDay, Instant endOfDay, boolean synchronous) {
697                JewishCalendar jewishCalendar = new JewishCalendar(getLocalDate());
698                if (jewishCalendar.getJewishMonth() == JewishCalendar.NISSAN && jewishCalendar.getJewishDayOfMonth() == 14) {
699                        if (isUseAstronomicalChatzosForOtherZmanim() && synchronous) {
700                                return getHalfDayBasedZman(startOfDay, getChatzosHayom(), 5);
701                        } else {
702                                return getShaahZmanisBasedZman(startOfDay, endOfDay, 5);
703                        }
704                } else {
705                        return null;
706                }
707        }
708        
709        /**
710         * A generic method for calculating <em>sof zman achilas chametz</em> or the latest time one is allowed eating
711         * <em>chametz</em> on <em>Erev Pesach</em> that is 4 * <em>shaos zmaniyos</em> (temporal hours) after the start of the
712         * day, calculated using the start and end of the day passed to this method. If the date is not <em>erev Pesach</em>, a
713         * null will be returned. The time from the start of day to the end of day is divided into 12 <em>shaos zmaniyos</em>
714         * (temporal hours), and <em>sof zman achilas chametz</em> is calculated as 4 of those <em>shaos zmaniyos</em> after the
715         * beginning of the day. As an example, passing {@link #getSunrise() sunrise} and {@link #getSunset() sunset} to this
716         * method will return <em>sof zman achilas chametz</em> according to the opinion of the <a href=
717         * "https://en.wikipedia.org/wiki/Vilna_Gaon">GRA</a>. This method's synchronous parameter indicates if the start
718         * and end of day for the calculation are synchronous, having the same offset. This is typically the case, but some
719         * <em>zmanim</em> calculations are based on a start and end at different offsets from the real start and end of the day,
720         * such as starting the day at <em>alos</em> and an ending it at <em>tzais Geonim</em> or some other variant. If the day
721         * is not synchronous a {@link #getHalfDayBasedZman(Instant, Instant, double) half-day based calculations} will be bypassed.
722         * It would be illogical to use a half-day based calculation that start/end at <em>chatzos</em> when the two "halves" of
723         * the day are not equal, and the halfway point between them is not at <em>chatzos</em>.
724         * 
725         * @param startOfDay the start of day for calculating <em>sof zman achilas chametz</em>. This can be sunrise or any <em>alos</em>
726         *         passed to this method.
727         * @param endOfDay the end of day for calculating <em>sof zman achilas chametz</em>. This can be sunset or any <em>tzais</em>
728         *         passed to this method.
729         * @param synchronous If the <em>zman</em> has a synchronous start and end of the day. If this is <code>false</code>, using a {@link
730         *         isUseAstronomicalChatzosForOtherZmanim()} makes no sense and will be ignored even if set to true, since by definition
731         *         <em>chatzos</em> will not be the middle of the day for the <em>zman</em>.
732         * @return the <code>Instant</code> of the <em>sof zman achilas chametz</em> based on the start and end of day times passed
733         *         to this method. If the date is not <em>Erev Pesach</em> or if the calculation can't be computed such as in the
734         *         Arctic Circle where there is at least one day a year where the sun does not rise, and one where it does not set,
735         *         a <code>null</code> will be returned. See detailed explanation on top of the {@link AstronomicalCalendar}
736         *         documentation.
737         */
738        public Instant getSofZmanAchilasChametz(Instant startOfDay, Instant endOfDay, boolean synchronous) {
739                JewishCalendar jewishCalendar = new JewishCalendar(getLocalDate());
740                if (jewishCalendar.getJewishMonth() == JewishCalendar.NISSAN && jewishCalendar.getJewishDayOfMonth() == 14) {
741                        return getSofZmanTfila(startOfDay, endOfDay, synchronous);
742                } else {
743                        return null;
744                }
745        }
746        
747        /**
748         * A generic method for calculating the latest <em>zman tfila</em> that calls {@link #getSofZmanTfila(Instant, Instant, boolean)}
749         * passing <code>false</code> to the synchronous parameter since there is no way to know if the start and end of the day are 
750         * synchronous. Passing true when they are not synchronous is too much of a risk. See information on that method for more details.
751         * @param startOfDay the start of day for calculating <em>zman tfilah</em>. This can be sunrise or any <em>alos</em> passed
752         *         to this method.
753         * @param endOfDay the end of day for calculating <em>zman tfilah</em>. This can be sunset or any <em>tzais</em> passed to
754         *         this method.
755         * @return the <code>Instant</code> of the latest <em>zman tfilah</em> based on the start and end of day times passed to this
756         *         method. If the calculation can't be computed such as in the Arctic Circle where there is at least one day
757         *         a year where the sun does not rise, and one where it does not set, a <code>null</code> will be returned. See
758         *         detailed explanation on top of the {@link AstronomicalCalendar} documentation.
759         * @see #getSofZmanShma(Instant, Instant, boolean)
760         */
761        public Instant getSofZmanTfila(Instant startOfDay, Instant endOfDay) {
762                return getSofZmanTfila(startOfDay, endOfDay, false);
763        }
764
765        /**
766         * This method returns the latest <em>zman tfila</em> (time to recite shema in the morning) that is 4 *
767         * {@link #getShaahZmanisGRA() <em>shaos zmaniyos</em> }(solar hours) after {@link #getSunrise() sunrise} or
768         * {@link #getSeaLevelSunrise() sea level sunrise} (depending on the {@link #isUseElevation()} setting), according
769         * to the <a href="https://en.wikipedia.org/wiki/Vilna_Gaon">GRA</a>. 
770         * The day is calculated from {@link #getSeaLevelSunrise() sea level sunrise} to {@link #getSeaLevelSunset() sea level
771         * sunset} or from {@link #getSunrise() sunrise} to {@link #getSunset() sunset} (depending on the
772         * {@link #isUseElevation()} setting).
773         * 
774         * @see #getSofZmanTfila(Instant, Instant)
775         * @see #getShaahZmanisGRA()
776         * @see ComprehensiveZmanimCalendar#getSofZmanTfilaBaalHatanya()
777         * @return the <code>Instant</code> of the latest <em>zman tfilah</em>. If the calculation can't be computed such as in
778         *         the Arctic Circle where there is at least one day a year where the sun does not rise, and one where it
779         *         does not set, a <code>null</code> will be returned. See detailed explanation on top of the
780         *         {@link AstronomicalCalendar} documentation.
781         */
782        public Instant getSofZmanTfilaGRA() {
783                return getSofZmanTfila(getSunriseBasedOnElevationSetting(), getSunsetBasedOnElevationSetting(), true);
784        }
785
786        /**
787         * This method returns the latest <em>zman tfila</em> (time to recite shema in the morning) that is 4 * {@link
788         * getShaahZmanis72Minutes() <em>shaos zmaniyos</em>} (solar hours) after {@link #getAlos72Minutes()}, according to the
789         * <em><a href="https://en.wikipedia.org/wiki/Avraham_Gombiner">Magen Avraham (MGA)</a></em>. The day is calculated
790         * from 72 minutes before {@link #getSunriseBasedOnElevationSetting()} to 72 minutes after {@link
791         * getSunsetBasedOnElevationSetting()}. The use of elevation depends on the {@link #isUseElevation()} setting).
792         * 
793         * @return the <code>Instant</code> of the latest <em>zman tfila</em>. If the calculation can't be computed such as in
794         *         the Arctic Circle where there is at least one day a year where the sun does not rise, and one where it
795         *         does not set, a <code>null</code> will be returned. See detailed explanation on top of the
796         *         {@link AstronomicalCalendar} documentation.
797         * @see #getSofZmanTfila(Instant, Instant, boolean)
798         * @see #getShaahZmanis72Minutes()
799         * @see #getAlos72Minutes()
800         */
801        public Instant getSofZmanTfilaMGA72Minutes() {
802                return getSofZmanTfila(getAlos72Minutes(), getTzais72Minutes(), true);
803        }
804
805        /**
806         * A generic method for calculating <em>mincha gedola</em> (the earliest time to recite the <em>mincha</em> prayers) that
807         * is 6.5 * <em>shaos zmaniyos</em> (temporal hours) after the start of the day, calculated using the start and end of the
808         * day passed to this method. The time from the start of day to the end of day are divided into 12 <em>shaos zmaniyos</em>
809         * (temporal hours), and <em>mincha gedola</em> is calculated as 6.5 of those <em>shaos zmaniyos</em> after the beginning
810         * of the day. As an example, passing {@link #getSunrise() sunrise} and {@link #getSunset() sunset} or
811         * {@link #getSeaLevelSunrise() sea level sunrise} and {@link #getSeaLevelSunset() sea level sunset} (depending on the {@link
812         * isUseElevation()} elevation setting) to this method will return <em>mincha gedola</em> according to the opinion of the
813         * <a href="https://en.wikipedia.org/wiki/Vilna_Gaon">GRA</a>. Alternatively, this method uses {@link
814         * isUseAstronomicalChatzosForOtherZmanim()} to control if the time is based on 6.5 <em>shaos zmaniyos</em> into the day
815         * mentioned above, or as half an hour <em>zmaniyos</em> based on the second half of the day after <em>chatzos</em> ({@link
816         * getSunTransit() astronomical <em>chatzos</em>} if supported by the {@link AstronomicalCalculator calculator} and {@link
817         * isUseAstronomicalChatzos() configured} or {@link #getChatzosHayomAsHalfDay() <em>chatzos</em> as half a day} if not. This
818         * method's synchronous parameter indicates if the start and end of day for the calculation are synchronous, having the same
819         * offset. This is typically the case, but some <em>zmanim</em> calculations are based on a start and end at different offsets
820         * from the real start and end of the day, such as starting the day at <em>alos</em> and an ending it at <em>tzais Geonim</em>
821         * or some other variant. If the day is not synchronous a {@link #getHalfDayBasedZman(Instant, Instant, double) half-day based
822         * calculations} will be bypassed. It would be illogical to use a half-day based calculation that start/end at <em>chatzos</em>
823         * when the two "halves" of the day are not equal, and the halfway point between them is not at <em>chatzos</em>.
824         * 
825         * @param startOfDay the start of day for calculating <em>Mincha gedola</em>. This can be sunrise or any <em>alos</em> passed
826         *         to this method.
827         * @param endOfDay the end of day for calculating <em>Mincha gedola</em>. This can be sunset or any <em>tzais</em> passed
828         *         to this method.
829         * @param synchronous If the <em>zman</em> has a synchronous start and end of the day. If this is <code>false</code>, using a
830         *         {@link #isUseAstronomicalChatzosForOtherZmanim()} makes no sense and will be ignored even if set to true, since by
831         *         definition <em>chatzos</em> will not be the middle of the day for the <em>zman</em>.
832         * @return the <code>Instant</code> of the time of <em>Mincha gedola</em> based on the start and end of day times
833         *         passed to this method. If the calculation can't be computed such as in the Arctic Circle where there is
834         *         at least one day a year where the sun does not rise, and one where it does not set, a <code>null</code> will
835         *         be returned. See detailed explanation on top of the {@link AstronomicalCalendar} documentation.
836         * @see #getSunTransit()
837         * @see #getChatzosHayomAsHalfDay()
838         * @see #getChatzosHayom()
839         * @see #getChatzosHalayla()
840         * @see #isUseAstronomicalChatzos()
841         * @see #isUseAstronomicalChatzosForOtherZmanim()
842         */
843        public Instant getMinchaGedola(Instant startOfDay, Instant endOfDay, boolean synchronous) {
844                if (isUseAstronomicalChatzosForOtherZmanim() && synchronous) {
845                        return getHalfDayBasedZman(getChatzosHayom(), endOfDay, 0.5);
846                } else {
847                        return getShaahZmanisBasedZman(startOfDay, endOfDay, 6.5);
848                }
849        }
850        
851        /**
852         * A generic method for calculating <em>mincha gedola</em> that calls {@link #getMinchaGedola(Instant, Instant, boolean)} passing
853         * <code>false</code> to the synchronous parameter since there is no way to know if the start and end of the day are
854         * synchronous. Passing true when they are not synchronous is too much of a risk. See information on that method for more
855         * details.
856         * @param startOfDay the start of day for calculating <em>Mincha gedola</em>. This can be sunrise or any <em>alos</em> passed
857         *         to this method.
858         * @param endOfDay the end of day for calculating <em>Mincha gedola</em>. This can be sunset or any <em>tzais</em> passed to
859         *         this method.
860         * @return the <code>Instant</code> of the latest <em>Mincha gedola</em> based on the start and end of day times passed to this
861         *         method. If the calculation can't be computed such as in the Arctic Circle where there is at least one day
862         *         a year where the sun does not rise, and one where it does not set, a <code>null</code> will be returned. See
863         *         detailed explanation on top of the {@link AstronomicalCalendar} documentation.
864         * @see #getMinchaGedola(Instant, Instant, boolean)
865         */
866        public Instant getMinchaGedola(Instant startOfDay, Instant endOfDay) {
867                return getMinchaGedola(startOfDay, endOfDay, false);
868        }
869
870        /**
871         * This method returns the latest <em>mincha gedola</em>,the earliest time one can pray <em>mincha</em> that is 6.5 *
872         * {@link #getShaahZmanisGRA() <em>shaos zmaniyos</em>} (solar hours) after {@link #getSunrise() sunrise} or
873         * {@link #getSeaLevelSunrise() sea level sunrise} (depending on the {@link #isUseElevation()} setting), according
874         * to the <a href="https://en.wikipedia.org/wiki/Vilna_Gaon">GRA</a>. <em>Mincha gedola</em> is the earliest
875         * time one can pray <em>mincha</em>. The Ramba"m is of the opinion that it is better to delay <em>mincha</em> until
876         * {@link #getMinchaKetanaGRA() <em>mincha ketana GRA</em>} while the Ra"sh, Tur, GRA and others are of the
877         * opinion that <em>mincha</em> can be prayed <em>lechatchila</em> starting at <em>mincha gedola</em>.
878         * The day is calculated from {@link #getSeaLevelSunrise() sea level sunrise} to {@link #getSeaLevelSunset() sea level
879         * sunset} or {@link #getSunrise() sunrise} to {@link #getSunset() sunset} (depending on the {@link #isUseElevation()}
880         * setting).
881         * @todo Consider adjusting this to calculate the time as half an hour <em>zmaniyos</em> after {@link #getChatzosHayom()}
882         *         that uses {@link #isUseAstronomicalChatzos()} to determine the type of <em>chatzos</em> to utilize (if the
883         *         {@link com.kosherjava.zmanim.util.AstronomicalCalculator calculator} support astronomical <em>chatzos</em>),
884         *         based on the {@link #isUseAstronomicalChatzos()} setting.
885         * @see #getMinchaGedola(Instant, Instant)
886         * @see #getShaahZmanisGRA()
887         * @see #getMinchaKetanaGRA()
888         * @see ComprehensiveZmanimCalendar#getMinchaGedolaBaalHatanya()
889         * @return the <code>Instant</code> of the time of mincha gedola. If the calculation can't be computed such as in the
890         *         Arctic Circle where there is at least one day a year where the sun does not rise, and one where it does
891         *         not set, a <code>null</code> will be returned. See detailed explanation on top of the
892         *         {@link AstronomicalCalendar} documentation.
893         */
894        public Instant getMinchaGedolaGRA() {
895                return getMinchaGedola(getSunriseBasedOnElevationSetting(), getSunsetBasedOnElevationSetting(), true);
896        }
897        
898        /**
899         * A generic method for calculating <em>samuch lemincha ketana</em>, / near <em>mincha ketana</em> time that is half
900         * an hour before {@link #getMinchaKetana(Instant, Instant)}  or 9 * <em>shaos zmaniyos</em> (temporal hours) after the
901         * start of the day, calculated using the start and end of the day passed to this method.
902         * The time from the start of day to the end of day are divided into 12 <em>shaos zmaniyos</em> (temporal hours), and
903         * <em>samuch lemincha ketana</em> is calculated as 9 of those <em>shaos zmaniyos</em> after the beginning of the day.
904         * For example, passing {@link #getSunrise() sunrise} and {@link #getSunset() sunset} or {@link #getSeaLevelSunrise() sea
905         * level sunrise} and {@link #getSeaLevelSunset() sea level sunset} (depending on the {@link #isUseElevation()} elevation
906         * setting) to this method will return <em>samuch lemincha ketana</em> according to the opinion of the
907         * <a href="https://en.wikipedia.org/wiki/Vilna_Gaon">GRA</a>. See the <a href=
908         * "https://hebrewbooks.org/pdfpager.aspx?req=60387&st=&pgnum=294">Mechaber and Mishna Berurah 232</a> and <a href=
909         * "https://hebrewbooks.org/pdfpager.aspx?req=60388&pgnum=34">249:2</a>.
910         * 
911         * @param startOfDay the start of day for calculating <em>samuch lemincha ketana</em>. This can be sunrise or any <em>alos</em>
912         *         passed to this method.
913         * @param endOfDay the end of day for calculating <em>samuch lemincha ketana</em>. This can be sunset or any <em>tzais</em>
914         *         passed to this method.
915         * @param synchronous If the <em>zman</em> has a synchronous start and end of the day. If this is <code>false</code>, using a
916         *         {@link #isUseAstronomicalChatzosForOtherZmanim()} makes no sense and will be ignored even if set to true, since by
917         *         definition <em>chatzos</em> will not be the middle of the day for the <em>zman</em>.
918         * @return the <code>Instant</code> of the time of <em>Mincha ketana</em> based on the start and end of day times
919         *         passed to this method. If the calculation can't be computed such as in the Arctic Circle where there is
920         *         at least one day a year where the sun does not rise, and one where it does not set, a <code>null</code> will
921         *         be returned. See detailed explanation on top of the {@link AstronomicalCalendar} documentation.
922         * @see ComprehensiveZmanimCalendar#getSamuchLeMinchaKetanaGRA()
923         * @see ComprehensiveZmanimCalendar#getSamuchLeMinchaKetana16Point1Degrees()
924         * @see ComprehensiveZmanimCalendar#getSamuchLeMinchaKetana72Minutes()
925         */
926        public Instant getSamuchLeMinchaKetana(Instant startOfDay, Instant endOfDay, boolean synchronous) {
927                if (isUseAstronomicalChatzosForOtherZmanim() && synchronous) {
928                        return getHalfDayBasedZman(getChatzosHayom(), endOfDay, 3);
929                } else {
930                        return getShaahZmanisBasedZman(startOfDay, endOfDay, 9);
931                }
932        }
933        
934        /**
935         * A generic method for calculating <em>samuch lemincha ketana</em> that calls {@link #getSamuchLeMinchaKetana(Instant, Instant, boolean)}
936         * passing <code>false</code> to the synchronous parameter since there is no way to know if the start and end of the day are 
937         * synchronous. Passing true when they are not synchronous is too much of a risk. See information on that method for more details.
938         * @param startOfDay the start of day for calculating <em>samuch lemincha ketana</em>. This can be sunrise or any <em>alos</em>
939         *         passed to this method.
940         * @param endOfDay the end of day for calculating <em>samuch lemincha ketana</em>. This can be sunset or any <em>tzais</em>
941         *         passed to this method.
942         * @return the <code>Instant</code> of the time of <em>samuch lemincha ketana</em> based on the start and end of day times
943         *         passed to this method. If the calculation can't be computed such as in the Arctic Circle where there is
944         *         at least one day a year where the sun does not rise, and one where it does not set, a <code>null</code> will
945         *         be returned. See detailed explanation on top of the {@link AstronomicalCalendar} documentation.
946         * @see #getSamuchLeMinchaKetana(Instant, Instant, boolean)
947         */
948        public Instant getSamuchLeMinchaKetana(Instant startOfDay, Instant endOfDay) {
949                return getSamuchLeMinchaKetana(startOfDay, endOfDay, false);
950        }
951
952        /**
953         * A generic method for calculating <em>mincha ketana</em>, (the preferred time to recite the mincha prayers in
954         * the opinion of the <em><a href="https://en.wikipedia.org/wiki/Maimonides">Rambam</a></em> and others) that is
955         * 9.5 * <em>shaos zmaniyos</em> (temporal hours) after the start of the day, calculated using the start and end
956         * of the day passed to this method.
957         * The time from the start of day to the end of day are divided into 12 <em>shaos zmaniyos</em> (temporal hours), and
958         * <em>mincha ketana</em> is calculated as 9.5 of those <em>shaos zmaniyos</em> after the beginning of the day. As an
959         * example, passing {@link #getSunrise() sunrise} and {@link #getSunset() sunset} or {@link #getSeaLevelSunrise() sea
960         * level sunrise} and {@link #getSeaLevelSunset() sea level sunset} (depending on the {@link #isUseElevation()}
961         * elevation setting) to this method will return <em>mincha ketana</em> according to the opinion of the
962         * <a href="https://en.wikipedia.org/wiki/Vilna_Gaon">GRA</a>. This method's synchronous parameter indicates if the start
963         * and end of day for the calculation are synchronous, having the same offset. This is typically the case, but some
964         * <em>zmanim</em> calculations are based on a start and end at different offsets from the real start and end of the day,
965         * such as starting the day at <em>alos</em> and an ending it at <em>tzais Geonim</em> or some other variant. If the day
966         * is not synchronous a {@link #getHalfDayBasedZman(Instant, Instant, double) half-day based calculations} will be bypassed.
967         * It would be illogical to use a half-day based calculation that start/end at <em>chatzos</em> when the two "halves" of
968         * the day are not equal, and the halfway point between them is not at <em>chatzos</em>.
969         * 
970         * @param startOfDay the start of day for calculating <em>Mincha ketana</em>. This can be sunrise or any <em>alos</em> passed
971         *         to this method.
972         * @param endOfDay the end of day for calculating <em>Mincha ketana</em>. This can be sunset or any <em>tzais</em> passed to
973         *         this method.
974         * @param synchronous If the <em>zman</em> has a synchronous start and end of the day. If this is <code>false</code>, using a
975         *         {@link #isUseAstronomicalChatzosForOtherZmanim()} makes no sense and will be ignored even if set to true, since by
976         *         definition <em>chatzos</em> will not be the middle of the day for the <em>zman</em>.
977         * @return the <code>Instant</code> of the time of <em>Mincha ketana</em> based on the start and end of day times
978         *         passed to this method. If the calculation can't be computed such as in the Arctic Circle where there is
979         *         at least one day a year where the sun does not rise, and one where it does not set, a <code>null</code> will
980         *         be returned. See detailed explanation on top of the {@link AstronomicalCalendar} documentation.
981         */
982        public Instant getMinchaKetana(Instant startOfDay, Instant endOfDay, boolean synchronous) {
983                if (isUseAstronomicalChatzosForOtherZmanim() && synchronous) {
984                        return getHalfDayBasedZman(getChatzosHayom(), endOfDay, 3.5);
985                } else {
986                        return getShaahZmanisBasedZman(startOfDay, endOfDay, 9.5);
987                }
988        }
989        
990        /**
991         * A generic method for calculating <em>mincha ketana</em> that calls {@link #getMinchaKetana(Instant, Instant, boolean)} passing
992         * <code>false</code> to the synchronous parameter since there is no way to know if the start and end of the day are synchronous.
993         * Passing true when they are not synchronous is too much of a risk. See information on that method for more details.
994         * @param startOfDay the start of day for calculating <em>Mincha ketana</em>. This can be sunrise or any <em>alos</em> passed
995         *         to this method.
996         * @param endOfDay the end of day for calculating <em>Mincha ketana</em>. This can be sunset or any <em>tzais</em> passed to
997         *         this method.
998         * @return the <code>Instant</code> of the time of <em>Mincha ketana</em> based on the start and end of day times
999         *         passed to this method. If the calculation can't be computed such as in the Arctic Circle where there is
1000         *         at least one day a year where the sun does not rise, and one where it does not set, a <code>null</code> will
1001         *         be returned. See detailed explanation on top of the {@link AstronomicalCalendar} documentation.
1002         * @see #getMinchaKetana(Instant, Instant, boolean)
1003         */
1004        public Instant getMinchaKetana(Instant startOfDay, Instant endOfDay) {
1005                return getMinchaKetana(startOfDay, endOfDay, false);
1006        }
1007
1008        /**
1009         * This method returns <em>mincha ketana</em>,the preferred earliest time to pray <em>mincha</em> in the
1010         * opinion of the <em><a href="https://en.wikipedia.org/wiki/Maimonides">Rambam</a></em> and others, that is 9.5
1011         * * {@link #getShaahZmanisGRA() <em>shaos zmaniyos</em>} (solar hours) after {@link #getSunrise() sunrise} or
1012         * {@link #getSeaLevelSunrise() sea level sunrise} (depending on the {@link #isUseElevation()} setting), according
1013         * to the <a href="https://en.wikipedia.org/wiki/Vilna_Gaon">GRA</a>. For more information on this see the
1014         * documentation on {@link #getMinchaGedolaGRA() <em>mincha gedola</em>}.
1015         * The day is calculated from {@link #getSeaLevelSunrise() sea level sunrise} to {@link #getSeaLevelSunset() sea level
1016         * sunset} or from {@link #getSunrise() sunrise} to {@link #getSunset() sunset} (depending on the {@link #isUseElevation()}
1017         * setting.
1018         * 
1019         * @see #getMinchaKetana(Instant, Instant)
1020         * @see #getShaahZmanisGRA()
1021         * @see #getMinchaGedolaGRA()
1022         * @see ComprehensiveZmanimCalendar#getMinchaKetanaBaalHatanya()
1023         * @return the <code>Instant</code> of the time of mincha ketana. If the calculation can't be computed such as in the
1024         *         Arctic Circle where there is at least one day a year where the sun does not rise, and one where it does
1025         *         not set, a <code>null</code> will be returned. See detailed explanation on top of the
1026         *         {@link AstronomicalCalendar} documentation.
1027         */
1028        public Instant getMinchaKetanaGRA() {
1029                return getMinchaKetana(getSunriseBasedOnElevationSetting(), getSunsetBasedOnElevationSetting(), true);
1030        }
1031
1032        /**
1033         * A generic method for calculating <em>plag hamincha</em> (the earliest time that Shabbos can be started) that is
1034         * 10.75 hours after the start of the day, (or 1.25 hours before the end of the day) based on the start and end of
1035         * the day passed to the method.
1036         * The time from the start of day to the end of day are divided into 12 <em>shaos zmaniyos</em> (temporal hours), and
1037         * <em>plag hamincha</em> is calculated as 10.75 of those <em>shaos zmaniyos</em> after the beginning of the day. As an
1038         * example, passing {@link #getSunrise() sunrise} and {@link #getSunset() sunset} or {@link #getSeaLevelSunrise() sea level
1039         * sunrise} and {@link #getSeaLevelSunset() sea level sunset} (depending on the {@link #isUseElevation()} elevation
1040         * setting) to this method will return <em>plag mincha</em> according to the opinion of the
1041         * <a href="https://en.wikipedia.org/wiki/Vilna_Gaon">GRA</a>. This method's synchronous parameter indicates if the start
1042         * and end of day for the calculation are synchronous, having the same offset. This is typically the case, but some
1043         * <em>zmanim</em> calculations are based on a start and end at different offsets from the real start and end of the day,
1044         * such as starting the day at <em>alos</em> and an ending it at <em>tzais Geonim</em> or some other variant. If the day
1045         * is not synchronous a {@link #getHalfDayBasedZman(Instant, Instant, double) half-day based calculations} will be bypassed. It
1046         * would be illogical to use a half-day based calculation that start/end at <em>chatzos</em> when the two "halves" of the
1047         * day are not equal, and the halfway point between them is not at <em>chatzos</em>.
1048         * 
1049         * @param startOfDay the start of day for calculating <em>plag hamincha</em>. This can be sunrise or any <em>alos</em> passed to
1050         *         this method.
1051         * @param endOfDay the end of day for calculating <em>plag hamincha</em>. This can be sunset or any <em>tzais</em> passed to
1052         *         this method.
1053         * @param synchronous If the <em>zman</em> has a synchronous start and end of the day. If this is <code>false</code>, using a
1054         *         {@link #isUseAstronomicalChatzosForOtherZmanim()} makes no sense and will be ignored even if set to true, since by
1055         *         definition <em>chatzos</em> will not be the middle of the day for the <em>zman</em>.
1056         * @return the <code>Instant</code> of the time of <em>plag hamincha</em> based on the start and end of day times
1057         *         passed to this method. If the calculation can't be computed such as in the Arctic Circle where there is
1058         *         at least one day a year where the sun does not rise, and one where it does not set, a <code>null</code>
1059         *         will be returned. See detailed explanation on top of the {@link AstronomicalCalendar} documentation.
1060         */
1061        public Instant getPlagHamincha(Instant startOfDay, Instant endOfDay, boolean synchronous) {
1062                if (isUseAstronomicalChatzosForOtherZmanim() && synchronous) {
1063                        return getHalfDayBasedZman(getChatzosHayom(), endOfDay, 4.75);
1064                } else {
1065                        return getShaahZmanisBasedZman(startOfDay, endOfDay, 10.75);
1066                }
1067        }
1068        
1069        /**
1070         * A generic method for calculating <em>plag hamincha</em> that calls {@link #getPlagHamincha(Instant, Instant, boolean)} passing
1071         * <code>false</code> to the synchronous parameter since there is no way to know if the start and end of the day are synchronous.
1072         * Passing true when they are not synchronous is too much of a risk. See information on that method for more details.
1073         * @param startOfDay the start of day for calculating <em>plag hamincha</em>. This can be sunrise or any <em>alos</em> passed to
1074         *         this method.
1075         * @param endOfDay the end of day for calculating <em>plag hamincha</em>. This can be sunset or any <em>tzais</em> passed to this
1076         *         method.
1077         * @return the <code>Instant</code> of the time of <em>plag hamincha</em> based on the start and end of day times
1078         *         passed to this method. If the calculation can't be computed such as in the Arctic Circle where there is
1079         *         at least one day a year where the sun does not rise, and one where it does not set, a <code>null</code>
1080         *         will be returned. See detailed explanation on top of the {@link AstronomicalCalendar} documentation.
1081         * @see #getPlagHamincha(Instant, Instant, boolean)
1082         */
1083        public Instant getPlagHamincha(Instant startOfDay, Instant endOfDay) {
1084                return getPlagHamincha(startOfDay, endOfDay, false);
1085        }
1086
1087        /**
1088         * This method returns <em>plag hamincha</em>, that is 10.75 * {@link #getShaahZmanisGRA() <em>shaos zmaniyos</em>}
1089         * (solar hours) after {@link #getSunrise() sunrise} or {@link #getSeaLevelSunrise() sea level sunrise} (depending on
1090         * the {@link #isUseElevation()} setting), according to the <a href="https://en.wikipedia.org/wiki/Vilna_Gaon"
1091         * >GRA</a>. <em>Plag hamincha</em> is the earliest time that <em>Shabbos</em> can be started.
1092         * The day is calculated from {@link #getSeaLevelSunrise() sea level sunrise} to {@link #getSeaLevelSunset() sea level
1093         * sunset} or {@link #getSunrise() sunrise} to {@link #getSunset() sunset} (depending on the {@link #isUseElevation()}
1094         * 
1095         * @see #getPlagHamincha(Instant, Instant, boolean)
1096         * @see #getPlagHamincha(Instant, Instant)
1097         * @see ComprehensiveZmanimCalendar#getPlagHaminchaBaalHatanya()
1098         * @return the <code>Instant</code> of the time of <em>plag hamincha</em>. If the calculation can't be computed such as
1099         *         in the Arctic Circle where there is at least one day a year where the sun does not rise, and one where it
1100         *         does not set, a <code>null</code> will be returned. See detailed explanation on top of the
1101         *         {@link AstronomicalCalendar} documentation.
1102         */
1103        public Instant getPlagHaminchaGRA() {
1104                return getPlagHamincha(getSunriseBasedOnElevationSetting(), getSunsetBasedOnElevationSetting(), true);
1105        }
1106
1107        /**
1108         * A method that returns a <em>shaah zmanis</em> ({@link #getTemporalHour(Instant, Instant) temporal hour}) according to
1109         * the opinion of the <a href="https://en.wikipedia.org/wiki/Vilna_Gaon">GRA</a>. This calculation divides the day
1110         * based on the opinion of the <em>GRA</em> that the day runs from {@link #getSeaLevelSunrise() sea level sunrise} to
1111         * {@link #getSeaLevelSunset() sea level sunset} or {@link #getSunrise() sunrise} to {@link #getSunset() sunset} (depending
1112         * on the {@link #isUseElevation()} setting). The day is split into 12 equal parts with each one being a <em>shaah zmanis</em>.
1113         * This method is similar to {@link #getTemporalHour()}, but can account for elevation.
1114         * 
1115         * @return the <code>Duration</code> of a <em>shaah zmanis</em> calculated from sunrise to sunset. If the calculation can't be
1116         *         computed such as in the Arctic Circle where there is at least one day a year where the sun does not rise, and one
1117         *         where it does not set, a <code>null</code> will be returned. See  detailed explanation on top of the
1118         *         {@link AstronomicalCalendar} documentation.
1119         * @see #getTemporalHour(Instant, Instant)
1120         * @see #getSeaLevelSunrise()
1121         * @see #getSeaLevelSunset()
1122         * @see ComprehensiveZmanimCalendar#getShaahZmanisBaalHatanya()
1123         */
1124        public Duration getShaahZmanisGRA() {
1125                return getTemporalHour(getSunriseBasedOnElevationSetting(), getSunsetBasedOnElevationSetting());
1126        }
1127        
1128        /**
1129         * A utility method to return <em>alos</em> (dawn) or <em>tzais</em> (dusk) based on a fractional day offset. As an
1130         * example passing 1.5 to this method as done in the {@link ComprehensiveZmanimCalendar#getTzais90Zmanis()} will return
1131         * the time 90 minutes <em>zmaniyos</em> after {@link #getSunsetBasedOnElevationSetting()}, a <em>zman</em> known as
1132         * the <em>achtel zman</em> or 1/8th of the length of the day (12 * 60 = 720-minute day / 8 = 90 or 1.5 hours
1133         * <em>zmaniyos</em>) after sunset.
1134         * @param hours the number of <em>shaos zmaniyos</em> (temporal hours) before sunrise or after sunset that defines dawn
1135         *        or dusk. If a negative number is passed in, it will return the time of <em>alos</em> (dawn) (subtracting the
1136         *        time from sunrise) and if a positive number is passed in, it will return the time of <em>tzais</em> (dusk)
1137         *        (adding the time to sunset). If 0 is passed in, a <code>null</code> will be returned (since we can't tell if it
1138         *        is sunrise or sunset based).
1139         * @return the <code>Instant</code> representing the time. If the calculation can't be computed such as in the Arctic
1140         *         Circle where there is at least one day a year where the sun does not rise, and one where it does not set,
1141         *         a <code>null</code> will be returned. See detailed explanation on top of the {@link AstronomicalCalendar}
1142         *         documentation. A <code>null</code> will also be returned if 0 is passed in, since we can't tell if it is a 
1143         *         sunrise or sunset based <em>zman</em>.
1144         * @see <a href="{@docRoot}/search.html?q=ComprehensiveZmanimCalendar%20Zmanis()">ComprehensiveZmanimCalendar...Zmanis()</a>
1145         * based <em>zmanim</em>.
1146         */
1147        public Instant getZmanisBasedOffset(double hours) {
1148                Duration shaahZmanis = getShaahZmanisGRA();
1149                if (shaahZmanis == null || hours == 0) {
1150                        return null;
1151                }
1152
1153                long totalNanos = (long) (shaahZmanis.toNanos() * hours);
1154                Duration offsetDuration = Duration.ofNanos(totalNanos);
1155
1156                if (hours > 0) {
1157                        return getTimeOffset(getSunsetBasedOnElevationSetting(), offsetDuration);
1158                } else {
1159                        return getTimeOffset(getSunriseBasedOnElevationSetting(), offsetDuration);
1160                }
1161        }
1162
1163        /**
1164         * A method that returns a <em>shaah zmanis</em> (temporal hour) according to the opinion of the <em><a href=
1165         * "https://en.wikipedia.org/wiki/Avraham_Gombiner">Magen Avraham (MGA)</a></em> based on a 72-minute <em>alos</em>
1166         * and <em>tzais</em>. This calculation divides the day that runs from dawn to dusk (for <em>sof zman krias shema</em> and
1167         * <em>tfila</em>). Dawn for this calculation is 72 minutes before {@link #getSunrise() sunrise} or {@link #getSeaLevelSunrise()
1168         * sea level sunrise} (depending on the {@link #isUseElevation()} elevation setting) and dusk is 72 minutes after {@link
1169         * getSunset() sunset} or {@link #getSeaLevelSunset() sea level sunset} (depending on the {@link #isUseElevation()} elevation
1170         * setting). This day is split into 12 equal parts with each part being a <em>shaah zmanis</em>. Alternate methods of calculating
1171         * a <em>shaah zmanis</em> according to the Magen Avraham (MGA) are available in the subclass {@link ComprehensiveZmanimCalendar}.
1172         * 
1173         * @return the <code>Duration</code> of a <em>shaah zmanis</em>. If the calculation can't be computed
1174         *         such as in the Arctic Circle where there is at least one day a year where the sun does not rise, and one
1175         *         where it does not set, a <code>null</code> will be returned. See detailed explanation on top of the
1176         *         {@link AstronomicalCalendar} documentation.
1177         */
1178        public Duration getShaahZmanis72Minutes() {
1179                return getTemporalHour(getAlos72Minutes(), getTzais72Minutes());
1180        }
1181
1182        /**
1183         * Default constructor will set a default {@link GeoLocation#GeoLocation()}, a default
1184         * {@link AstronomicalCalculator#getDefault() AstronomicalCalculator} and default the calendar to the current date.
1185         * 
1186         * @see AstronomicalCalendar#AstronomicalCalendar()
1187         */
1188        public ZmanimCalendar() {
1189                super();
1190        }
1191
1192        /**
1193         * A constructor that takes a {@link GeoLocation} as a parameter.
1194         * 
1195         * @param location the location
1196         */
1197        public ZmanimCalendar(GeoLocation location) {
1198                super(location);
1199        }
1200
1201        /**
1202         * A method to get the offset in minutes before {@link #getSeaLevelSunset() sea level sunset} which
1203         * is used in calculating candle lighting time. The default time used is 18 minutes before sea level sunset. Some
1204         * calendars use 15 minutes, while the custom in Jerusalem is to use a 40-minute offset. Please check the local custom
1205         * for candle lighting time.
1206         * 
1207         * @return Returns the currently set candle lighting offset in minutes.
1208         * @see #getCandleLighting()
1209         * @see #setCandleLightingOffset(double)
1210         */
1211        public double getCandleLightingOffset() {
1212                return candleLightingOffset;
1213        }
1214
1215        /**
1216         * A method to set the offset in minutes before {@link #getSeaLevelSunset() sea level sunset} that is
1217         * used in calculating candle lighting time. The default time used is 18 minutes before sunset. Some calendars use 15
1218         * minutes, while the custom in Jerusalem is to use a 40-minute offset.
1219         * 
1220         * @param candleLightingOffset The candle lighting offset to set in minutes.
1221         * @see #getCandleLighting()
1222         * @see #getCandleLightingOffset()
1223         */
1224        public void setCandleLightingOffset(double candleLightingOffset) {
1225                this.candleLightingOffset = candleLightingOffset;
1226        }
1227        
1228        /**
1229         * This is a utility method to determine if the current Instant passed in has a <em>melacha</em> (work) prohibition.
1230         * Since there are many opinions on the time of <em>tzais</em>, the <em>tzais</em> for the current day has to be passed to this
1231         * class. Sunset is the classes current day's {@link #getSunsetBasedOnElevationSetting() elevation adjusted sunset} that observes the
1232         * {@link #isUseElevation()} settings. The {@link JewishCalendar#getInIsrael()} will be set by the inIsrael parameter.
1233         * 
1234         * @param currentTime the current time
1235         * @param tzais the time of tzais
1236         * @param inIsrael whether to use Israel holiday scheme or not
1237         * @return true if <em>melacha</em> is prohibited or false if it is not.
1238         * @see JewishCalendar#isAssurBemelacha()
1239         * @see JewishCalendar#hasCandleLighting()
1240         * @see JewishCalendar#setInIsrael(boolean)
1241         */
1242        public boolean isAssurBemelacha(Instant currentTime, Instant tzais, boolean inIsrael) {
1243                JewishCalendar jewishCalendar = new JewishCalendar();
1244                jewishCalendar.setGregorianDate(getLocalDate());
1245                
1246                jewishCalendar.setInIsrael(inIsrael);
1247                
1248                if (jewishCalendar.hasCandleLighting() && currentTime.compareTo(getSunsetBasedOnElevationSetting()) >= 0) { //erev shabbos, YT or YT sheni and after shkiah
1249                        return true;
1250                }
1251                
1252                //is shabbos or YT and it is before tzais
1253                return jewishCalendar.isAssurBemelacha() && currentTime.compareTo(tzais) <= 0;
1254        }
1255        
1256        /**
1257         * A method that returns the <a href="https://en.wikipedia.org/wiki/Shneur_Zalman_of_Liadi">Baal Hatanya</a>'s <em>netz amiti</em>
1258         * (sunrise) without {@link AstronomicalCalculator#getElevationAdjustment(double)}. This forms the base for the Baal Hatanya's
1259         * dawn-based calculations that are calculated as a dip below the horizon before sunrise. According to the Baal Hatanya, <em>netz
1260         * amiti</em>, or true (halachic) sunrise, is when the top of the sun's disk is visible at an elevation similar to the mountains
1261         * of Eretz Yisrael. The time is calculated as the point at which the center of the sun's disk is 1.583° below the horizon. This
1262         * degree-based calculation can be found in Rabbi Shalom DovBer Levine's commentary on The <a href=
1263         * "https://www.chabadlibrary.org/books/pdf/Seder-Hachnosas-Shabbos.pdf">Baal Hatanya's Seder Hachnasas Shabbos</a>. From an
1264         * elevation of 546 meters, the top of <a href="https://en.wikipedia.org/wiki/Mount_Carmel">Har Hacarmel</a>, the sun disappears
1265         * when it is 1° 35' or 1.583° below the sea level horizon. This in turn is based on the Gemara <a href=
1266         * "https://hebrewbooks.org/shas.aspx?mesechta=2&daf=35">Shabbos 35a</a>. There are other opinions brought down by Rabbi Levine,
1267         * including Rabbi Yosef Yitzchok Feigelstock who calculates it as the degrees below the horizon 4 minutes after sunset in
1268         * Yerushalayim (on the equinox). That is brought down as 1.583°. This is identical to the 1° 35' <em>zman</em> and is probably a
1269         * typo and should be 1.683°. These calculations are used by most <a href="https://en.wikipedia.org/wiki/Chabad">Chabad</a>
1270         * calendars that use the Baal Hatanya's <em>zmanim</em>. See
1271         * <a href="https://www.chabad.org/library/article_cdo/aid/3209349/jewish/About-Our-Zmanim-Calculations.htm">About Our
1272         * <em>Zmanim</em> Calculations @ Chabad.org</a>.
1273         *
1274         * Note: <em>netz amiti</em> is used only for calculating certain <em>zmanim</em>, and is intentionally unpublished. For
1275         * practical purposes, daytime <em>mitzvos</em> like <em>shofar</em> and <em>lulav</em> should not be done until after the
1276         * published time for <em>netz</em> / sunrise.
1277         * 
1278         * @return the <code>Instant</code> representing the exact sea level <em>netz amiti</em> (sunrise) time. If the calculation can't
1279         *         be computed such as in the Arctic Circle where there is at least one day a year where the sun does not rise, and one
1280         *         where it does not set, a <code>null</code> will be returned. See detailed explanation on top of the page.
1281         * @see #getSunrise()
1282         * @see #getSeaLevelSunrise()
1283         * @see #getSunsetBaalHatanya()
1284         * @see #ZENITH_1_POINT_583
1285         */
1286        protected Instant getSunriseBaalHatanya() {
1287                return getSunriseOffsetByDegrees(ZENITH_1_POINT_583);
1288        }
1289        
1290        /**
1291         * A method that returns the <a href="https://en.wikipedia.org/wiki/Shneur_Zalman_of_Liadi">Baal Hatanya</a>'s <em>shkiah
1292         * amiti</em> (sunset) without {@link AstronomicalCalculator#getElevationAdjustment(double) elevation adjustment}. This forms
1293         * the base for the Baal Hatanya's dusk-based calculations that are calculated as a dip below the horizon after sunset. According
1294         * to the Baal Hatanya, <em>shkiah amiti</em>, true (<em>halachic</em>) sunset, is when the top of the sun's disk disappears from
1295         * view at an elevation similar to the mountains of <em>Eretz Yisrael</em>. This time is calculated as the point at which the
1296         * center of the sun's disk is 1.583° below the horizon.
1297         *
1298         * Note: <em>shkiah amiti</em> is used only for calculating certain <em>zmanim</em>, and is intentionally unpublished. For
1299         * practical purposes, all daytime mitzvos should be completed before the published time for <em>shkiah</em> / sunset. For further
1300         * explanation of the calculations used for the Baal Hatanya's <em>zmanim</em> in this library, see <a href=
1301         * "https://www.chabad.org/library/article_cdo/aid/3209349/jewish/About-Our-Zmanim-Calculations.htm">About Our <em>Zmanim</em>
1302         * Calculations @ Chabad.org</a>.
1303         * 
1304         * @return the <code>Instant</code> representing the exact sea level <em>shkiah amiti</em> (sunset) time. If the calculation can't
1305         *         be computed such as in the Arctic Circle where there is at least one day a year where the sun does not rise, and one
1306         *         where it does not set, a <code>null</code> will be returned. See detailed explanation on top of the {@link
1307         *         AstronomicalCalendar} documentation.
1308         * @see #getSunset()
1309         * @see #getSeaLevelSunset()
1310         * @see #getSunriseBaalHatanya()
1311         * @see #ZENITH_1_POINT_583
1312         */
1313        protected Instant getSunsetBaalHatanya() {
1314                return getSunsetOffsetByDegrees(ZENITH_1_POINT_583);
1315        }
1316
1317        /**
1318         * A generic utility method for calculating any <em>shaah zmanis</em> (temporal hour) based <em>zman</em> with the day
1319         * defined as the start and end of day (or night) and the number of <em>shaos zmaniyos</em> passed to the method. This
1320         * simplifies the code in other methods such as {@link #getPlagHamincha(Instant, Instant)} and cuts down on code replication.
1321         * As an example, passing {@link #getSunrise() sunrise} and {@link #getSunset() sunset} or {@link
1322         * getSeaLevelSunrise() sea level sunrise} and {@link #getSeaLevelSunset() sea level sunset} (depending on the {@link
1323         * isUseElevation()} elevation setting) and 10.75 hours to this method will return <em>plag mincha</em> according to the
1324         * opinion of the <a href="https://en.wikipedia.org/wiki/Vilna_Gaon">GRA</a>.
1325         * 
1326         * @param startOfDay the start of day for calculating the <em>zman</em>. This can be sunrise or any <em>alos</em> passed to
1327         *         this method.
1328         * @param endOfDay the end of day for calculating the <em>zman</em>. This can be sunset or any <em>tzais</em> passed to this
1329         *         method.
1330         * @param hours the number of <em>shaos zmaniyos</em> (temporal hours) to offset from the start of day
1331         * @return the <code>Instant</code> of the time of <em>zman</em> with the <em>shaos zmaniyos</em> (temporal hours)
1332         *         in the day offset from the start of day passed to this method. If the calculation can't be computed such
1333         *         as in the Arctic Circle where there is at least one day a year where the sun does not rise, and one
1334         *         where it does not set, a <code>null</code> will be  returned. See detailed explanation on top of the
1335         *         {@link AstronomicalCalendar} documentation.
1336         */
1337        public Instant getShaahZmanisBasedZman(Instant startOfDay, Instant endOfDay, double hours) {
1338                Duration shaahZmanis = getTemporalHour(startOfDay, endOfDay);
1339                if (shaahZmanis == null) { //defensive, should not be needed
1340                        return null;
1341                }
1342                
1343                long totalNanos = (long) (shaahZmanis.toNanos() * hours);
1344                return getTimeOffset(startOfDay, Duration.ofNanos(totalNanos));
1345        }
1346        
1347        
1348        /**
1349         * A utility method that returns the percentage of a <em>shaah zmanis</em> after sunset (or before sunrise) for a given degree
1350         * offset. For the <a href="https://kosherjava.com/2022/01/12/equinox-vs-equilux-zmanim-calculations/">equilux</a> where there
1351         * is a 720-minute day, passing 16.1° for the location of Jerusalem will return about 1.2. This will work for any location or
1352         * date, but will typically only be of interest at the equinox/equilux to calculate the percentage of a <em>shaah zmanis</em> for
1353         * those who want to use the <a href="https://en.wikipedia.org/wiki/Abraham_Cohen_Pimentel">Minchas Cohen</a> in Ma'amar 2:4 and
1354         * the <a href="https://en.wikipedia.org/wiki/Hezekiah_da_Silva">Pri Chadash</a> who calculate <em>tzais</em> as a percentage of
1355         * the day after sunset. While the Minchas Cohen only applies this to 72 minutes or a 1/10 of the day around the world (based on
1356         * the equinox / equilux in Israel), this method allows calculations for any degree level for any location.
1357         * 
1358         * @param degrees the number of degrees below the horizon after sunset.
1359         * @param sunset if <code>true</code> the calculation is in degrees after sunset, otherwise it is degrees before sunrise.
1360         * @return the <code>double</code> percentage of a <em>sha'ah zmanis</em> for a given set of degrees below the astronomical horizon
1361         *         for the current calendar.  If the calculation can't be computed a {@link Double#MIN_VALUE} will be returned. See detailed
1362         *         explanation on top of the page.
1363         */
1364        public double getPercentOfShaahZmanisFromDegrees(double degrees, boolean sunset) {
1365                Instant seaLevelSunrise = getSeaLevelSunrise();
1366                Instant seaLevelSunset = getSeaLevelSunset();
1367                Instant twilight;
1368                if (sunset) {
1369                        twilight = getSunsetOffsetByDegrees(GEOMETRIC_ZENITH + degrees);
1370                } else {
1371                        twilight = getSunriseOffsetByDegrees(GEOMETRIC_ZENITH + degrees);
1372                }
1373                if (seaLevelSunrise == null || seaLevelSunset == null || twilight == null) {
1374                        return Double.MIN_VALUE;
1375                }
1376                double shaahZmanis = (seaLevelSunset.toEpochMilli() - seaLevelSunrise.toEpochMilli()) / 12.0;
1377                long riseSetToTwilight;
1378                if (sunset) {
1379                        riseSetToTwilight = twilight.toEpochMilli() - seaLevelSunset.toEpochMilli();
1380                } else {
1381                        riseSetToTwilight = seaLevelSunrise.toEpochMilli() - twilight.toEpochMilli();
1382                }
1383                return riseSetToTwilight / shaahZmanis;
1384        }
1385        
1386        /**
1387         * A utility method to calculate <em>zmanim</em> based on <a href="https://en.wikipedia.org/wiki/Moshe_Feinstein">Rav Moshe
1388         * Feinstein</a> and others as calculated in <a href="https://en.wikipedia.org/wiki/Mesivtha_Tifereth_Jerusalem">MTJ</a>, <a href=
1389         * "https://en.wikipedia.org/wiki/Mesivtha_Tifereth_Jerusalem">Yeshiva of Staten Island</a>, and Camp Yeshiva
1390         * of Staten Island and other calendars. The day is split in two, from <em>alos</em> / sunrise to <em>chatzos</em>, and the
1391         * second half of the day, from <em>chatzos</em> to sunset / <em>tzais</em>. Morning based times are calculated. based on the first
1392         * 6 hours of the day, and afternoon times based on the second half of the day. As an example, passing 0.5, a start of
1393         * <em>chatzos</em> and an end of day as sunset will return the time of <em>mincha gedola</em> GRA as half an hour <em>zmanis</em>
1394         * based on the second half of the day. Some <em>zmanim</em> calculations can be based on subtracting <em>shaos zmaniyos</em>
1395         * from the end of the day, and that is supported by passing a negative hour to this method.
1396         * 
1397         * @param startOfHalfDay The start of the half day. This would be <em>alos</em> or sunrise for morning based times such as
1398         *         <em>sof zman krias shema</em> and <em>chatzos</em> for afternoon based times such as <em>mincha gedola</em>.
1399         * @param endOfHalfDay The end of the half day. This would be <em>chatzos</em> for morning based times  such as <em>sof zman
1400         *         krias shema</em>  and sunset or <em>tzais</em> for afternoon based times such as <em>mincha gedola</em>.
1401         * @param hours The number of <em>shaos zmaniyos</em> (hours) to offset the beginning of the first or second half of the day. For
1402         *         example, 3 for <em>sof zman Shma</em>, 0.5 for <em>mincha gedola</em> (half an hour after <em>chatzos</em>) and 4.75
1403         *         for <em>plag hamincha</em>. If the number of hours is negative, it will subtract the number of <em>shaos zmaniyos</em>
1404         *         from the end of the day.
1405         * @return the <code>Instant</code> of <em>zman</em> based on calculation of the first or second half of the day. If the
1406         *         calculation can't be computed such as in the Arctic Circle where there is at least one day a year where the
1407         *         sun does not rise, and one where it does not set, a <code>null</code> will be returned. See detailed explanation
1408         *         on top of the {@link AstronomicalCalendar} documentation.
1409         * @see ComprehensiveZmanimCalendar#getFixedLocalChatzosHayom()
1410         */
1411        public Instant getHalfDayBasedZman(Instant startOfHalfDay, Instant endOfHalfDay, double hours) {
1412                if (startOfHalfDay == null || endOfHalfDay == null) {
1413                        return null;
1414                }
1415                Duration shaahZmanis = getHalfDayBasedShaahZmanis(startOfHalfDay, endOfHalfDay);
1416                if (shaahZmanis == null) { //defensive, should not be needed
1417                        return null;
1418                }
1419                
1420                long totalNanos = (long)(shaahZmanis.toNanos() * hours);
1421                Duration offsetDuration = Duration.ofNanos(totalNanos);
1422                
1423                if (hours >= 0) { // forward from start of day
1424                        return getTimeOffset(startOfHalfDay, offsetDuration);
1425                } else { // subtract from end of day
1426                        return getTimeOffset(endOfHalfDay, offsetDuration);
1427                }
1428        }
1429        
1430        /**
1431         * A utility method to calculate the length of a <em>sha'ah zmanis</em> based on 1/6 of a 6-hour day.
1432         * @param startOfHalfDay The start of the half-day. This would be <em>alos</em> or sunrise for the first half of the day, or
1433         *         <em>chatzos</em> for the second half of the day.
1434         * @param endOfHalfDay The end of the half-day. This would be <em>chatzos</em> for the first half of the day, or sunset or
1435         *         <em>tzais</em> for the second half of the day.
1436         * @return The <code>Duration</code> of a <em>shaah zmanis</em> based on 1/6 of a half-day. If the calculation can't be computed
1437         *         such as in the Arctic Circle where there is at least one day a year where the sun does not rise, and one where it does
1438         *         not set, a <code>null</code> will be returned. See detailed explanation on top of the {@link AstronomicalCalendar}
1439         *         documentation.
1440         * @see #getHalfDayBasedZman(Instant, Instant, double)
1441         * @see #isUseAstronomicalChatzosForOtherZmanim()
1442         * @todo Consider adjusting various shaah zmanis times to use this.
1443         */
1444        public Duration getHalfDayBasedShaahZmanis(Instant startOfHalfDay, Instant endOfHalfDay) {
1445                if (startOfHalfDay == null || endOfHalfDay == null) {
1446                        return null;
1447                }
1448                return Duration.between(startOfHalfDay, endOfHalfDay).dividedBy(6);
1449        }
1450        
1451        /**
1452         * @see java.lang.Object#equals(Object)
1453         */
1454        public boolean equals(Object object) {
1455                if (this == object) {
1456                        return true;
1457                }
1458                if (object == null || getClass() != object.getClass()) {
1459                        return false;
1460                }
1461                if (!super.equals(object)) {
1462                        return false;
1463                }
1464                ZmanimCalendar that = (ZmanimCalendar) object;
1465                return useElevation == that.useElevation
1466                                && useAstronomicalChatzos == that.useAstronomicalChatzos
1467                                && useAstronomicalChatzosForOtherZmanim == that.useAstronomicalChatzosForOtherZmanim
1468                                && candleLightingOffset == that.candleLightingOffset;
1469        }
1470
1471        /**
1472         * @see java.lang.Object#hashCode()
1473         */
1474        public int hashCode() {
1475                return Objects.hash(super.hashCode(), useElevation, useAstronomicalChatzos,
1476                                useAstronomicalChatzosForOtherZmanim, Double.hashCode(candleLightingOffset));
1477        }
1478}