├── .gitignore ├── lib ├── zmanim-1.4.0alpha.jar ├── zmanimAstronomical-1.4.0alpha.jar └── readme.md ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ ├── maven-wrapper.properties │ └── MavenWrapperDownloader.java ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── settings.gradle ├── .github └── workflows │ ├── pull_request_worklow.yml │ └── codeql-analysis.yml ├── src ├── main │ └── java │ │ └── com │ │ └── kosherjava │ │ └── zmanim │ │ ├── util │ │ ├── package-info.java │ │ ├── Time.java │ │ ├── GeoLocationUtils.java │ │ ├── SunTimesCalculator.java │ │ ├── Zman.java │ │ ├── AstronomicalCalculator.java │ │ └── NOAACalculator.java │ │ ├── package-info.java │ │ └── hebrewcalendar │ │ ├── package-info.java │ │ ├── YerushalmiYomiCalculator.java │ │ ├── YomiCalculator.java │ │ └── Daf.java └── test │ └── java │ └── com │ └── kosherjava │ └── zmanim │ └── hebrewcalendar │ ├── UT_JewishDateNavigation.java │ ├── YomiCalculatorTest.java │ ├── UT_JewishLeapYear.java │ ├── package.html │ ├── UT_YerushalmiTest.java │ ├── UT_DaysInGregorianMonth.java │ ├── UT_DaysInJewishMonth.java │ └── UT_GregorianDateNavigation.java ├── SECURITY.md ├── gradlew.bat ├── README.md ├── pom.xml ├── gradlew ├── mvnw.cmd ├── mvnw └── CHANGELOG.md /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .idea/ 3 | zmanim.iml 4 | .gradle 5 | build -------------------------------------------------------------------------------- /lib/zmanim-1.4.0alpha.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KosherJava/zmanim/HEAD/lib/zmanim-1.4.0alpha.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KosherJava/zmanim/HEAD/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KosherJava/zmanim/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /lib/zmanimAstronomical-1.4.0alpha.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/KosherJava/zmanim/HEAD/lib/zmanimAstronomical-1.4.0alpha.jar -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | */ 4 | 5 | rootProject.name = 'zmanim' 6 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.4.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /lib/readme.md: -------------------------------------------------------------------------------- 1 | The latest Jar files can be downloaded from [Maven Central's KosherJava zmanim project](https://search.maven.org/artifact/com.kosherjava/zmanim). Click on the latest version and direct download of the Jar is available on the top right corner. The Jars available here are old legacy versions that need to be updated. 2 | -------------------------------------------------------------------------------- /.github/workflows/pull_request_worklow.yml: -------------------------------------------------------------------------------- 1 | name: Run unit tests 2 | on: [push, pull_request] 3 | jobs: 4 | test: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - name: Check out Repository 8 | uses: actions/checkout@v4 9 | 10 | - name: Set up JDK 1.8 11 | uses: actions/setup-java@v4 12 | with: 13 | java-version: 8.0.232 14 | distribution: adopt 15 | 16 | - name: Run tests 17 | run: mvn test 18 | -------------------------------------------------------------------------------- /src/main/java/com/kosherjava/zmanim/util/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Utility classes for the Zmanim API including classes to calculate sun based times, {@link com.kosherjava.zmanim.util.GeoLocation location information}, 3 | * some {@link com.kosherjava.zmanim.util.ZmanimFormatter formatting} and {@link com.kosherjava.zmanim.util.GeoLocationUtils geographic location utilities}. Included in this package 4 | * are implementations for both the {@link com.kosherjava.zmanim.util.SunTimesCalculator USNO} and {@link com.kosherjava.zmanim.util.NOAACalculator NOAA / Jean Meeus} algorithms. 5 | * 6 | * @author © Eliyahu Hershfeld 2004 - 2022 7 | */ 8 | package com.kosherjava.zmanim.util; 9 | -------------------------------------------------------------------------------- /src/test/java/com/kosherjava/zmanim/hebrewcalendar/UT_JewishDateNavigation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011. Jay R. Gindin 3 | */ 4 | 5 | package com.kosherjava.zmanim.hebrewcalendar; 6 | 7 | import org.junit.*; 8 | 9 | import java.util.Calendar; 10 | 11 | /** 12 | * 13 | */ 14 | @SuppressWarnings({ "MagicNumber" }) 15 | public class UT_JewishDateNavigation { 16 | 17 | 18 | @Test 19 | public void jewishForwardMonthToMonth() { 20 | 21 | JewishDate jewishDate = new JewishDate(); 22 | jewishDate.setJewishDate(5771, 1, 1); 23 | Assert.assertEquals(5, jewishDate.getGregorianDayOfMonth()); 24 | Assert.assertEquals(3, jewishDate.getGregorianMonth()); 25 | Assert.assertEquals(2011, jewishDate.getGregorianYear()); 26 | } 27 | 28 | 29 | @Test 30 | public void computeRoshHashana5771() { 31 | 32 | // At one point, this test was failing as the JewishDate class spun through a never-ending loop... 33 | 34 | JewishDate jewishDate = new JewishDate(); 35 | jewishDate.setJewishDate(5771, 7, 1); 36 | Assert.assertEquals(9, jewishDate.getGregorianDayOfMonth()); 37 | Assert.assertEquals(8, jewishDate.getGregorianMonth()); 38 | Assert.assertEquals(2010, jewishDate.getGregorianYear()); 39 | } 40 | 41 | 42 | } // End of UT_JewishDateNavigation class 43 | -------------------------------------------------------------------------------- /src/test/java/com/kosherjava/zmanim/hebrewcalendar/YomiCalculatorTest.java: -------------------------------------------------------------------------------- 1 | package com.kosherjava.zmanim.hebrewcalendar; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | public class YomiCalculatorTest { 7 | private static HebrewDateFormatter hdf = new HebrewDateFormatter(); 8 | static { 9 | hdf.setHebrewFormat(true); 10 | } 11 | 12 | @Test 13 | public void testCorrectDaf1() { 14 | JewishCalendar jewishCalendar = new JewishCalendar(5685, JewishDate.KISLEV, 12); 15 | Daf daf = YomiCalculator.getDafYomiBavli(jewishCalendar); 16 | Assert.assertEquals(5, daf.getMasechtaNumber()); 17 | Assert.assertEquals(2, daf.getDaf()); 18 | System.out.println(hdf.formatDafYomiBavli(jewishCalendar.getDafYomiBavli())); 19 | } 20 | 21 | @Test 22 | public void testCorrectDaf2() { 23 | JewishCalendar jewishCalendar = new JewishCalendar(5736, JewishDate.ELUL, 26); 24 | Daf daf = YomiCalculator.getDafYomiBavli(jewishCalendar); 25 | Assert.assertEquals(4, daf.getMasechtaNumber()); 26 | Assert.assertEquals(14, daf.getDaf()); 27 | System.out.println(hdf.formatDafYomiBavli(jewishCalendar.getDafYomiBavli())); 28 | } 29 | 30 | @Test 31 | public void testCorrectDaf3() { 32 | JewishCalendar jewishCalendar = new JewishCalendar(5777, JewishDate.ELUL, 10); 33 | Daf daf = YomiCalculator.getDafYomiBavli(jewishCalendar); 34 | Assert.assertEquals(23, daf.getMasechtaNumber()); 35 | Assert.assertEquals(47, daf.getDaf()); 36 | System.out.println(hdf.formatDafYomiBavli(jewishCalendar.getDafYomiBavli())); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/kosherjava/zmanim/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * The KosherJava Zmanim library is an API for a specialized calendar that can calculate different 3 | * astronomical times including sunrise and sunset and Jewish zmanim or religious 4 | * times for prayers and other Jewish religious duties. These classes extend the {@link java.util.GregorianCalendar} and can therefore use the 5 | * standard Calendar functionality to change dates etc. For non-religious astronomical / solar calculations such as sunrise, sunset and twilight, use the {@link com.kosherjava.zmanim.AstronomicalCalendar}. The {@link com.kosherjava.zmanim.ZmanimCalendar} contains the most 8 | * commonly used zmanim or religious time calculations. For a much more extensive list of zmanim use the {@link com.kosherjava.zmanim.ComplexZmanimCalendar}. 9 | * Note: It is important to read the technical notes on top of the {@link com.kosherjava.zmanim.util.AstronomicalCalculator} documentation. 10 | *
12 | Year Cheshvan Kislev LEAP? TYPE 13 | 5769 29 30 No Qesidrah 14 | 5770 30 30 No Shalem 15 | 5771 30 30 Yes Shalem Leap 16 | 5772 29 30 No Qesidrah 17 | 5773 29 29 No Haser 18 | 5774 30 30 Yes Shalem Leap 19 | 5775 29 30 No Qesidrah 20 | 5776 30 30 Yes Shalem Leap 21 | 5777 29 29 No Haser 22 | 5778 29 30 No Qesidrah 23 | 5779 30 30 Yes Shalem Leap 24 | 5780 30 30 No Shalem 25 | 5781 29 29 No Haser 26 | 5782 29 30 Yes Qesidrah Leap 27 | 5783 30 30 No Shalem 28 | 5784 29 29 Yes Qesidrah Leap 29 | 5785 30 30 No Shalem 30 | 5786 29 30 No Qesidrah 31 | 5787 30 30 Yes Shalem Leap 32 | 5788 30 30 No Shalem 33 | 5789 29 30 No Qesidrah 34 | 5790 29 29 Yes Haser Leap 35 | 5791 30 30 No Shalem 36 | 5792 29 30 No Qesidrah 37 | 5793 29 29 Yes Haser Leap 38 | 5794 30 30 No Shalem 39 | 5795 30 30 Yes Shalem Leap 40 |41 | 42 | 43 | Another bit of help is just to have a listing of the Hebrew months. The API declares Nisan as the first month 44 | and Adar (or Adar II) as the last month of the year: 45 |
Calendar) and end date (also as a Calendar).
120 | *
121 | * @param start date to start calculating from
122 | * @param end date to finish calculating at
123 | * @return the number of special days between the start and end dates
124 | */
125 | private static int getNumOfSpecialDays(Calendar start, Calendar end) {
126 |
127 | // Find the start and end Jewish years
128 | int startYear = new JewishCalendar(start).getJewishYear();
129 | int endYear = new JewishCalendar(end).getJewishYear();
130 |
131 | // Value to return
132 | int specialDays = 0;
133 |
134 | //Instant of special Dates
135 | JewishCalendar yom_kippur = new JewishCalendar(5770, 7, 10);
136 | JewishCalendar tisha_beav = new JewishCalendar(5770, 5, 9);
137 |
138 | // Go over the years and find special dates
139 | for (int i = startYear; i <= endYear; i++) {
140 | yom_kippur.setJewishYear(i);
141 | tisha_beav.setJewishYear(i);
142 |
143 | if (isBetween(start, yom_kippur.getGregorianCalendar(), end)) {
144 | specialDays++;
145 | }
146 | if (isBetween(start, tisha_beav.getGregorianCalendar(), end)) {
147 | specialDays++;
148 | }
149 | }
150 |
151 | return specialDays;
152 | }
153 |
154 | /**
155 | * Return if the date is between two dates
156 | *
157 | * @param start the start date
158 | * @param date the date being compared
159 | * @param end the end date
160 | * @return if the date is between the start and end dates
161 | */
162 | private static boolean isBetween(Calendar start, Calendar date, Calendar end ) {
163 | return start.before(date) && end.after(date);
164 | }
165 |
166 | /**
167 | * Return the number of days between the dates passed in
168 | * @param start the start date
169 | * @param end the end date
170 | * @return the number of days between the start and end dates
171 | */
172 | private static long getDiffBetweenDays(Calendar start, Calendar end) {
173 | return (end.getTimeInMillis() - start.getTimeInMillis()) / DAY_MILIS;
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/mvnw.cmd:
--------------------------------------------------------------------------------
1 | @REM ----------------------------------------------------------------------------
2 | @REM Licensed to the Apache Software Foundation (ASF) under one
3 | @REM or more contributor license agreements. See the NOTICE file
4 | @REM distributed with this work for additional information
5 | @REM regarding copyright ownership. The ASF licenses this file
6 | @REM to you under the Apache License, Version 2.0 (the
7 | @REM "License"); you may not use this file except in compliance
8 | @REM with the License. You may obtain a copy of the License at
9 | @REM
10 | @REM http://www.apache.org/licenses/LICENSE-2.0
11 | @REM
12 | @REM Unless required by applicable law or agreed to in writing,
13 | @REM software distributed under the License is distributed on an
14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 | @REM KIND, either express or implied. See the License for the
16 | @REM specific language governing permissions and limitations
17 | @REM under the License.
18 | @REM ----------------------------------------------------------------------------
19 |
20 | @REM ----------------------------------------------------------------------------
21 | @REM Maven Start Up Batch script
22 | @REM
23 | @REM Required ENV vars:
24 | @REM JAVA_HOME - location of a JDK home dir
25 | @REM
26 | @REM Optional ENV vars
27 | @REM M2_HOME - location of maven2's installed home dir
28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
31 | @REM e.g. to debug Maven itself, use
32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
34 | @REM ----------------------------------------------------------------------------
35 |
36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
37 | @echo off
38 | @REM set title of command window
39 | title %0
40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
42 |
43 | @REM set %HOME% to equivalent of $HOME
44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
45 |
46 | @REM Execute a user defined script before this one
47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending
49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
51 | :skipRcPre
52 |
53 | @setlocal
54 |
55 | set ERROR_CODE=0
56 |
57 | @REM To isolate internal variables from possible post scripts, we use another setlocal
58 | @setlocal
59 |
60 | @REM ==== START VALIDATION ====
61 | if not "%JAVA_HOME%" == "" goto OkJHome
62 |
63 | echo.
64 | echo Error: JAVA_HOME not found in your environment. >&2
65 | echo Please set the JAVA_HOME variable in your environment to match the >&2
66 | echo location of your Java installation. >&2
67 | echo.
68 | goto error
69 |
70 | :OkJHome
71 | if exist "%JAVA_HOME%\bin\java.exe" goto init
72 |
73 | echo.
74 | echo Error: JAVA_HOME is set to an invalid directory. >&2
75 | echo JAVA_HOME = "%JAVA_HOME%" >&2
76 | echo Please set the JAVA_HOME variable in your environment to match the >&2
77 | echo location of your Java installation. >&2
78 | echo.
79 | goto error
80 |
81 | @REM ==== END VALIDATION ====
82 |
83 | :init
84 |
85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
86 | @REM Fallback to current working directory if not found.
87 |
88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
90 |
91 | set EXEC_DIR=%CD%
92 | set WDIR=%EXEC_DIR%
93 | :findBaseDir
94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound
95 | cd ..
96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound
97 | set WDIR=%CD%
98 | goto findBaseDir
99 |
100 | :baseDirFound
101 | set MAVEN_PROJECTBASEDIR=%WDIR%
102 | cd "%EXEC_DIR%"
103 | goto endDetectBaseDir
104 |
105 | :baseDirNotFound
106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
107 | cd "%EXEC_DIR%"
108 |
109 | :endDetectBaseDir
110 |
111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
112 |
113 | @setlocal EnableExtensions EnableDelayedExpansion
114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
116 |
117 | :endReadAdditionalConfig
118 |
119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
122 |
123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
124 |
125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
127 | )
128 |
129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data.
131 | if exist %WRAPPER_JAR% (
132 | if "%MVNW_VERBOSE%" == "true" (
133 | echo Found %WRAPPER_JAR%
134 | )
135 | ) else (
136 | if not "%MVNW_REPOURL%" == "" (
137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
138 | )
139 | if "%MVNW_VERBOSE%" == "true" (
140 | echo Couldn't find %WRAPPER_JAR%, downloading it ...
141 | echo Downloading from: %DOWNLOAD_URL%
142 | )
143 |
144 | powershell -Command "&{"^
145 | "$webclient = new-object System.Net.WebClient;"^
146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
148 | "}"^
149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
150 | "}"
151 | if "%MVNW_VERBOSE%" == "true" (
152 | echo Finished downloading %WRAPPER_JAR%
153 | )
154 | )
155 | @REM End of extension
156 |
157 | @REM Provide a "standardized" way to retrieve the CLI args that will
158 | @REM work with both Windows and non-Windows executions.
159 | set MAVEN_CMD_LINE_ARGS=%*
160 |
161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
162 | if ERRORLEVEL 1 goto error
163 | goto end
164 |
165 | :error
166 | set ERROR_CODE=1
167 |
168 | :end
169 | @endlocal & set ERROR_CODE=%ERROR_CODE%
170 |
171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending
173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
175 | :skipRcPost
176 |
177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause
179 |
180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
181 |
182 | exit /B %ERROR_CODE%
183 |
--------------------------------------------------------------------------------
/src/main/java/com/kosherjava/zmanim/hebrewcalendar/YomiCalculator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Zmanim Java API
3 | * Copyright (C) 2011-2025 Eliyahu Hershfeld
4 | *
5 | * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General
6 | * Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option)
7 | * any later version.
8 | *
9 | * This library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied
10 | * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11 | * details.
12 | * You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to
13 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA,
14 | * or connect to: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
15 | */
16 | package com.kosherjava.zmanim.hebrewcalendar;
17 |
18 | import java.util.Calendar;
19 | import java.util.GregorianCalendar;
20 |
21 | /**
22 | * This class calculates the Daf Yomi Bavli page (daf) for a given date. To calculate Daf Yomi Yerushalmi
23 | * use the {@link YerushalmiYomiCalculator}. The library may cover Mishna Yomi etc. at some point in the future.
24 | *
25 | * @author © Bob Newell (original C code)
26 | * @author © Eliyahu Hershfeld 2011 - 2025
27 | */
28 | public class YomiCalculator {
29 |
30 | /**
31 | * The start date of the first Daf Yomi Bavli cycle of September 11, 1923 / Rosh Hashana 5684.
32 | */
33 | private static final Calendar dafYomiStartDay = new GregorianCalendar(1923, Calendar.SEPTEMBER, 11);
34 | /** The start date of the first Daf Yomi Bavli cycle in the Julian calendar. Used internally for calculations.*/
35 | private static final int dafYomiJulianStartDay = getJulianDay(dafYomiStartDay);
36 | /**
37 | * The date that the pagination for the Daf Yomi Maseches Shekalim changed to use the commonly used Vilna
38 | * Shas pagination from the no longer commonly available Zhitomir / Slavuta Shas used by Rabbi Meir Shapiro.
39 | */
40 | private static final Calendar shekalimChangeDay = new GregorianCalendar(1975, Calendar.JUNE, 24);
41 |
42 | /** The Julian date that the cycle for Shekalim changed.
43 | * @see #getDafYomiBavli(JewishCalendar) for details.
44 | */
45 | private static final int shekalimJulianChangeDay = getJulianDay(shekalimChangeDay);
46 |
47 | /**
48 | * Default constructor.
49 | */
50 | public YomiCalculator() {
51 | // nothing here
52 | }
53 |
54 | /**
55 | * Returns the Daf Yomi Bavli {@link Daf} for a given date. The first Daf Yomi cycle
57 | * started on Rosh Hashana 5684 (September 11, 1923) and calculations prior to this date will result in an
58 | * IllegalArgumentException thrown. For historical calculations (supported by this method), it is important to note
59 | * that a change in length of the cycle was instituted starting in the eighth Daf Yomi cycle beginning on June 24,
60 | * 1975. The Daf Yomi Bavli cycle has a single masechta of the Talmud Yerushalmi - Shekalim as part of the cycle.
61 | * Unlike the Bavli where the number of daf per masechta was standardized since the original Bomberg Edition published from 1520 - 1523, there is no
63 | * uniform page length in the Yerushalmi. The early cycles had the Yerushalmi Shekalim length of 13 days following the
64 | * Slavuta/Zhytomyr
66 | * Shas used by Rabbi Meir Shapiro. With the start of the eighth Daf Yomi
67 | * cycle beginning on June 24, 1975, the length of the Yerushalmi Shekalim was changed from 13 to 22 daf to follow
68 | * the Vilna Shas that is in common use today.
69 | *
70 | * @param jewishCalendar
71 | * The JewishCalendar date for calculation. TODO: this can be changed to use a regular GregorianCalendar since
72 | * there is nothing specific to the JewishCalendar in this class.
73 | * @return the {@link Daf}.
74 | *
75 | * @throws IllegalArgumentException
76 | * if the date is prior to the September 11, 1923, the start date of the first Daf Yomi cycle.
77 | */
78 | public static Daf getDafYomiBavli(JewishCalendar jewishCalendar) {
79 | /*
80 | * The number of daf per masechta. Since the number of blatt in Shekalim changed on the 8th Daf Yomi cycle
81 | * beginning on June 24, 1975, from 13 to 22, the actual calculation for blattPerMasechta[4] will later be
82 | * adjusted based on the cycle.
83 | */
84 | int[] blattPerMasechta = { 64, 157, 105, 121, 22, 88, 56, 40, 35, 31, 32, 29, 27, 122, 112, 91, 66, 49, 90, 82,
85 | 119, 119, 176, 113, 24, 49, 76, 14, 120, 110, 142, 61, 34, 34, 28, 22, 4, 9, 5, 73 };
86 | Calendar calendar = jewishCalendar.getGregorianCalendar();
87 |
88 | Daf dafYomi = null;
89 | int julianDay = getJulianDay(calendar);
90 | int cycleNo;
91 | int dafNo;
92 | if (calendar.before(dafYomiStartDay)) {
93 | // TODO: should we return a null or throw an IllegalArgumentException?
94 | throw new IllegalArgumentException(calendar + " is prior to organized Daf Yomi Bavli cycles that started on "
95 | + dafYomiStartDay);
96 | }
97 | if (calendar.equals(shekalimChangeDay) || calendar.after(shekalimChangeDay)) {
98 | cycleNo = 8 + ((julianDay - shekalimJulianChangeDay) / 2711);
99 | dafNo = ((julianDay - shekalimJulianChangeDay) % 2711);
100 | } else {
101 | cycleNo = 1 + ((julianDay - dafYomiJulianStartDay) / 2702);
102 | dafNo = ((julianDay - dafYomiJulianStartDay) % 2702);
103 | }
104 |
105 | int total = 0;
106 | int masechta = -1;
107 | int blatt;
108 |
109 | // Fix Shekalim for old cycles.
110 | if (cycleNo <= 7) {
111 | blattPerMasechta[4] = 13;
112 | }
113 |
114 | // Finally find the daf.
115 | for (int i : blattPerMasechta) {
116 | masechta++;
117 | total = total + i - 1;
118 | if (dafNo < total) {
119 | blatt = 1 + i - (total - dafNo);
120 | // Fiddle with the weird ones near the end.
121 | if (masechta == 36) {
122 | blatt += 21;
123 | } else if (masechta == 37) {
124 | blatt += 24;
125 | } else if (masechta == 38) {
126 | blatt += 32;
127 | }
128 | dafYomi = new Daf(masechta, blatt);
129 | break;
130 | }
131 | }
132 |
133 | return dafYomi;
134 | }
135 |
136 | /**
137 | * Return the Julian day from a Java Calendar.
138 | *
139 | * @param calendar
140 | * The Java Calendar of the date to be calculated
141 | * @return the Julian day number corresponding to the date
142 | */
143 | private static int getJulianDay(Calendar calendar) {
144 | int year = calendar.get(Calendar.YEAR);
145 | int month = calendar.get(Calendar.MONTH) + 1;
146 | int day = calendar.get(Calendar.DAY_OF_MONTH);
147 | if (month <= 2) {
148 | year -= 1;
149 | month += 12;
150 | }
151 | int a = year / 100;
152 | int b = 2 - a + a / 4;
153 | return (int) (Math.floor(365.25 * (year + 4716)) + Math.floor(30.6001 * (month + 1)) + day + b - 1524.5);
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/src/test/java/com/kosherjava/zmanim/hebrewcalendar/UT_GregorianDateNavigation.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2011. Jay R. Gindin
3 | */
4 |
5 | package com.kosherjava.zmanim.hebrewcalendar;
6 |
7 | import org.junit.*;
8 |
9 | import java.util.Calendar;
10 |
11 | /**
12 | * Checks that we can roll forward & backward the gregorian dates...
13 | */
14 | @SuppressWarnings({ "MagicNumber" })
15 | public class UT_GregorianDateNavigation {
16 |
17 | @Test
18 | public void gregorianForwardMonthToMonth() {
19 |
20 | Calendar cal = Calendar.getInstance();
21 | cal.set(Calendar.YEAR, 2011);
22 | cal.set(Calendar.MONTH, Calendar.JANUARY);
23 | cal.set(Calendar.DATE, 31);
24 |
25 | JewishDate hebrewDate = new JewishDate(cal);
26 | Assert.assertEquals(5771, hebrewDate.getJewishYear());
27 | Assert.assertEquals(11, hebrewDate.getJewishMonth());
28 | Assert.assertEquals(26, hebrewDate.getJewishDayOfMonth());
29 |
30 | hebrewDate.forward(Calendar.DATE, 1);
31 | Assert.assertEquals(1, hebrewDate.getGregorianMonth());
32 | Assert.assertEquals(1, hebrewDate.getGregorianDayOfMonth());
33 | Assert.assertEquals(11, hebrewDate.getJewishMonth());
34 | Assert.assertEquals(27, hebrewDate.getJewishDayOfMonth());
35 |
36 | cal.set(Calendar.MONTH, Calendar.FEBRUARY);
37 | cal.set(Calendar.DATE, 28);
38 | hebrewDate.setDate(cal);
39 | Assert.assertEquals(1, hebrewDate.getGregorianMonth());
40 | Assert.assertEquals(28, hebrewDate.getGregorianDayOfMonth());
41 | Assert.assertEquals(12, hebrewDate.getJewishMonth());
42 | Assert.assertEquals(24, hebrewDate.getJewishDayOfMonth());
43 |
44 | hebrewDate.forward(Calendar.DATE, 1);
45 | Assert.assertEquals(2, hebrewDate.getGregorianMonth());
46 | Assert.assertEquals(1, hebrewDate.getGregorianDayOfMonth());
47 | Assert.assertEquals(12, hebrewDate.getJewishMonth());
48 | Assert.assertEquals(25, hebrewDate.getJewishDayOfMonth());
49 |
50 | cal.set(Calendar.MONTH, Calendar.MARCH);
51 | cal.set(Calendar.DATE, 31);
52 | hebrewDate.setDate(cal);
53 | hebrewDate.forward(Calendar.DATE, 1);
54 | Assert.assertEquals(3, hebrewDate.getGregorianMonth());
55 | Assert.assertEquals(1, hebrewDate.getGregorianDayOfMonth());
56 | Assert.assertEquals(13, hebrewDate.getJewishMonth());
57 | Assert.assertEquals(26, hebrewDate.getJewishDayOfMonth());
58 |
59 | cal.set(Calendar.MONTH, Calendar.APRIL);
60 | cal.set(Calendar.DATE, 30);
61 | hebrewDate.setDate(cal);
62 | hebrewDate.forward(Calendar.DATE, 1);
63 | Assert.assertEquals(4, hebrewDate.getGregorianMonth());
64 | Assert.assertEquals(1, hebrewDate.getGregorianDayOfMonth());
65 | Assert.assertEquals(1, hebrewDate.getJewishMonth());
66 | Assert.assertEquals(27, hebrewDate.getJewishDayOfMonth());
67 |
68 | cal.set(Calendar.MONTH, Calendar.MAY);
69 | cal.set(Calendar.DATE, 31);
70 | hebrewDate.setDate(cal);
71 | hebrewDate.forward(Calendar.DATE, 1);
72 | Assert.assertEquals(5, hebrewDate.getGregorianMonth());
73 | Assert.assertEquals(1, hebrewDate.getGregorianDayOfMonth());
74 | Assert.assertEquals(2, hebrewDate.getJewishMonth());
75 | Assert.assertEquals(28, hebrewDate.getJewishDayOfMonth());
76 |
77 | cal.set(Calendar.MONTH, Calendar.JUNE);
78 | cal.set(Calendar.DATE, 30);
79 | hebrewDate.setDate(cal);
80 | hebrewDate.forward(Calendar.DATE, 1);
81 | Assert.assertEquals(6, hebrewDate.getGregorianMonth());
82 | Assert.assertEquals(1, hebrewDate.getGregorianDayOfMonth());
83 | Assert.assertEquals(3, hebrewDate.getJewishMonth());
84 | Assert.assertEquals(29, hebrewDate.getJewishDayOfMonth());
85 |
86 | cal.set(Calendar.MONTH, Calendar.JULY);
87 | cal.set(Calendar.DATE, 31);
88 | hebrewDate.setDate(cal);
89 | hebrewDate.forward(Calendar.DATE, 1);
90 | Assert.assertEquals(7, hebrewDate.getGregorianMonth());
91 | Assert.assertEquals(1, hebrewDate.getGregorianDayOfMonth());
92 | Assert.assertEquals(5, hebrewDate.getJewishMonth());
93 | Assert.assertEquals(1, hebrewDate.getJewishDayOfMonth());
94 |
95 | cal.set(Calendar.MONTH, Calendar.AUGUST);
96 | cal.set(Calendar.DATE, 31);
97 | hebrewDate.setDate(cal);
98 | hebrewDate.forward(Calendar.DATE, 1);
99 | Assert.assertEquals(8, hebrewDate.getGregorianMonth());
100 | Assert.assertEquals(1, hebrewDate.getGregorianDayOfMonth());
101 | Assert.assertEquals(6, hebrewDate.getJewishMonth());
102 | Assert.assertEquals(2, hebrewDate.getJewishDayOfMonth());
103 |
104 | cal.set(Calendar.MONTH, Calendar.SEPTEMBER);
105 | cal.set(Calendar.DATE, 30);
106 | hebrewDate.setDate(cal);
107 | hebrewDate.forward(Calendar.DATE, 1);
108 | Assert.assertEquals(9, hebrewDate.getGregorianMonth());
109 | Assert.assertEquals(1, hebrewDate.getGregorianDayOfMonth());
110 | Assert.assertEquals(7, hebrewDate.getJewishMonth());
111 | Assert.assertEquals(3, hebrewDate.getJewishDayOfMonth());
112 |
113 | cal.set(Calendar.MONTH, Calendar.OCTOBER);
114 | cal.set(Calendar.DATE, 31);
115 | hebrewDate.setDate(cal);
116 | hebrewDate.forward(Calendar.DATE, 1);
117 | Assert.assertEquals(10, hebrewDate.getGregorianMonth());
118 | Assert.assertEquals(1, hebrewDate.getGregorianDayOfMonth());
119 | Assert.assertEquals(5772, hebrewDate.getJewishYear());
120 | Assert.assertEquals(8, hebrewDate.getJewishMonth());
121 | Assert.assertEquals(4, hebrewDate.getJewishDayOfMonth());
122 |
123 | cal.set(Calendar.MONTH, Calendar.NOVEMBER);
124 | cal.set(Calendar.DATE, 30);
125 | hebrewDate.setDate(cal);
126 | hebrewDate.forward(Calendar.DATE, 1);
127 | Assert.assertEquals(11, hebrewDate.getGregorianMonth());
128 | Assert.assertEquals(1, hebrewDate.getGregorianDayOfMonth());
129 | Assert.assertEquals(9, hebrewDate.getJewishMonth());
130 | Assert.assertEquals(5, hebrewDate.getJewishDayOfMonth());
131 |
132 | cal.set(Calendar.MONTH, Calendar.DECEMBER);
133 | cal.set(Calendar.DATE, 31);
134 | hebrewDate.setDate(cal);
135 | hebrewDate.forward(Calendar.DATE, 1);
136 | Assert.assertEquals(2012, hebrewDate.getGregorianYear());
137 | Assert.assertEquals(0, hebrewDate.getGregorianMonth());
138 | Assert.assertEquals(1, hebrewDate.getGregorianDayOfMonth());
139 | Assert.assertEquals(10, hebrewDate.getJewishMonth());
140 | Assert.assertEquals(6, hebrewDate.getJewishDayOfMonth());
141 | }
142 |
143 |
144 | @Test
145 | public void gregorianBackwardMonthToMonth() {
146 |
147 | Calendar cal = Calendar.getInstance();
148 | cal.set(Calendar.YEAR, 2011);
149 | cal.set(Calendar.MONTH, Calendar.JANUARY);
150 | cal.set(Calendar.DATE, 1);
151 |
152 | JewishDate hebrewDate = new JewishDate(cal);
153 | hebrewDate.back();
154 | Assert.assertEquals(2010, hebrewDate.getGregorianYear());
155 | Assert.assertEquals(11, hebrewDate.getGregorianMonth());
156 | Assert.assertEquals(31, hebrewDate.getGregorianDayOfMonth());
157 | Assert.assertEquals(10, hebrewDate.getJewishMonth());
158 | Assert.assertEquals(24, hebrewDate.getJewishDayOfMonth());
159 |
160 | cal.set(Calendar.DATE, 1);
161 | cal.set(Calendar.MONTH, Calendar.DECEMBER);
162 | cal.set(Calendar.YEAR, 2010);
163 | hebrewDate.setDate(cal);
164 | hebrewDate.back();
165 | Assert.assertEquals(10, hebrewDate.getGregorianMonth());
166 | Assert.assertEquals(30, hebrewDate.getGregorianDayOfMonth());
167 | Assert.assertEquals(9, hebrewDate.getJewishMonth());
168 | Assert.assertEquals(23, hebrewDate.getJewishDayOfMonth());
169 |
170 | cal.set(Calendar.DATE, 1);
171 | cal.set(Calendar.MONTH, Calendar.NOVEMBER);
172 | hebrewDate.setDate(cal);
173 | hebrewDate.back();
174 | Assert.assertEquals(9, hebrewDate.getGregorianMonth());
175 | Assert.assertEquals(31, hebrewDate.getGregorianDayOfMonth());
176 | Assert.assertEquals(8, hebrewDate.getJewishMonth());
177 | Assert.assertEquals(23, hebrewDate.getJewishDayOfMonth());
178 |
179 | cal.set(Calendar.DATE, 1);
180 | cal.set(Calendar.MONTH, Calendar.OCTOBER);
181 | hebrewDate.setDate(cal);
182 | hebrewDate.back();
183 | Assert.assertEquals(8, hebrewDate.getGregorianMonth());
184 | Assert.assertEquals(30, hebrewDate.getGregorianDayOfMonth());
185 | Assert.assertEquals(7, hebrewDate.getJewishMonth());
186 | Assert.assertEquals(22, hebrewDate.getJewishDayOfMonth());
187 |
188 | cal.set(Calendar.DATE, 1);
189 | cal.set(Calendar.MONTH, Calendar.SEPTEMBER);
190 | hebrewDate.setDate(cal);
191 | hebrewDate.back();
192 | Assert.assertEquals(7, hebrewDate.getGregorianMonth());
193 | Assert.assertEquals(31, hebrewDate.getGregorianDayOfMonth());
194 | Assert.assertEquals(5770, hebrewDate.getJewishYear());
195 | Assert.assertEquals(6, hebrewDate.getJewishMonth());
196 | Assert.assertEquals(21, hebrewDate.getJewishDayOfMonth());
197 |
198 | cal.set(Calendar.DATE, 1);
199 | cal.set(Calendar.MONTH, Calendar.AUGUST);
200 | hebrewDate.setDate(cal);
201 | hebrewDate.back();
202 | Assert.assertEquals(6, hebrewDate.getGregorianMonth());
203 | Assert.assertEquals(31, hebrewDate.getGregorianDayOfMonth());
204 | Assert.assertEquals(5, hebrewDate.getJewishMonth());
205 | Assert.assertEquals(20, hebrewDate.getJewishDayOfMonth());
206 |
207 | cal.set(Calendar.DATE, 1);
208 | cal.set(Calendar.MONTH, Calendar.JULY);
209 | hebrewDate.setDate(cal);
210 | hebrewDate.back();
211 | Assert.assertEquals(5, hebrewDate.getGregorianMonth());
212 | Assert.assertEquals(30, hebrewDate.getGregorianDayOfMonth());
213 | Assert.assertEquals(4, hebrewDate.getJewishMonth());
214 | Assert.assertEquals(18, hebrewDate.getJewishDayOfMonth());
215 |
216 | cal.set(Calendar.DATE, 1);
217 | cal.set(Calendar.MONTH, Calendar.JUNE);
218 | hebrewDate.setDate(cal);
219 | hebrewDate.back();
220 | Assert.assertEquals(4, hebrewDate.getGregorianMonth());
221 | Assert.assertEquals(31, hebrewDate.getGregorianDayOfMonth());
222 | Assert.assertEquals(3, hebrewDate.getJewishMonth());
223 | Assert.assertEquals(18, hebrewDate.getJewishDayOfMonth());
224 |
225 | cal.set(Calendar.DATE, 1);
226 | cal.set(Calendar.MONTH, Calendar.MAY);
227 | hebrewDate.setDate(cal);
228 | hebrewDate.back();
229 | Assert.assertEquals(3, hebrewDate.getGregorianMonth());
230 | Assert.assertEquals(30, hebrewDate.getGregorianDayOfMonth());
231 | Assert.assertEquals(2, hebrewDate.getJewishMonth());
232 | Assert.assertEquals(16, hebrewDate.getJewishDayOfMonth());
233 |
234 | cal.set(Calendar.DATE, 1);
235 | cal.set(Calendar.MONTH, Calendar.APRIL);
236 | hebrewDate.setDate(cal);
237 | hebrewDate.back();
238 | Assert.assertEquals(2, hebrewDate.getGregorianMonth());
239 | Assert.assertEquals(31, hebrewDate.getGregorianDayOfMonth());
240 | Assert.assertEquals(1, hebrewDate.getJewishMonth());
241 | Assert.assertEquals(16, hebrewDate.getJewishDayOfMonth());
242 |
243 | cal.set(Calendar.DATE, 1);
244 | cal.set(Calendar.MONTH, Calendar.MARCH);
245 | hebrewDate.setDate(cal);
246 | hebrewDate.back();
247 | Assert.assertEquals(1, hebrewDate.getGregorianMonth());
248 | Assert.assertEquals(28, hebrewDate.getGregorianDayOfMonth());
249 | Assert.assertEquals(12, hebrewDate.getJewishMonth());
250 | Assert.assertEquals(14, hebrewDate.getJewishDayOfMonth());
251 |
252 | cal.set(Calendar.DATE, 1);
253 | cal.set(Calendar.MONTH, Calendar.FEBRUARY);
254 | hebrewDate.setDate(cal);
255 | hebrewDate.back();
256 | Assert.assertEquals(0, hebrewDate.getGregorianMonth());
257 | Assert.assertEquals(31, hebrewDate.getGregorianDayOfMonth());
258 | Assert.assertEquals(11, hebrewDate.getJewishMonth());
259 | Assert.assertEquals(16, hebrewDate.getJewishDayOfMonth());
260 |
261 | }
262 |
263 |
264 |
265 | } // End of UT_GregorianDateNavigation class
266 |
--------------------------------------------------------------------------------
/mvnw:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # ----------------------------------------------------------------------------
3 | # Licensed to the Apache Software Foundation (ASF) under one
4 | # or more contributor license agreements. See the NOTICE file
5 | # distributed with this work for additional information
6 | # regarding copyright ownership. The ASF licenses this file
7 | # to you under the Apache License, Version 2.0 (the
8 | # "License"); you may not use this file except in compliance
9 | # with the License. You may obtain a copy of the License at
10 | #
11 | # http://www.apache.org/licenses/LICENSE-2.0
12 | #
13 | # Unless required by applicable law or agreed to in writing,
14 | # software distributed under the License is distributed on an
15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 | # KIND, either express or implied. See the License for the
17 | # specific language governing permissions and limitations
18 | # under the License.
19 | # ----------------------------------------------------------------------------
20 |
21 | # ----------------------------------------------------------------------------
22 | # Maven Start Up Batch script
23 | #
24 | # Required ENV vars:
25 | # ------------------
26 | # JAVA_HOME - location of a JDK home dir
27 | #
28 | # Optional ENV vars
29 | # -----------------
30 | # M2_HOME - location of maven2's installed home dir
31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven
32 | # e.g. to debug Maven itself, use
33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files
35 | # ----------------------------------------------------------------------------
36 |
37 | if [ -z "$MAVEN_SKIP_RC" ] ; then
38 |
39 | if [ -f /etc/mavenrc ] ; then
40 | . /etc/mavenrc
41 | fi
42 |
43 | if [ -f "$HOME/.mavenrc" ] ; then
44 | . "$HOME/.mavenrc"
45 | fi
46 |
47 | fi
48 |
49 | # OS specific support. $var _must_ be set to either true or false.
50 | cygwin=false;
51 | darwin=false;
52 | mingw=false
53 | case "`uname`" in
54 | CYGWIN*) cygwin=true ;;
55 | MINGW*) mingw=true;;
56 | Darwin*) darwin=true
57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
59 | if [ -z "$JAVA_HOME" ]; then
60 | if [ -x "/usr/libexec/java_home" ]; then
61 | export JAVA_HOME="`/usr/libexec/java_home`"
62 | else
63 | export JAVA_HOME="/Library/Java/Home"
64 | fi
65 | fi
66 | ;;
67 | esac
68 |
69 | if [ -z "$JAVA_HOME" ] ; then
70 | if [ -r /etc/gentoo-release ] ; then
71 | JAVA_HOME=`java-config --jre-home`
72 | fi
73 | fi
74 |
75 | if [ -z "$M2_HOME" ] ; then
76 | ## resolve links - $0 may be a link to maven's home
77 | PRG="$0"
78 |
79 | # need this for relative symlinks
80 | while [ -h "$PRG" ] ; do
81 | ls=`ls -ld "$PRG"`
82 | link=`expr "$ls" : '.*-> \(.*\)$'`
83 | if expr "$link" : '/.*' > /dev/null; then
84 | PRG="$link"
85 | else
86 | PRG="`dirname "$PRG"`/$link"
87 | fi
88 | done
89 |
90 | saveddir=`pwd`
91 |
92 | M2_HOME=`dirname "$PRG"`/..
93 |
94 | # make it fully qualified
95 | M2_HOME=`cd "$M2_HOME" && pwd`
96 |
97 | cd "$saveddir"
98 | # echo Using m2 at $M2_HOME
99 | fi
100 |
101 | # For Cygwin, ensure paths are in UNIX format before anything is touched
102 | if $cygwin ; then
103 | [ -n "$M2_HOME" ] &&
104 | M2_HOME=`cygpath --unix "$M2_HOME"`
105 | [ -n "$JAVA_HOME" ] &&
106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
107 | [ -n "$CLASSPATH" ] &&
108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
109 | fi
110 |
111 | # For Mingw, ensure paths are in UNIX format before anything is touched
112 | if $mingw ; then
113 | [ -n "$M2_HOME" ] &&
114 | M2_HOME="`(cd "$M2_HOME"; pwd)`"
115 | [ -n "$JAVA_HOME" ] &&
116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
117 | fi
118 |
119 | if [ -z "$JAVA_HOME" ]; then
120 | javaExecutable="`which javac`"
121 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
122 | # readlink(1) is not available as standard on Solaris 10.
123 | readLink=`which readlink`
124 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
125 | if $darwin ; then
126 | javaHome="`dirname \"$javaExecutable\"`"
127 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
128 | else
129 | javaExecutable="`readlink -f \"$javaExecutable\"`"
130 | fi
131 | javaHome="`dirname \"$javaExecutable\"`"
132 | javaHome=`expr "$javaHome" : '\(.*\)/bin'`
133 | JAVA_HOME="$javaHome"
134 | export JAVA_HOME
135 | fi
136 | fi
137 | fi
138 |
139 | if [ -z "$JAVACMD" ] ; then
140 | if [ -n "$JAVA_HOME" ] ; then
141 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
142 | # IBM's JDK on AIX uses strange locations for the executables
143 | JAVACMD="$JAVA_HOME/jre/sh/java"
144 | else
145 | JAVACMD="$JAVA_HOME/bin/java"
146 | fi
147 | else
148 | JAVACMD="`which java`"
149 | fi
150 | fi
151 |
152 | if [ ! -x "$JAVACMD" ] ; then
153 | echo "Error: JAVA_HOME is not defined correctly." >&2
154 | echo " We cannot execute $JAVACMD" >&2
155 | exit 1
156 | fi
157 |
158 | if [ -z "$JAVA_HOME" ] ; then
159 | echo "Warning: JAVA_HOME environment variable is not set."
160 | fi
161 |
162 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
163 |
164 | # traverses directory structure from process work directory to filesystem root
165 | # first directory with .mvn subdirectory is considered project base directory
166 | find_maven_basedir() {
167 |
168 | if [ -z "$1" ]
169 | then
170 | echo "Path not specified to find_maven_basedir"
171 | return 1
172 | fi
173 |
174 | basedir="$1"
175 | wdir="$1"
176 | while [ "$wdir" != '/' ] ; do
177 | if [ -d "$wdir"/.mvn ] ; then
178 | basedir=$wdir
179 | break
180 | fi
181 | # workaround for JBEAP-8937 (on Solaris 10/Sparc)
182 | if [ -d "${wdir}" ]; then
183 | wdir=`cd "$wdir/.."; pwd`
184 | fi
185 | # end of workaround
186 | done
187 | echo "${basedir}"
188 | }
189 |
190 | # concatenates all lines of a file
191 | concat_lines() {
192 | if [ -f "$1" ]; then
193 | echo "$(tr -s '\n' ' ' < "$1")"
194 | fi
195 | }
196 |
197 | BASE_DIR=`find_maven_basedir "$(pwd)"`
198 | if [ -z "$BASE_DIR" ]; then
199 | exit 1;
200 | fi
201 |
202 | ##########################################################################################
203 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
204 | # This allows using the maven wrapper in projects that prohibit checking in binary data.
205 | ##########################################################################################
206 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
207 | if [ "$MVNW_VERBOSE" = true ]; then
208 | echo "Found .mvn/wrapper/maven-wrapper.jar"
209 | fi
210 | else
211 | if [ "$MVNW_VERBOSE" = true ]; then
212 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
213 | fi
214 | if [ -n "$MVNW_REPOURL" ]; then
215 | jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
216 | else
217 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
218 | fi
219 | while IFS="=" read key value; do
220 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;;
221 | esac
222 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
223 | if [ "$MVNW_VERBOSE" = true ]; then
224 | echo "Downloading from: $jarUrl"
225 | fi
226 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
227 | if $cygwin; then
228 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"`
229 | fi
230 |
231 | if command -v wget > /dev/null; then
232 | if [ "$MVNW_VERBOSE" = true ]; then
233 | echo "Found wget ... using wget"
234 | fi
235 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
236 | wget "$jarUrl" -O "$wrapperJarPath"
237 | else
238 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
239 | fi
240 | elif command -v curl > /dev/null; then
241 | if [ "$MVNW_VERBOSE" = true ]; then
242 | echo "Found curl ... using curl"
243 | fi
244 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
245 | curl -o "$wrapperJarPath" "$jarUrl" -f
246 | else
247 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
248 | fi
249 |
250 | else
251 | if [ "$MVNW_VERBOSE" = true ]; then
252 | echo "Falling back to using Java to download"
253 | fi
254 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
255 | # For Cygwin, switch paths to Windows format before running javac
256 | if $cygwin; then
257 | javaClass=`cygpath --path --windows "$javaClass"`
258 | fi
259 | if [ -e "$javaClass" ]; then
260 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
261 | if [ "$MVNW_VERBOSE" = true ]; then
262 | echo " - Compiling MavenWrapperDownloader.java ..."
263 | fi
264 | # Compiling the Java class
265 | ("$JAVA_HOME/bin/javac" "$javaClass")
266 | fi
267 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
268 | # Running the downloader
269 | if [ "$MVNW_VERBOSE" = true ]; then
270 | echo " - Running MavenWrapperDownloader.java ..."
271 | fi
272 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
273 | fi
274 | fi
275 | fi
276 | fi
277 | ##########################################################################################
278 | # End of extension
279 | ##########################################################################################
280 |
281 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
282 | if [ "$MVNW_VERBOSE" = true ]; then
283 | echo $MAVEN_PROJECTBASEDIR
284 | fi
285 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
286 |
287 | # For Cygwin, switch paths to Windows format before running java
288 | if $cygwin; then
289 | [ -n "$M2_HOME" ] &&
290 | M2_HOME=`cygpath --path --windows "$M2_HOME"`
291 | [ -n "$JAVA_HOME" ] &&
292 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
293 | [ -n "$CLASSPATH" ] &&
294 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
295 | [ -n "$MAVEN_PROJECTBASEDIR" ] &&
296 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"`
297 | fi
298 |
299 | # Provide a "standardized" way to retrieve the CLI args that will
300 | # work with both Windows and non-Windows executions.
301 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
302 | export MAVEN_CMD_LINE_ARGS
303 |
304 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
305 |
306 | exec "$JAVACMD" \
307 | $MAVEN_OPTS \
308 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
309 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
310 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
311 |
--------------------------------------------------------------------------------
/src/main/java/com/kosherjava/zmanim/util/GeoLocationUtils.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Zmanim Java API
3 | * Copyright (C) 2004-2020 Eliyahu Hershfeld
4 | *
5 | * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General
6 | * Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option)
7 | * any later version.
8 | *
9 | * This library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied
10 | * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11 | * details.
12 | * You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to
13 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA,
14 | * or connect to: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
15 | */
16 | package com.kosherjava.zmanim.util;
17 |
18 | /**
19 | * A class for various location calculations
20 | * Most of the code in this class is ported from Chris Veness'
21 | * LGPL Javascript Implementation
22 | *
23 | * @author © Eliyahu Hershfeld 2009 - 2020
24 | * @deprecated All methods in this call are available in the {@link GeoLocation} class, and this class that duplicates that
25 | * code will be removed in release 3.0.
26 | */
27 | public class GeoLocationUtils {
28 | /**
29 | * Constant for a distance type calculation.
30 | * @see #getGeodesicDistance(GeoLocation, GeoLocation)
31 | */
32 | private static int DISTANCE = 0;
33 |
34 | /**
35 | * Constant for a initial bearing type calculation.
36 | * @see #getGeodesicInitialBearing(GeoLocation, GeoLocation)
37 | */
38 | private static int INITIAL_BEARING = 1;
39 |
40 | /**
41 | * Constant for a final bearing type calculation.
42 | * @see #getGeodesicFinalBearing(GeoLocation, GeoLocation)
43 | */
44 | private static int FINAL_BEARING = 2;
45 |
46 | /**
47 | * Calculate the geodesic initial bearing between this Object and
48 | * a second Object passed to this method using Thaddeus
49 | * Vincenty's inverse formula See T Vincenty, "Direct and
50 | * Inverse Solutions of Geodesics on the Ellipsoid with application of nested equations", Survey Review, vol XXII
51 | * no 176, 1975.
52 | *
53 | * @param location
54 | * the initial location
55 | * @param destination
56 | * the destination location
57 | * @return the geodesic bearing
58 | */
59 | public static double getGeodesicInitialBearing(GeoLocation location, GeoLocation destination) {
60 | return vincentyFormula(location, destination, INITIAL_BEARING);
61 | }
62 |
63 | /**
64 | * Calculate the geodesic final bearing between this Object
65 | * and a second Object passed to this method using Thaddeus Vincenty's
66 | * inverse formula See T Vincenty, "Direct and Inverse Solutions of Geodesics
67 | * on the Ellipsoid with application of nested equations", Survey Review, vol XXII no 176, 1975.
68 | *
69 | * @param location
70 | * the initial location
71 | * @param destination
72 | * the destination location
73 | * @return the geodesic bearing
74 | */
75 | public static double getGeodesicFinalBearing(GeoLocation location, GeoLocation destination) {
76 | return vincentyFormula(location, destination, FINAL_BEARING);
77 | }
78 |
79 | /**
80 | * Calculate geodesic distance in Meters
81 | * between this Object and a second Object passed to this method using Thaddeus Vincenty's inverse formula See T Vincenty,
83 | * "Direct and Inverse Solutions of Geodesics on the
84 | * Ellipsoid with application of nested equations", Survey Review, vol XXII no 176, 1975. This uses the
85 | * WGS-84 geodetic model.
86 | *
87 | * @param location
88 | * the initial location
89 | * @param destination
90 | * the destination location
91 | * @return the geodesic distance in Meters
92 | */
93 | public static double getGeodesicDistance(GeoLocation location, GeoLocation destination) {
94 | return vincentyFormula(location, destination, DISTANCE);
95 | }
96 |
97 | /**
98 | * Calculates the initial geodesic bearing, final bearing or
99 | * geodesic distance using Thaddeus Vincenty's inverse formula See T Vincenty, "Direct and Inverse Solutions of Geodesics on the Ellipsoid
102 | * with application of nested equations", Survey Review, vol XXII no 176, 1975.
103 | *
104 | * @param location
105 | * the initial location
106 | * @param destination
107 | * the destination location
108 | * @param formula
109 | * This formula calculates initial bearing ({@link #INITIAL_BEARING}),
110 | * final bearing ({@link #FINAL_BEARING}) and distance ({@link #DISTANCE}).
111 | * @return
112 | * the geodesic distance, initial or final bearing (based on the formula passed in) between the location
113 | * and destination in Meters
114 | * @see #getGeodesicDistance(GeoLocation, GeoLocation)
115 | * @see #getGeodesicInitialBearing(GeoLocation, GeoLocation)
116 | * @see #getGeodesicFinalBearing(GeoLocation, GeoLocation)
117 | */
118 | private static double vincentyFormula(GeoLocation location, GeoLocation destination, int formula) {
119 | double a = 6378137; // length of semi-major axis of the ellipsoid (radius at equator) in metres based on WGS-84
120 | double b = 6356752.3142; // length of semi-minor axis of the ellipsoid (radius at the poles) in meters based on WGS-84
121 | double f = 1 / 298.257223563; // flattening of the ellipsoid based on WGS-84
122 | double L = Math.toRadians(destination.getLongitude() - location.getLongitude()); //difference in longitude of two points;
123 | double U1 = Math.atan((1 - f) * Math.tan(Math.toRadians(location.getLatitude()))); // reduced latitude (latitude on the auxiliary sphere)
124 | double U2 = Math.atan((1 - f) * Math.tan(Math.toRadians(destination.getLatitude()))); // reduced latitude (latitude on the auxiliary sphere)
125 |
126 | double sinU1 = Math.sin(U1), cosU1 = Math.cos(U1);
127 | double sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);
128 |
129 | double lambda = L;
130 | double lambdaP = 2 * Math.PI;
131 | double iterLimit = 20;
132 | double sinLambda = 0;
133 | double cosLambda = 0;
134 | double sinSigma = 0;
135 | double cosSigma = 0;
136 | double sigma = 0;
137 | double sinAlpha = 0;
138 | double cosSqAlpha = 0;
139 | double cos2SigmaM = 0;
140 | double C;
141 | while (Math.abs(lambda - lambdaP) > 1e-12 && --iterLimit > 0) {
142 | sinLambda = Math.sin(lambda);
143 | cosLambda = Math.cos(lambda);
144 | sinSigma = Math.sqrt((cosU2 * sinLambda) * (cosU2 * sinLambda)
145 | + (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda)
146 | * (cosU1 * sinU2 - sinU1 * cosU2 * cosLambda));
147 | if (sinSigma == 0)
148 | return 0; // co-incident points
149 | cosSigma = sinU1 * sinU2 + cosU1 * cosU2 * cosLambda;
150 | sigma = Math.atan2(sinSigma, cosSigma);
151 | sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma;
152 | cosSqAlpha = 1 - sinAlpha * sinAlpha;
153 | cos2SigmaM = cosSigma - 2 * sinU1 * sinU2 / cosSqAlpha;
154 | if (Double.isNaN(cos2SigmaM))
155 | cos2SigmaM = 0; // equatorial line: cosSqAlpha=0 (§6)
156 | C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha));
157 | lambdaP = lambda;
158 | lambda = L
159 | + (1 - C)
160 | * f
161 | * sinAlpha
162 | * (sigma + C
163 | * sinSigma
164 | * (cos2SigmaM + C * cosSigma
165 | * (-1 + 2 * cos2SigmaM * cos2SigmaM)));
166 | }
167 | if (iterLimit == 0)
168 | return Double.NaN; // formula failed to converge
169 |
170 | double uSq = cosSqAlpha * (a * a - b * b) / (b * b);
171 | double A = 1 + uSq / 16384
172 | * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq)));
173 | double B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq)));
174 | double deltaSigma = B
175 | * sinSigma
176 | * (cos2SigmaM + B
177 | / 4
178 | * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - B
179 | / 6 * cos2SigmaM
180 | * (-3 + 4 * sinSigma * sinSigma)
181 | * (-3 + 4 * cos2SigmaM * cos2SigmaM)));
182 | double distance = b * A * (sigma - deltaSigma);
183 |
184 | // initial bearing
185 | double fwdAz = Math.toDegrees(Math.atan2(cosU2 * sinLambda, cosU1
186 | * sinU2 - sinU1 * cosU2 * cosLambda));
187 | // final bearing
188 | double revAz = Math.toDegrees(Math.atan2(cosU1 * sinLambda, -sinU1
189 | * cosU2 + cosU1 * sinU2 * cosLambda));
190 | if (formula == DISTANCE) {
191 | return distance;
192 | } else if (formula == INITIAL_BEARING) {
193 | return fwdAz;
194 | } else if (formula == FINAL_BEARING) {
195 | return revAz;
196 | } else { // should never happen
197 | return Double.NaN;
198 | }
199 | }
200 |
201 | /**
202 | * Returns the rhumb line
203 | * bearing from the current location to the GeoLocation passed in.
204 | *
205 | * @param location
206 | * the initial location
207 | * @param destination
208 | * the destination location
209 | * @return the bearing in degrees
210 | */
211 | public static double getRhumbLineBearing(GeoLocation location, GeoLocation destination) {
212 | double dLon = Math.toRadians(destination.getLongitude() - location.getLongitude());
213 | double dPhi = Math.log(Math.tan(Math.toRadians(destination.getLatitude())
214 | / 2 + Math.PI / 4)
215 | / Math.tan(Math.toRadians(location.getLatitude()) / 2 + Math.PI / 4));
216 | if (Math.abs(dLon) > Math.PI)
217 | dLon = dLon > 0 ? -(2 * Math.PI - dLon) : (2 * Math.PI + dLon);
218 | return Math.toDegrees(Math.atan2(dLon, dPhi));
219 | }
220 |
221 | /**
222 | * Returns the rhumb line distance between two GeoLocations
223 | * passed in. Ported from Chris Veness' Javascript Implementation.
224 | *
225 | * @param location
226 | * the initial location
227 | * @param destination
228 | * the destination location
229 | * @return the distance in Meters
230 | */
231 | public static double getRhumbLineDistance(GeoLocation location, GeoLocation destination) {
232 | double earthRadius = 6378137; // Earth's radius in meters (WGS-84)
233 | double dLat = Math.toRadians(location.getLatitude()) - Math.toRadians(destination.getLatitude());
234 | double dLon = Math.abs(Math.toRadians(location.getLongitude()) - Math.toRadians(destination.getLongitude()));
235 | double dPhi = Math.log(Math.tan(Math.toRadians(location.getLatitude()) / 2 + Math.PI / 4)
236 | / Math.tan(Math.toRadians(destination.getLatitude()) / 2 + Math.PI / 4));
237 | double q = dLat / dPhi;
238 |
239 | if (!(Math.abs(q) <= Double.MAX_VALUE)) {
240 | q = Math.cos(Math.toRadians(destination.getLatitude()));
241 | }
242 | // if dLon over 180° take shorter rhumb across 180° meridian:
243 | if (dLon > Math.PI) {
244 | dLon = 2 * Math.PI - dLon;
245 | }
246 | double d = Math.sqrt(dLat * dLat + q * q * dLon * dLon);
247 | return d * earthRadius;
248 | }
249 | }
250 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 3.0.0 (future)
2 | ### Includes breaking changes
3 | * Remove deprecated methods flagged for removal.
4 | * Remove deprecated classes such as the redundant [GeoLocationUtils](https://github.com/KosherJava/zmanim/blob/master/src/main/java/com/kosherjava/zmanim/util/GeoLocationUtils.java).
5 | * Possibly rename some classes such as the confusingly named [ComplexZmanimCalendar](https://github.com/KosherJava/zmanim/blob/master/src/main/java/com/kosherjava/zmanim/ComplexZmanimCalendar.java).
6 | * `getSofZman*Chametz*` times will retun null if it is not _Erev Pesach_.
7 | * Possibly increase the minimum supported JRE version from version 8 (the code currently almost certainly works on 6 today).
8 | * ...
9 |
10 | ## [2.6.0](https://github.com/KosherJava/zmanim/compare/2.5.0...master) (future)
11 |
12 | * `ZmanimCalendar` [Astronomical Chatzos based changes](https://github.com/KosherJava/zmanim/commit/c523424b327f173d70f024bdf207ccae0413d487):
13 | * Add setting `useAstronomicalChatzos` (defaulted to true) to keep the mistaken compat break introduced in the v2.5.0 release.
14 | * Add setting `useAstronomicalChatzosForOtherZmanim` (defaulted to false).
15 | * Add `getChatzosAsHalfDay()` to retain the old behavior of chatzos being halfway between sunrise and sunset.
16 | * Use `useAstronomicalChatzos` to control if `getChatzos()` returns `getSunTransit()` (astronomical chatzos) or getChatzosAsHalfDay().
17 | * Add `getHalfDayBasedZman(Date startOfHalfDay, Date endOfHalfDay, double hours)` to allow other zmanim to be impacted by chatzos.
18 | * Use `useAstronomicalChatzosForOtherZmanim`.
19 | * `ZmanimCalendar` - add utility method [getPercentOfShaahZmanisFromDegrees(double degrees, boolean sunset)`](https://github.com/KosherJava/zmanim/commit/60d1f09322835835035afa507ac2dc852f1cb033) to simplify zmaniyos time calculations. This allows calculations of various percentage of the day zmanim calculations.
20 | * Use Astronomical Chatzos Halayla (as opposed as halfway between sunset and sunrise or 12 hours after chatzos hayom)
21 | * `AstronomicalCalculator` - [add `getSunLowerTransit()`](https://github.com/KosherJava/zmanim/commit/a76a3b65aeb45912bfdb02ce354f74bb97a9d9b2)
22 | * `AstronomicalCalculator` - [add abstract method `getUTCMidnight()`](https://github.com/KosherJava/zmanim/commit/f1904b12393c48b069d1333a7397fce66804958d)
23 | * `NOAACalculator` - [implement `getUTCMidnight()`](https://github.com/KosherJava/zmanim/commit/b93eea3388bfdcc2dd526bbcb1be37ddb88fee08)
24 | * `AstronomicalCalculator` - [add abstract method `getUTCMidnight()`](https://github.com/KosherJava/zmanim/commit/1223dd0b6ad2b492818aacc5eb478747989e0ace)
25 | * `ComplexZmanimCalendar` - [significant updates](https://github.com/KosherJava/zmanim/commit/46800aa750ac56c2da9bc55fbf976ea1a092221d)
26 | * Deprecate `getTzaisGeonim3Point65Degrees()` and `getTzaisGeonim3Point676Degrees()`, very early tzais geonim time that are earlier than 13.5 minutes in Yerushalayim at the equinox / equilux.
27 | * Started coding some zmanim to use the half-day zmanim config.
28 | * Deprecate `getFixedLocalChatzosBasedZmanim()` in favor of `getHalfDayBasedZman()` in the parent ZmanimCalendar class.
29 | * `getFixedLocalChatzos()` now just calls the new getLocalMeanTime(12.0) in the grandparent AstronomicalCalendar class.
30 | * Remove `getSolarMidnight()` that was added to the AstronomicalCalendar grandparent class.
31 | * Undeprecate `getPlagAlosToSunset()` since it is not a zman that can be too late.
32 | * Add [`getSofZmanAchilasChametzMGA72MinutesZmanis()` and `getSofZmanBiurChametzMGA72MinutesZmanis()`](https://github.com/KosherJava/zmanim/commit/c444fd3d1ae327560158b5f11c918a59c4eff55e)
33 | * [Add null checks in `getMinchaGedolaAhavatShalom()`](https://github.com/KosherJava/zmanim/commit/93f441f1ff87d4669c91b596eed157c9cf448bca)
34 | * [Fix `getAlos60()` to use `getElevationAdjustedSunrise()`](https://github.com/KosherJava/zmanim/commit/f5a5b2c68e1f0e2f9f4fbdd2cc585085f2914b74)
35 | * Update Tefila method to Use [Consistent Spelling](https://github.com/KosherJava/zmanim/commit/bca6ddb85542683f229d905636a06fbfc66fbe03).
36 | * `HebrewdateFormatter`
37 | * add method [`formatParsha(JewishCalendar.Parsha parsha)`](https://github.com/KosherJava/zmanim/commit/ee3347b04bf0f4221bc8aa71af59437cd7533f72) to allow formatting of a parsha retrieved from `JewishCalendar.getUpcomingParshah()`.
38 | * [Fix NullPointer in HebrewDateFormatter week formatting](https://github.com/KosherJava/zmanim/commit/6cef302f4ac815941c1f61765f2749d698f86042)
39 | * `TefilaRules`
40 | * [add `isMizmorLesodaRecited()`](https://github.com/KosherJava/zmanim/commit/2cde42644dc72a49b3e4228244bc79cc276e138e)
41 | * fix [Tachanun is not recited on Erev Rosh Hashana](https://github.com/KosherJava/zmanim/commit/0b6b95cfdebd14f19078875564b87068ed2623c4)
42 | * `JewishCalendar`
43 | * [`isYomTov()` now returns true for 20 Nissan](https://github.com/KosherJava/zmanim/commit/4e5abe6e98d5404f41da519f2f902b3af1e58e30)
44 | * [add missing brace to `isYomTov()` and simplify logic](https://github.com/KosherJava/zmanim/commit/e34fc879313b045f35e70b5947e2c2e20a4364c5)
45 | * `GeoLocation` - [add NaN validation to `setLatitude` and `setLongitude`](https://github.com/KosherJava/zmanim/commit/d064715ebeaead29a01ec673f3885ee9bd9c78b4)
46 | * `NOAACalculator` - [fix Solar Azimuth and Elevation](https://github.com/KosherJava/zmanim/commit/860f1939c25b38dd4d23adb1772b12ccbc71fc76)
47 | * `AstronomicalCalculator` - [add `getSolarAzimuth()` and `getSolarElevation()`](https://github.com/KosherJava/zmanim/commit/feecf7ad2d9ce527cfe0314ae01710d68c6c3c2e)
48 | * `AstronomicalCalendar`
49 | * [Fix null handling in `getSunTransit(Date,Date)`](https://github.com/KosherJava/zmanim/commit/8221e2895cbab62b037c16de1711f9faacd78a7b)
50 | * [Deprecate `getSunriseSolarDipFromOffset` and `getSunsetSolarDipFromOffset`](https://github.com/KosherJava/zmanim/commit/0ce858258bff15c11235b1f1063d2eb0ef22b994)
51 | * [Pass proper parameter to `getDateFromTime` in `getLocalMeanTime`](https://github.com/KosherJava/zmanim/commit/da7e888299c27622e1786af7d517f620060a38e0)
52 | * [Add `getLocalMeanTime()`](https://github.com/KosherJava/zmanim/commit/14bcdc085011ccce327f69d6a001772c0581fcc2).
53 | * [Move `getSolarMidnight()`](https://github.com/KosherJava/zmanim/commit/a4535717353eb77da10b6951e4a627b10258ac9e) to the parent class where it belongs.
54 | * [Correct USNO noon calculation](https://github.com/KosherJava/zmanim/commit/3735c92289a66039b24d7e2b470955b5297f0ca5) in some locations where it was sometimes 12 hours off.
55 |
56 | ## [2.5.0](https://github.com/KosherJava/zmanim/compare/2.4.0...2.5.0) (2023-06-09)
57 |
58 | * Update `ComplexZmanimCalendar.getSolarMidnight()` to support astronomocal midnight that works even in the Arctic/Antarctic.
59 | * Add special Shabbasos/Parshiyos Shuva, Shira, Hagadol, Chazon and Nachamu
60 | * Fix isYomTov() should return false on Erev Shavuos.
61 | * Correct spelling of Bein Hashmashos methods the the `ComplexZmanimCalendar` (was missing the second H).
62 | * Various Daf Yomi Yerushalmi fixes including:
63 | * Correct calculation of the _daf_ number.
64 | * Correct the order of transliterated Yerushalmi _masechtos_.
65 | * Correct the Hebrew spelling of the _masechta_ Kilayim.
66 | * Added number of IS methods such as is `isYomKippur()`, `isSuccos()`, `isPesach()` etc. to the `JewishCalendar` class.
67 | * Add `isAlHanissimRecited(JewishCalendar)` and `isYaalehVeyavoRecited(JewishCalendar)` to the `TefilaRules` class.
68 | * Clarify documentation to explain that isMacharChodesh() Refers to the Haftorah
69 |
70 | ## [2.4.0](https://github.com/KosherJava/zmanim/compare/2.3.0...2.4.0) (2022-11-27)
71 |
72 | * JewishCalendar.getUpcomingParshah() that will return the upcoming _Parsha_ regardless of the day of week.
73 | * Change YerushalmiYomiCalculator to return null on Yom Kippur and Tisha Be'Av when there is no Daf.
74 | * Add some Luach Ahavat Shalom Zmanim
75 | * Add _BeHaB_ to the `JewishCalendar`class
76 | * Add _Yom Kippur Katan_ and _Isru Chag_ to the `JewishCalendar`class.
77 | * Add the `TefilaRules` class, a utility class for info like:
78 | * is _vesain tal umatar_ recited etc.
79 | * is _tachanun_ recited by _shacharis_ or _mincha_.
80 | * Is _hallel_ or _hallel shalem_ recited
81 | * Deprecate the _tefila_ rules methods that existed in JewishCalendar class in favor of using the ones in the `TefilaRules` class.
82 | * Add `getSamuchLeMinchaKetana` _zman_.
83 | * Deprecate `getSofZmanShmaFixedLocal()` and `getSofZmanTfilaFixedLocal()` with future plans of removal.
84 | * Deprecate multiple "dangerous" _zmanim_ as an alert to developers, with plans on retaining them.
85 |
86 | ## [2.3.0](https://github.com/KosherJava/zmanim/compare/98d704...2.3.0) (2021-12-07)
87 |
88 | * Fix an issue with sof _zman kiddush levana_ being off by an hour when the _molad_ is on one side of the DST change, and the _sof zman_ on the other.
89 | * Add seasonal _davening_ based _zmanim_ including _Vesein Tal Umatar/ Vesein Berachah / Mashiv Haruach_.
90 | * Add Rav Moshe Feinstein's _zmanim_ used in MTJ and Yeshiva of Staten Island.
91 | * Refactor code for alos and _tzeis zmaniyos_ based time (ports to other languages can simplify things by doing the same).
92 | * Fix Hebrew spelling of _Parshas Nitzavim_.
93 |
94 | ## [2.2.0](https://github.com/KosherJava/zmanim/compare/2.1.0...98d704) (2021-03-15)
95 |
96 | * Added JewishCalendar.isTaanisBechoros().
97 | * Updated Javadocs - document sources for `getFixedLocalChatzos()` and clarify _Yerushalmi Yomi_ Start Date.
98 |
99 | ## [2.1.0](https://github.com/KosherJava/zmanim/compare/8ffa53b9a...2.1.0) (2020-12-02)
100 |
101 | * Added six variants of the Yereim's _bain hashmashos zmanim_.
102 | * `AstronomicalCalculator.getRefraction()` and `.getSolarRadius()` now have public access.
103 | * Deprecate the `GeoLocationUtils` class. All of its functionality is in the `GeoLocation` class.
104 | * Updated JavaDocs (no more errors or warnings).
105 | * Added Lag Ba'omer.
106 | * Added Shushan Purim Katan.
107 | * Added `Daf.setMasechtaTransliterated(String[] masechtosBavliTransliterated)` and `Daf.setYerushlmiMasechtaTransliterated(String[] masechtosYerushalmiTransliterated)`.
108 | * Simplify and reduce code duplication in `ZmanimCalendar` generic _zmanim_ calculations.
109 | * Fix `AstronomicalCalendar` `getSunriseSolarDipFromOffset()` and `getSunsetSolarDipFromOffset` (they are still inefficient) to properly allow calculations before and after sun rise/set.
110 | * Change some Hebrew lists that are not expected to change to be final.
111 |
112 | ## [2.0.3] (2020-10-01)
113 | * Semver change (just a versioning change).
114 |
115 | ## [2.02] (2020-09-30)
116 | * Fix JavaDoc references to new package structure.
117 |
118 | ## [2.01] (2020-09-29)
119 | * Fix #160 `isShabbosMevorchim` should return false for the month of Tishrei.
120 | * Fix #161 a mistake in `Zman.toString()`.
121 | * Fix java 6 compilation issues.
122 |
123 | ## [2.0] (2020-08-03)
124 |
125 | * Changed package structure to `com.kosherjava.zmanim` from `net.sourceforge.zmanim`.
126 | * Added Maven and Gradle support.
127 | * Use DST for TimeZone display name (#150).
128 | * Convert `formatMolad()` to static.
129 | * Convert `getTimeOffset()` to static.
130 | * Pass alos and tzais parameters for `TchilasZmanKidushLevana3Days`.
131 | * Historical _daf yomi_ dates should be final.
132 | * Add _Birkas Hachama_, update documentation.
133 | * Update formatter class for Enums in `JewishCalendar`.
134 |
135 |
136 | ## Older Changes (since 1.3)
137 |
138 | * Default calculator changed from USNO to NOAA.
139 | * Remove the redundant `ZmanimCalculator` class (backwards breaking if you used this calculator).
140 | * Support optional elevation adjustments for zmanim besides sunrise and sunset.
141 | * Added multiple alternative zmanim .
142 | * Added Baal Hatanya _zmanim_.
143 | * Replaced GPL parsha code with an LGPL kosher version.
144 | * Added JSON serialization / output (was previously limited to XML).
145 | * Add _Daf Yomi Yerishalmi_.
146 | * Many `JewishCalendar` related tweaks and enhancements.
147 | * Many minor bug fixes and enhancements.
148 |
149 | See [GitHub Commits](https://github.com/KosherJava/zmanim/commits/master) for more details.
150 |
151 |
--------------------------------------------------------------------------------
/src/main/java/com/kosherjava/zmanim/util/SunTimesCalculator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Zmanim Java API
3 | * Copyright (C) 2004-2025 Eliyahu Hershfeld
4 | *
5 | * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General
6 | * Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option)
7 | * any later version.
8 | *
9 | * This library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied
10 | * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11 | * details.
12 | * You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to
13 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA,
14 | * or connect to: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
15 | */
16 | package com.kosherjava.zmanim.util;
17 |
18 | import java.util.Calendar;
19 |
20 | /**
21 | * Implementation of sunrise and sunset methods to calculate astronomical times. This calculator uses the Java algorithm
22 | * written by Kevin
23 | * Boone that is based on the US Naval Observatory'sAstronomical Almanac and used with his permission. Added to Kevin's
25 | * code is adjustment of the zenith to account for elevation. This algorithm returns the same time every year and does not
26 | * account for leap years. It is not as accurate as the Jean Meeus based {@link NOAACalculator} that is the default calculator
27 | * use by the KosherJava zmanim library.
28 | *
29 | * @author © Eliyahu Hershfeld 2004 - 2025
30 | * @author © Kevin Boone 2000
31 | */
32 | public class SunTimesCalculator extends AstronomicalCalculator {
33 |
34 | /**
35 | * Default constructor of the SunTimesCalculator.
36 | */
37 | public SunTimesCalculator() {
38 | super();
39 | }
40 |
41 | /**
42 | * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getCalculatorName()
43 | */
44 | public String getCalculatorName() {
45 | return "US Naval Almanac Algorithm";
46 | }
47 |
48 | /**
49 | * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCSunrise(Calendar, GeoLocation, double, boolean)
50 | */
51 | public double getUTCSunrise(Calendar calendar, GeoLocation geoLocation, double zenith, boolean adjustForElevation) {
52 | double elevation = adjustForElevation ? geoLocation.getElevation() : 0;
53 | double adjustedZenith = adjustZenith(zenith, elevation);
54 | return getTimeUTC(calendar, geoLocation, adjustedZenith, true);
55 | }
56 |
57 | /**
58 | * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCSunset(Calendar, GeoLocation, double, boolean)
59 | */
60 | public double getUTCSunset(Calendar calendar, GeoLocation geoLocation, double zenith, boolean adjustForElevation) {
61 | double elevation = adjustForElevation ? geoLocation.getElevation() : 0;
62 | double adjustedZenith = adjustZenith(zenith, elevation);
63 | return getTimeUTC(calendar, geoLocation, adjustedZenith, false);
64 | }
65 |
66 | /**
67 | * The number of degrees of longitude that corresponds to one hour of time difference.
68 | */
69 | private static final double DEG_PER_HOUR = 360.0 / 24.0;
70 |
71 | /**
72 | * The sine in degrees.
73 | * @param deg the degrees
74 | * @return sin of the angle in degrees
75 | */
76 | private static double sinDeg(double deg) {
77 | return Math.sin(deg * 2.0 * Math.PI / 360.0);
78 | }
79 |
80 | /**
81 | * Return the arc cosine in degrees.
82 | * @param x angle
83 | * @return acos of the angle in degrees
84 | */
85 | private static double acosDeg(double x) {
86 | return Math.acos(x) * 360.0 / (2 * Math.PI);
87 | }
88 |
89 | /**
90 | * Return the arc sine in degrees.
91 | * @param x angle
92 | * @return asin of the angle in degrees
93 | */
94 | private static double asinDeg(double x) {
95 | return Math.asin(x) * 360.0 / (2 * Math.PI);
96 | }
97 |
98 | /**
99 | * Return the tangent in degrees.
100 | * @param deg degrees
101 | * @return tan of the angle in degrees
102 | */
103 | private static double tanDeg(double deg) {
104 | return Math.tan(deg * 2.0 * Math.PI / 360.0);
105 | }
106 |
107 | /**
108 | * Calculate cosine of the angle in degrees
109 | *
110 | * @param deg degrees
111 | * @return cosine of the angle in degrees
112 | */
113 | private static double cosDeg(double deg) {
114 | return Math.cos(deg * 2.0 * Math.PI / 360.0);
115 | }
116 |
117 | /**
118 | * Get time difference between location's longitude and the Meridian, in hours.
119 | *
120 | * @param longitude the longitude
121 | * @return time difference between the location's longitude and the Meridian, in hours. West of Meridian has a negative time difference
122 | */
123 | private static double getHoursFromMeridian(double longitude) {
124 | return longitude / DEG_PER_HOUR;
125 | }
126 |
127 | /**
128 | * Calculate the approximate time of sunset or sunrise in days since midnight Jan 1st, assuming 6am and 6pm events. We
129 | * need this figure to derive the Sun's mean anomaly.
130 | *
131 | * @param dayOfYear the day of year
132 | * @param hoursFromMeridian hours from the meridian
133 | * @param isSunrise true for sunrise and false for sunset
134 | *
135 | * @return the approximate time of sunset or sunrise in days since midnight Jan 1st, assuming 6am and 6pm events. We
136 | * need this figure to derive the Sun's mean anomaly.
137 | */
138 | private static double getApproxTimeDays(int dayOfYear, double hoursFromMeridian, boolean isSunrise) {
139 | if (isSunrise) {
140 | return dayOfYear + ((6.0 - hoursFromMeridian) / 24);
141 | } else { // sunset
142 | return dayOfYear + ((18.0 - hoursFromMeridian) / 24);
143 | }
144 | }
145 |
146 | /**
147 | * Calculate the Sun's mean anomaly in degrees, at sunrise or sunset, given the longitude in degrees
148 | *
149 | * @param dayOfYear the day of the year
150 | * @param longitude longitude
151 | * @param isSunrise true for sunrise and false for sunset
152 | * @return the Sun's mean anomaly in degrees
153 | */
154 | private static double getMeanAnomaly(int dayOfYear, double longitude, boolean isSunrise) {
155 | return (0.9856 * getApproxTimeDays(dayOfYear, getHoursFromMeridian(longitude), isSunrise)) - 3.289;
156 | }
157 |
158 | /**
159 | * Returns the Sun's true longitude in degrees.
160 | * @param sunMeanAnomaly the Sun's mean anomaly in degrees
161 | * @return the Sun's true longitude in degrees. The result is an angle >= 0 and <= 360.
162 | */
163 | private static double getSunTrueLongitude(double sunMeanAnomaly) {
164 | double l = sunMeanAnomaly + (1.916 * sinDeg(sunMeanAnomaly)) + (0.020 * sinDeg(2 * sunMeanAnomaly)) + 282.634;
165 |
166 | // get longitude into 0-360 degree range
167 | if (l >= 360.0) {
168 | l = l - 360.0;
169 | }
170 | if (l < 0) {
171 | l = l + 360.0;
172 | }
173 | return l;
174 | }
175 |
176 | /**
177 | * Calculates the Sun's right ascension in hours.
178 | * @param sunTrueLongitude the Sun's true longitude in degrees > 0 and < 360.
179 | * @return the Sun's right ascension in hours in angles > 0 and < 360.
180 | */
181 | private static double getSunRightAscensionHours(double sunTrueLongitude) {
182 | double a = 0.91764 * tanDeg(sunTrueLongitude);
183 | double ra = 360.0 / (2.0 * Math.PI) * Math.atan(a);
184 |
185 | double lQuadrant = Math.floor(sunTrueLongitude / 90.0) * 90.0;
186 | double raQuadrant = Math.floor(ra / 90.0) * 90.0;
187 | ra = ra + (lQuadrant - raQuadrant);
188 |
189 | return ra / DEG_PER_HOUR; // convert to hours
190 | }
191 |
192 | /**
193 | * Calculate the cosine of the Sun's local hour angle
194 | *
195 | * @param sunTrueLongitude the sun's true longitude
196 | * @param latitude the latitude
197 | * @param zenith the zenith
198 | * @return the cosine of the Sun's local hour angle
199 | */
200 | private static double getCosLocalHourAngle(double sunTrueLongitude, double latitude, double zenith) {
201 | double sinDec = 0.39782 * sinDeg(sunTrueLongitude);
202 | double cosDec = cosDeg(asinDeg(sinDec));
203 | return (cosDeg(zenith) - (sinDec * sinDeg(latitude))) / (cosDec * cosDeg(latitude));
204 | }
205 |
206 | /**
207 | * Calculate local mean time of rising or setting. By 'local' is meant the exact time at the location, assuming that
208 | * there were no time zone. That is, the time difference between the location and the Meridian depended entirely on
209 | * the longitude. We can't do anything with this time directly; we must convert it to UTC and then to a local time.
210 | *
211 | * @param localHour the local hour
212 | * @param sunRightAscensionHours the sun's right ascension in hours
213 | * @param approxTimeDays approximate time days
214 | *
215 | * @return the fractional number of hours since midnight as a double
216 | */
217 | private static double getLocalMeanTime(double localHour, double sunRightAscensionHours, double approxTimeDays) {
218 | return localHour + sunRightAscensionHours - (0.06571 * approxTimeDays) - 6.622;
219 | }
220 |
221 | /**
222 | * Get sunrise or sunset time in UTC, according to flag. This time is returned as
223 | * a double and is not adjusted for time-zone.
224 | *
225 | * @param calendar
226 | * the Calendar object to extract the day of year for calculation
227 | * @param geoLocation
228 | * the GeoLocation object that contains the latitude and longitude
229 | * @param zenith
230 | * Sun's zenith, in degrees
231 | * @param isSunrise
232 | * True for sunrise and false for sunset.
233 | * @return the time as a double. If an error was encountered in the calculation
234 | * (expected behavior for some locations such as near the poles,
235 | * {@link Double#NaN} will be returned.
236 | */
237 | private static double getTimeUTC(Calendar calendar, GeoLocation geoLocation, double zenith, boolean isSunrise) {
238 | int dayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
239 | double sunMeanAnomaly = getMeanAnomaly(dayOfYear, geoLocation.getLongitude(), isSunrise);
240 | double sunTrueLong = getSunTrueLongitude(sunMeanAnomaly);
241 | double sunRightAscensionHours = getSunRightAscensionHours(sunTrueLong);
242 | double cosLocalHourAngle = getCosLocalHourAngle(sunTrueLong, geoLocation.getLatitude(), zenith);
243 |
244 | double localHourAngle;
245 | if (isSunrise) {
246 | localHourAngle = 360.0 - acosDeg(cosLocalHourAngle);
247 | } else { // sunset
248 | localHourAngle = acosDeg(cosLocalHourAngle);
249 | }
250 | double localHour = localHourAngle / DEG_PER_HOUR;
251 |
252 | double localMeanTime = getLocalMeanTime(localHour, sunRightAscensionHours,
253 | getApproxTimeDays(dayOfYear, getHoursFromMeridian(geoLocation.getLongitude()), isSunrise));
254 | double pocessedTime = localMeanTime - getHoursFromMeridian(geoLocation.getLongitude());
255 | return pocessedTime > 0 ? pocessedTime % 24 : pocessedTime % 24 + 24; // ensure that the time is >= 0 and < 24
256 | }
257 |
258 | /**
259 | * Return the Universal Coordinated Time (UTC)
260 | * of solar noon for the given day at the given location
261 | * on earth. This implementation returns solar noon as the time halfway between sunrise and sunset.
262 | * {@link NOAACalculator}, the default calculator, returns true solar noon. See The Definition of Chatzos for details on solar
264 | * noon calculations.
265 | * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCNoon(Calendar, GeoLocation)
266 | * @see NOAACalculator
267 | *
268 | * @param calendar
269 | * The Calendar representing the date to calculate solar noon for
270 | * @param geoLocation
271 | * The location information used for astronomical calculating sun times.
272 | * @return the time in minutes from zero UTC. If an error was encountered in the calculation (expected behavior for
273 | * some locations such as near the poles, {@link Double#NaN} will be returned.
274 | */
275 | public double getUTCNoon(Calendar calendar, GeoLocation geoLocation) {
276 | double sunrise = getUTCSunrise(calendar, geoLocation, 90, false);
277 | double sunset = getUTCSunset(calendar, geoLocation, 90, false);
278 | double noon = sunrise + ((sunset - sunrise) / 2);
279 | if (noon < 0) {
280 | noon += 12;
281 | }
282 | if (noon < sunrise) {
283 | noon -= 12;
284 | }
285 | return noon;
286 | }
287 |
288 | /**
289 | * Return the Universal Coordinated Time (UTC)
290 | * of midnight for the given day at the given location on earth. This implementation returns solar midnight as 12 hours
291 | * after utc noon that is halfway between sunrise and sunset.
292 | * {@link NOAACalculator}, the default calculator, returns true solar noon. See The Definition of Chatzos for details on solar
294 | * noon calculations.
295 | * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCNoon(Calendar, GeoLocation)
296 | * @see NOAACalculator
297 | *
298 | * @param calendar
299 | * The Calendar representing the date to calculate solar noon for
300 | * @param geoLocation
301 | * The location information used for astronomical calculating sun times.
302 | * @return the time in minutes from zero UTC. If an error was encountered in the calculation (expected behavior for
303 | * some locations such as near the poles, {@link Double#NaN} will be returned.
304 | */
305 | public double getUTCMidnight(Calendar calendar, GeoLocation geoLocation) {
306 | return (getUTCNoon(calendar, geoLocation) + 12);
307 | }
308 |
309 | /**
310 | * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getSolarAzimuth(Calendar, GeoLocation)
311 | */
312 | public double getSolarAzimuth(Calendar calendar, GeoLocation geoLocation) {
313 | throw new UnsupportedOperationException("The SunTimesCalculator class does not implement the getSolarAzimuth method. Use the NOAACalculator instead.");
314 | }
315 |
316 | /**
317 | * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getSolarElevation(Calendar, GeoLocation)
318 | */
319 | public double getSolarElevation(Calendar calendar, GeoLocation geoLocation) {
320 | throw new UnsupportedOperationException("The SunTimesCalculator class does not implement the getSolarElevation method. Use the NOAACalculator instead.");
321 | }
322 | }
323 |
--------------------------------------------------------------------------------
/src/main/java/com/kosherjava/zmanim/util/Zman.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Zmanim Java API
3 | * Copyright (C) 2004-2025 Eliyahu Hershfeld
4 | *
5 | * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General
6 | * Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option)
7 | * any later version.
8 | *
9 | * This library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied
10 | * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11 | * details.
12 | * You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to
13 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA,
14 | * or connect to: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
15 | */
16 | package com.kosherjava.zmanim.util;
17 |
18 | import java.text.SimpleDateFormat;
19 | import java.util.Comparator;
20 | import java.util.Date;
21 |
22 | /**
23 | * A wrapper class for astronomical times / zmanim that is mostly intended to allow sorting collections of astronomical times.
24 | * It has fields for both date/time and duration based zmanim, name / labels as well as a longer description or explanation of a
25 | * zman.
26 | * 27 | * Here is an example of various ways of sorting zmanim. 28 | *
First create the Calendar for the location you would like to calculate: 29 | * 30 | *
31 | * String locationName = "Lakewood, NJ";
32 | * double latitude = 40.0828; // Lakewood, NJ
33 | * double longitude = -74.2094; // Lakewood, NJ
34 | * double elevation = 20; // optional elevation correction in Meters
35 | * // the String parameter in getTimeZone() has to be a valid timezone listed in {@link java.util.TimeZone#getAvailableIDs()}
36 | * TimeZone timeZone = TimeZone.getTimeZone("America/New_York");
37 | * GeoLocation location = new GeoLocation(locationName, latitude, longitude, elevation, timeZone);
38 | * ComplexZmanimCalendar czc = new ComplexZmanimCalendar(location);
39 | * Zman sunset = new Zman(czc.getSunset(), "Sunset");
40 | * Zman shaah16 = new Zman(czc.getShaahZmanis16Point1Degrees(), "Shaah zmanis 16.1");
41 | * Zman sunrise = new Zman(czc.getSunrise(), "Sunrise");
42 | * Zman shaah = new Zman(czc.getShaahZmanisGra(), "Shaah zmanis GRA");
43 | * ArrayList<Zman> zl = new ArrayList<Zman>();
44 | * zl.add(sunset);
45 | * zl.add(shaah16);
46 | * zl.add(sunrise);
47 | * zl.add(shaah);
48 | * //will sort sunset, shaah 1.6, sunrise, shaah GRA
49 | * System.out.println(zl);
50 | * Collections.sort(zl, Zman.DATE_ORDER);
51 | * // will sort sunrise, sunset, shaah, shaah 1.6 (the last 2 are not in any specific order)
52 | * Collections.sort(zl, Zman.DURATION_ORDER);
53 | * // will sort sunrise, sunset (the first 2 are not in any specific order), shaah GRA, shaah 1.6
54 | * Collections.sort(zl, Zman.NAME_ORDER);
55 | * // will sort shaah 1.6, shaah GRA, sunrise, sunset
56 | *
57 | *
58 | * @author © Eliyahu Hershfeld 2007-2025
59 | * @todo Add secondary sorting. As of now the {@code Comparator}s in this class do not sort by secondary order. This means that when sorting a
60 | * {@link java.util.Collection} of zmanim and using the {@link #DATE_ORDER} {@code Comparator} will have the duration based zmanim
61 | * at the end, but they will not be sorted by duration. This should be N/A for label based sorting.
62 | */
63 | public class Zman {
64 | /**
65 | * The name / label of the zman.
66 | */
67 | private String label;
68 |
69 | /**
70 | * The {@link Date} of the zman
71 | */
72 | private Date zman;
73 |
74 | /**
75 | * The duration if the zman is a {@link com.kosherjava.zmanim.AstronomicalCalendar#getTemporalHour() temporal hour} (or the various
76 | * shaah zmanis base times such as {@link com.kosherjava.zmanim.ZmanimCalendar#getShaahZmanisGra() shaah Zmanis GRA} or
77 | * {@link com.kosherjava.zmanim.ComplexZmanimCalendar#getShaahZmanis16Point1Degrees() shaah Zmanis 16.1°}).
78 | */
79 | private long duration;
80 |
81 | /**
82 | * A longer description or explanation of a zman.
83 | */
84 | private String description;
85 |
86 | /**
87 | * The location information of the zman.
88 | */
89 | private GeoLocation geoLocation;
90 |
91 | /**
92 | * The constructor setting a {@link Date} based zman and a label. In most cases you will likely want to call
93 | * {@link #Zman(Date, GeoLocation, String)} that also sets the location.
94 | * @param date the Date of the zman.
95 | * @param label the label of the zman such as "Sof Zman Krias Shema GRA".
96 | * @see #Zman(Date, GeoLocation, String)
97 | */
98 | public Zman(Date date, String label) {
99 | this(date, null, label);
100 | }
101 |
102 | /**
103 | * The constructor setting a {@link Date} based zman and a label. In most cases you will likely want to call
104 | * {@link #Zman(Date, GeoLocation, String)} that also sets the geo location.
105 | * @param date the Date of the zman.
106 | * @param geoLocation the {@link GeoLocation} of the zman.
107 | * @param label the label of the zman such as "Sof Zman Krias Shema GRA".
108 | */
109 | public Zman(Date date, GeoLocation geoLocation, String label) {
110 | this.zman = date;
111 | this.geoLocation = geoLocation;
112 | this.label = label;
113 | }
114 |
115 | /**
116 | * The constructor setting a duration based zman such as
117 | * {@link com.kosherjava.zmanim.AstronomicalCalendar#getTemporalHour() temporal hour} (or the various shaah zmanis times such as
118 | * {@link com.kosherjava.zmanim.ZmanimCalendar#getShaahZmanisGra() shaah zmanis GRA} or
119 | * {@link com.kosherjava.zmanim.ComplexZmanimCalendar#getShaahZmanis16Point1Degrees() shaah Zmanis 16.1°}) and label.
120 | * @param duration a duration based zman such as ({@link com.kosherjava.zmanim.AstronomicalCalendar#getTemporalHour()}
121 | * @param label the label of the zman such as "Shaah Zmanis GRA".
122 | * @see #Zman(Date, String)
123 | */
124 | public Zman(long duration, String label) {
125 | this.label = label;
126 | this.duration = duration;
127 | }
128 |
129 | /**
130 | * Returns the {@code Date} based zman.
131 | * @return the zman.
132 | * @see #setZman(Date)
133 | */
134 | public Date getZman() {
135 | return this.zman;
136 | }
137 |
138 | /**
139 | * Sets a {@code Date} based zman.
140 | * @param date a {@code Date} based zman
141 | * @see #getZman()
142 | */
143 | public void setZman(Date date) {
144 | this.zman = date;
145 | }
146 |
147 | /**
148 | * Returns the {link TimeZone} of the zman.
149 | * @return the time zone
150 | */
151 | public GeoLocation getGeoLocation() {
152 | return geoLocation;
153 | }
154 |
155 | /**
156 | * Sets the {@code GeoLocation} of the zman.
157 | * @param geoLocation the {@code GeoLocation} of the zman.
158 | */
159 | public void setGeoLocation(GeoLocation geoLocation) {
160 | this.geoLocation = geoLocation;
161 | }
162 |
163 | /**
164 | * Returns a duration based zman such as {@link com.kosherjava.zmanim.AstronomicalCalendar#getTemporalHour() temporal hour}
165 | * (or the various shaah zmanis times such as {@link com.kosherjava.zmanim.ZmanimCalendar#getShaahZmanisGra() shaah zmanis GRA}
166 | * or {@link com.kosherjava.zmanim.ComplexZmanimCalendar#getShaahZmanis16Point1Degrees() shaah zmanis 16.1°}).
167 | * @return the duration based zman.
168 | * @see #setDuration(long)
169 | */
170 | public long getDuration() {
171 | return this.duration;
172 | }
173 |
174 | /**
175 | * Sets a duration based zman such as {@link com.kosherjava.zmanim.AstronomicalCalendar#getTemporalHour() temporal hour}
176 | * (or the various shaah zmanis times as {@link com.kosherjava.zmanim.ZmanimCalendar#getShaahZmanisGra() shaah zmanis GRA} or
177 | * {@link com.kosherjava.zmanim.ComplexZmanimCalendar#getShaahZmanis16Point1Degrees() shaah zmanis 16.1°}).
178 | * @param duration duration based zman such as {@link com.kosherjava.zmanim.AstronomicalCalendar#getTemporalHour()}.
179 | * @see #getDuration()
180 | */
181 | public void setDuration(long duration) {
182 | this.duration = duration;
183 | }
184 |
185 | /**
186 | * Returns the name / label of the zman such as "Sof Zman Krias Shema GRA". There are no automatically set labels
187 | * and you must set them using {@link #setLabel(String)}.
188 | * @return the name/label of the zman.
189 | * @see #setLabel(String)
190 | */
191 | public String getLabel() {
192 | return this.label;
193 | }
194 |
195 | /**
196 | * Sets the name / label of the zman such as "Sof Zman Krias Shema GRA".
197 | * @param label the name / label to set for the zman.
198 | * @see #getLabel()
199 | */
200 | public void setLabel(String label) {
201 | this.label = label;
202 | }
203 |
204 | /**
205 | * Returns the longer description or explanation of a zman. There is no default value for this and it must be set using
206 | * {@link #setDescription(String)}
207 | * @return the description or explanation of a zman.
208 | * @see #setDescription(String)
209 | */
210 | public String getDescription() {
211 | return this.description;
212 | }
213 |
214 | /**
215 | * Sets the longer description or explanation of a zman.
216 | * @param description
217 | * the zman description to set.
218 | * @see #getDescription()
219 | */
220 | public void setDescription(String description) {
221 | this.description = description;
222 | }
223 |
224 | /**
225 | * A {@link Comparator} that will compare and sort zmanim by date/time order. Compares its two arguments by the zman's date/time
226 | * order. Returns a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater
227 | * than the second.
228 | * Please note that this class will handle cases where either the {@code Zman} is a null or {@link #getZman()} returns a null.
229 | */
230 | public static final ComparatorString representing the serialized Object. Very
271 | * similar to the toString method but the return value is in an xml format. The format currently used (subject to
272 | * change) is:
273 | *
274 | * 275 | * <Zman> 276 | * <Label>Sof Zman Krias Shema GRA</Label> 277 | * <Zman>1969-02-08T09:37:56.820</Zman> 278 | * <TimeZone> 279 | * <TimezoneName>America/Montreal</TimezoneName> 280 | * <TimeZoneDisplayName>Eastern Standard Time</TimeZoneDisplayName> 281 | * <TimezoneGMTOffset>-5</TimezoneGMTOffset> 282 | * <TimezoneDSTOffset>1</TimezoneDSTOffset> 283 | * </TimeZone> 284 | * <Duration>0</Duration> 285 | * <Description>Sof Zman Krias Shema GRA is 3 sha'os zmaniyos calculated from sunrise to sunset.</Description> 286 | * </Zman> 287 | *288 | * @return The XML formatted
String.
289 | */
290 | public String toXML() {
291 | SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
292 | StringBuilder sb = new StringBuilder();
293 | sb.append("["ברכות",
175 | * "שבת", "עירובין",
176 | * "פסחים", "שקלים", "יומא",
177 | * "סוכה", "ביצה", "ראש השנה",
178 | * "תענית", "מגילה", "מועד
179 | * קטן", "חגיגה", "יבמות",
180 | * "כתובות", "נדרים","נזיר",
181 | * "סוטה", "גיטין", "קידושין",
182 | * "בבא קמא", "בבא מציעא",
183 | * "בבא בתרא", "סנהדרין",
184 | * "מכות", "שבועות", "עבודה
185 | * זרה", "הוריות", "זבחים",
186 | * "מנחות", "חולין", "בכורות",
187 | * "ערכין", "תמורה", "כריתות",
188 | * "מעילה", "קינים", "תמיד",
189 | * "מידות", "נדה"].
190 | *
191 | * @return the masechta (tractate) of the Daf Yomi in Hebrew. As an example, it will return
192 | * ברכות for Berachos.
193 | */
194 | public String getMasechta() {
195 | return masechtosBavli[masechtaNumber];
196 | }
197 |
198 | /**
199 | * Returns the transliterated name of the masechta (tractate) of the Daf Yomi in Yerushalmi. The list of
200 | * mashechtos is:
201 | * Berachos, Pe'ah, Demai, Kilayim, Shevi'is, Terumos, Ma'asros, Ma'aser Sheni, Chalah, Orlah, Bikurim,
202 | * Shabbos, Eruvin, Pesachim, Beitzah, Rosh Hashanah, Yoma, Sukah, Ta'anis, Shekalim, Megilah, Chagigah,
203 | * Moed Katan, Yevamos, Kesuvos, Sotah, Nedarim, Nazir, Gitin, Kidushin, Bava Kama, Bava Metzia,
204 | * Bava Basra, Shevuos, Makos, Sanhedrin, Avodah Zarah, Horayos, Nidah and No Daf Today.
205 | *
206 | * @return the transliterated name of the masechta (tractate) of the Daf Yomi such as Berachos.
207 | */
208 | public String getYerushalmiMasechtaTransliterated() {
209 | return masechtosYerushalmiTransliterated[masechtaNumber];
210 | }
211 |
212 | /**
213 | * @see #getYerushalmiMasechtaTransliterated()
214 | * @deprecated misspelled method name to be removed in 3.0.0.
215 | * @return the transliterated name of the masechta (tractate) of the Daf Yomi such as Berachos.
216 | */
217 | @Deprecated // (forRemoval=true) // add back once Java 9 is the minimum supported version
218 | public String getYerushlmiMasechtaTransliterated() {
219 | return getYerushalmiMasechtaTransliterated();
220 | }
221 |
222 | /**
223 | * Setter method to allow overriding of the default list of Yerushalmi masechtos transliterated into Latin chars.
224 | * The default uses Ashkenazi American English transliteration.
225 | *
226 | * @param masechtosYerushalmiTransliterated the list of transliterated Yerushalmi masechtos to set.
227 | */
228 | public void setYerushalmiMasechtaTransliterated(String[] masechtosYerushalmiTransliterated) {
229 | Daf.masechtosYerushalmiTransliterated = masechtosYerushalmiTransliterated;
230 | }
231 |
232 | /**
233 | * @see #setYerushalmiMasechtaTransliterated(String[])
234 | * @deprecated misspelled method name to be removed in 3.0.0.
235 | * @param masechtosYerushalmiTransliterated the list of transliterated Yerushalmi masechtos to set.
236 | */
237 | @Deprecated // (forRemoval=true) // add back once Java 9 is the minimum supported version
238 | public void setYerushlmiMasechtaTransliterated(String[] masechtosYerushalmiTransliterated) {
239 | setYerushalmiMasechtaTransliterated(masechtosYerushalmiTransliterated);
240 | }
241 |
242 | /**
243 | * Getter method to allow retrieving the list of Yerushalmi masechtos transliterated into Latin chars.
244 | * The default uses Ashkenazi American English transliteration.
245 | *
246 | * @return the array of transliterated masechta (tractate) names of the Daf Yomi Yerushalmi.
247 | */
248 | public static String[] getYerushalmiMasechtosTransliterated() {
249 | return masechtosYerushalmiTransliterated;
250 | }
251 |
252 | /**
253 | * @see #getYerushalmiMasechtosTransliterated()
254 | * @deprecated misspelled method name to be removed in 3.0.0.
255 | * @return the array of transliterated masechta (tractate) names of the Daf Yomi Yerushalmi.
256 | */
257 | @Deprecated // (forRemoval=true) // add back once Java 9 is the minimum supported version
258 | public static String[] getYerushlmiMasechtosTransliterated() {
259 | return getYerushalmiMasechtosTransliterated();
260 | }
261 |
262 | /**
263 | * Getter method to allow retrieving the list of Yerushalmi masechtos.
264 | *
265 | * @return the array of Hebrew masechta (tractate) names of the Daf Yomi Yerushalmi.
266 | */
267 | public static String[] getYerushalmiMasechtos() {
268 | return masechtosYerushalmi;
269 | }
270 |
271 | /**
272 | * @see #getYerushalmiMasechtos()
273 | * @deprecated misspelled method name to be removed in 3.0.0.
274 | * @return the array of Hebrew masechta (tractate) names of the Daf Yomi Yerushalmi.
275 | */
276 | @Deprecated // (forRemoval=true) // add back once Java 9 is the minimum supported version
277 | public static String[] getYerushlmiMasechtos() {
278 | return getYerushalmiMasechtos();
279 | }
280 |
281 | /**
282 | * Returns the Yerushalmi masechta (tractate) of the Daf Yomi in Hebrew. As an example, it will return
283 | * ברכות for Berachos.
284 | *
285 | * @return the Yerushalmi masechta (tractate) of the Daf Yomi in Hebrew. As an example, it will return
286 | * ברכות for Berachos.
287 | */
288 | public String getYerushalmiMasechta() {
289 | return masechtosYerushalmi[masechtaNumber];
290 | }
291 | }
292 |
--------------------------------------------------------------------------------
/src/main/java/com/kosherjava/zmanim/util/AstronomicalCalculator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Zmanim Java API
3 | * Copyright (C) 2004-2025 Eliyahu Hershfeld
4 | *
5 | * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General
6 | * Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option)
7 | * any later version.
8 | *
9 | * This library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied
10 | * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11 | * details.
12 | * You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to
13 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA,
14 | * or connect to: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
15 | */
16 | package com.kosherjava.zmanim.util;
17 |
18 | import java.util.Calendar;
19 |
20 | /**
21 | * An abstract class that all sun time calculating classes extend. This allows the algorithm used to be changed at
22 | * runtime, easily allowing comparison the results of using different algorithms.
23 | * @todo Consider methods that would allow atmospheric modeling. This can currently be adjusted by {@link
24 | * #setRefraction(double) setting the refraction}.
25 | *
26 | * @author © Eliyahu Hershfeld 2004 - 2025
27 | */
28 | public abstract class AstronomicalCalculator implements Cloneable {
29 | /**
30 | * The commonly used average solar refraction. Calendrical Calculations lists a more accurate global average of 34.478885263888294
32 | *
33 | * @see #getRefraction()
34 | */
35 | private double refraction = 34 / 60d;
36 |
37 | /**
38 | * The commonly used average solar radius in minutes of a degree.
39 | *
40 | * @see #getSolarRadius()
41 | */
42 | private double solarRadius = 16 / 60d;
43 |
44 | /**
45 | * The commonly used average earth radius in KM. At this time, this only affects elevation adjustment and not the
46 | * sunrise and sunset calculations. The value currently defaults to 6356.9 KM.
47 | *
48 | * @see #getEarthRadius()
49 | * @see #setEarthRadius(double)
50 | */
51 | private double earthRadius = 6356.9; // in KM
52 |
53 | /**
54 | * Default constructor using the default {@link #refraction refraction}, {@link #solarRadius solar radius} and
55 | * {@link #earthRadius earth radius}.
56 | */
57 | public AstronomicalCalculator() {
58 | // keep the defaults for now.
59 | }
60 |
61 | /**
62 | * A method that returns the earth radius in KM. The value currently defaults to 6356.9 KM if not set.
63 | *
64 | * @return the earthRadius the earth radius in KM.
65 | */
66 | public double getEarthRadius() {
67 | return earthRadius;
68 | }
69 |
70 | /**
71 | * A method that allows setting the earth's radius.
72 | *
73 | * @param earthRadius
74 | * the earthRadius to set in KM
75 | */
76 | public void setEarthRadius(double earthRadius) {
77 | this.earthRadius = earthRadius;
78 | }
79 |
80 | /**
81 | * The zenith of astronomical sunrise and sunset. The sun is 90° from the vertical 0°
82 | */
83 | private static final double GEOMETRIC_ZENITH = 90;
84 |
85 | /**
86 | * Returns the default class for calculating sunrise and sunset. This is currently the more accurate
87 | * {@link NOAACalculator}, but this may change in the future.
88 | *
89 | * @return AstronomicalCalculator the default class for calculating sunrise and sunset. In the current
90 | * implementation the default calculator returned is the more accurate {@link NOAACalculator}.
91 | */
92 | public static AstronomicalCalculator getDefault() {
93 | return new NOAACalculator();
94 | }
95 |
96 | /**
97 | * Returns the name of the algorithm.
98 | *
99 | * @return the descriptive name of the algorithm.
100 | */
101 | public abstract String getCalculatorName();
102 |
103 | /**
104 | * A method that calculates UTC sunrise as well as any time based on an angle above or below sunrise. This abstract
105 | * method is implemented by the classes that extend this class.
106 | *
107 | * @param calendar
108 | * Used to calculate day of year.
109 | * @param geoLocation
110 | * The location information used for astronomical calculating sun times.
111 | * @param zenith
112 | * the azimuth below the vertical zenith of 90 degrees. for sunrise typically the {@link #adjustZenith
113 | * zenith} used for the calculation uses geometric zenith of 90° and {@link #adjustZenith adjusts}
114 | * this slightly to account for solar refraction and the sun's radius. Another example would be
115 | * {@link com.kosherjava.zmanim.AstronomicalCalendar#getBeginNauticalTwilight()} that passes
116 | * {@link com.kosherjava.zmanim.AstronomicalCalendar#NAUTICAL_ZENITH} to this method.
117 | * @param adjustForElevation
118 | * Should the time be adjusted for elevation
119 | * @return The UTC time of sunrise in 24-hour format. 5:45:00 AM will return 5.75.0. If an error was encountered in
120 | * the calculation (expected behavior for some locations such as near the poles,
121 | * {@link java.lang.Double#NaN} will be returned.
122 | * @see #getElevationAdjustment(double)
123 | */
124 | public abstract double getUTCSunrise(Calendar calendar, GeoLocation geoLocation, double zenith,
125 | boolean adjustForElevation);
126 |
127 | /**
128 | * A method that calculates UTC sunset as well as any time based on an angle above or below sunset. This abstract
129 | * method is implemented by the classes that extend this class.
130 | *
131 | * @param calendar
132 | * Used to calculate day of year.
133 | * @param geoLocation
134 | * The location information used for astronomical calculating sun times.
135 | * @param zenith
136 | * the azimuth below the vertical zenith of 90°. For sunset typically the {@link #adjustZenith
137 | * zenith} used for the calculation uses geometric zenith of 90° and {@link #adjustZenith adjusts}
138 | * this slightly to account for solar refraction and the sun's radius. Another example would be
139 | * {@link com.kosherjava.zmanim.AstronomicalCalendar#getEndNauticalTwilight()} that passes
140 | * {@link com.kosherjava.zmanim.AstronomicalCalendar#NAUTICAL_ZENITH} to this method.
141 | * @param adjustForElevation
142 | * Should the time be adjusted for elevation
143 | * @return The UTC time of sunset in 24-hour format. 5:45:00 AM will return 5.75.0. If an error was encountered in
144 | * the calculation (expected behavior for some locations such as near the poles,
145 | * {@link java.lang.Double#NaN} will be returned.
146 | * @see #getElevationAdjustment(double)
147 | */
148 | public abstract double getUTCSunset(Calendar calendar, GeoLocation geoLocation, double zenith,
149 | boolean adjustForElevation);
150 |
151 |
152 | /**
153 | * Return solar noon (UTC) for the given day at the
154 | * given location on earth. The {@link com.kosherjava.zmanim.util.NOAACalculator} implementation calculates
155 | * true solar noon, while the {@link com.kosherjava.zmanim.util.SunTimesCalculator} approximates it, calculating
156 | * the time as halfway between sunrise and sunset.
157 | *
158 | * @param calendar
159 | * Used to calculate day of year.
160 | * @param geoLocation
161 | * The location information used for astronomical calculating sun times.
162 | *
163 | * @return the time in minutes from zero UTC
164 | */
165 | public abstract double getUTCNoon(Calendar calendar, GeoLocation geoLocation);
166 |
167 |
168 | /**
169 | * Return solar midnight (UTC) for the given day at the
170 | * given location on earth. The the {@link com.kosherjava.zmanim.util.NOAACalculator} implementation calculates
171 | * true solar midnight, while the {@link com.kosherjava.zmanim.util.SunTimesCalculator} approximates it, calculating
172 | * the time as 12 hours after halfway between sunrise and sunset.
173 | *
174 | * @param calendar
175 | * Used to calculate day of year.
176 | * @param geoLocation
177 | * The location information used for astronomical calculating sun times.
178 | *
179 | * @return the time in minutes from zero UTC
180 | */
181 | public abstract double getUTCMidnight(Calendar calendar, GeoLocation geoLocation);
182 |
183 | /**
184 | * Return the Solar Elevation for the
185 | * horizontal coordinate system at the given location at the given time. Can be negative if the sun is below the
186 | * horizon. Not corrected for altitude.
187 | *
188 | * @param calendar
189 | * time of calculation
190 | * @param geoLocation
191 | * The location information
192 | * @return solar elevation in degrees. The horizon (calculated in a vacuum using the solar radius as the point)
193 | * is 090°, civil twilight is -690° etc. This means that sunrise and sunset that do use
194 | * refraction and are calculated from the upper limb of the sun will return about 0.83390°.
195 | */
196 | public abstract double getSolarElevation(Calendar calendar, GeoLocation geoLocation);
197 |
198 | /**
199 | * Return the Solar Azimuth for the
200 | * horizontal coordinate system at the given location at the given time. Not corrected for altitude. True south is 180
201 | * degrees.
202 | *
203 | * @param calendar
204 | * time of calculation
205 | * @param geoLocation
206 | * The location information
207 | * @return the solar azimuth in degrees. Astronomical midday would be 180 in the norther hemosphere and 0 in the
208 | * southern hemosphere. Depending on the location and time of year, sunrise will have an azimuth of about
209 | * 90° and sunset about 270°.
210 | */
211 | public abstract double getSolarAzimuth(Calendar calendar, GeoLocation geoLocation);
212 |
213 | /**
214 | * Method to return the adjustment to the zenith required to account for the elevation. Since a person at a higher
215 | * elevation can see farther below the horizon, the calculation for sunrise / sunset is calculated below the horizon
216 | * used at sea level. This is only used for sunrise and sunset and not times before or after it such as
217 | * {@link com.kosherjava.zmanim.AstronomicalCalendar#getBeginNauticalTwilight() nautical twilight} since those
218 | * calculations are based on the level of available light at the given dip below the horizon, something that is not
219 | * affected by elevation, the adjustment should only be made if the zenith == 90° {@link #adjustZenith adjusted}
220 | * for refraction and solar radius. The algorithm used is
221 | *
222 | * 223 | * elevationAdjustment = Math.toDegrees(Math.acos(earthRadiusInMeters / (earthRadiusInMeters + elevationMeters))); 224 | *225 | * 226 | * The source of this algorithm is Calendrical 227 | * Calculations by Edward M. Reingold and Nachum Dershowitz. An alternate algorithm that produces similar (but 228 | * not completely accurate) result found in Ma'aglay Tzedek by Moishe Kosower and other sources is: 229 | * 230 | *
231 | * elevationAdjustment = 0.0347 * Math.sqrt(elevationMeters); 232 | *233 | * 234 | * @param elevation 235 | * elevation in Meters. 236 | * @return the adjusted zenith 237 | */ 238 | double getElevationAdjustment(double elevation) { 239 | double elevationAdjustment = Math.toDegrees(Math.acos(earthRadius / (earthRadius + (elevation / 1000)))); 240 | return elevationAdjustment; 241 | } 242 | 243 | /** 244 | * Adjusts the zenith of astronomical sunrise and sunset to account for solar refraction, solar radius and 245 | * elevation. The value for Sun's zenith and true rise/set Zenith (used in this class and subclasses) is the angle 246 | * that the center of the Sun makes to a line perpendicular to the Earth's surface. If the Sun were a point and the 247 | * Earth were without an atmosphere, true sunset and sunrise would correspond to a 90° zenith. Because the Sun 248 | * is not a point, and because the atmosphere refracts light, this 90° zenith does not, in fact, correspond to 249 | * true sunset or sunrise, instead the center of the Sun's disk must lie just below the horizon for the upper edge 250 | * to be obscured. This means that a zenith of just above 90° must be used. The Sun subtends an angle of 16 251 | * minutes of arc (this can be changed via the {@link #setSolarRadius(double)} method , and atmospheric refraction 252 | * accounts for 34 minutes or so (this can be changed via the {@link #setRefraction(double)} method), giving a total 253 | * of 50 arcminutes. The total value for ZENITH is 90+(5/6) or 90.8333333° for true sunrise/sunset. Since a 254 | * person at an elevation can see below the horizon of a person at sea level, this will also adjust the zenith to 255 | * account for elevation if available. Note that this will only adjust the value if the zenith is exactly 90 degrees. 256 | * For values below and above this no correction is done. As an example, astronomical twilight is when the sun is 257 | * 18° below the horizon or {@link com.kosherjava.zmanim.AstronomicalCalendar#ASTRONOMICAL_ZENITH 108° 258 | * below the zenith}. This is traditionally calculated with none of the above mentioned adjustments. The same goes 259 | * for various tzais and alos times such as the 260 | * {@link com.kosherjava.zmanim.ZmanimCalendar#ZENITH_16_POINT_1 16.1°} dip used in 261 | * {@link com.kosherjava.zmanim.ComplexZmanimCalendar#getAlos16Point1Degrees()}. 262 | * 263 | * @param zenith 264 | * the azimuth below the vertical zenith of 90°. For sunset typically the {@link #adjustZenith 265 | * zenith} used for the calculation uses geometric zenith of 90° and {@link #adjustZenith adjusts} 266 | * this slightly to account for solar refraction and the sun's radius. Another example would be 267 | * {@link com.kosherjava.zmanim.AstronomicalCalendar#getEndNauticalTwilight()} that passes 268 | * {@link com.kosherjava.zmanim.AstronomicalCalendar#NAUTICAL_ZENITH} to this method. 269 | * @param elevation 270 | * elevation in Meters. 271 | * @return The zenith adjusted to include the {@link #getSolarRadius sun's radius}, {@link #getRefraction 272 | * refraction} and {@link #getElevationAdjustment elevation} adjustment. This will only be adjusted for 273 | * sunrise and sunset (if the zenith == 90°) 274 | * @see #getElevationAdjustment(double) 275 | */ 276 | double adjustZenith(double zenith, double elevation) { 277 | double adjustedZenith = zenith; 278 | if (zenith == GEOMETRIC_ZENITH) { // only adjust if it is exactly sunrise or sunset 279 | adjustedZenith = zenith + (getSolarRadius() + getRefraction() + getElevationAdjustment(elevation)); 280 | } 281 | return adjustedZenith; 282 | } 283 | 284 | /** 285 | * Method to get the refraction value to be used when calculating sunrise and sunset. The default value is 34 286 | * arcminutes. The Errata and Notes 287 | * for Calendrical Calculations: The Millennium Edition by Edward M. Reingold and Nachum Dershowitz lists the 288 | * actual average refraction value as 34.478885263888294 or approximately 34' 29". The refraction value as well 289 | * as the solarRadius and elevation adjustment are added to the zenith used to calculate sunrise and sunset. 290 | * 291 | * @return The refraction in arcminutes. 292 | */ 293 | public double getRefraction() { 294 | return this.refraction; 295 | } 296 | 297 | /** 298 | * A method to allow overriding the default refraction of the calculator. 299 | * @todo At some point in the future, an AtmosphericModel or Refraction object that models the atmosphere of different 300 | * locations might be used for increased accuracy. 301 | * 302 | * @param refraction 303 | * The refraction in arcminutes. 304 | * @see #getRefraction() 305 | */ 306 | public void setRefraction(double refraction) { 307 | this.refraction = refraction; 308 | } 309 | 310 | /** 311 | * Method to get the sun's radius. The default value is 16 arcminutes. The sun's radius as it appears from earth is 312 | * almost universally given as 16 arcminutes but in fact it differs by the time of the year. At the perihelion it has an apparent radius of 16.293, while at the 314 | * aphelion it has an apparent radius of 15.755. There is little 315 | * affect for most location, but at high and low latitudes the difference becomes more apparent. My Calculations for 316 | * the difference at the location of the Royal Observatory, Greenwich 317 | * shows only a 4.494-second difference between the perihelion and aphelion radii, but moving into the arctic circle the 318 | * difference becomes more noticeable. Tests for Tromso, Norway (latitude 69.672312, longitude 19.049787) show that 319 | * on May 17, the rise of the midnight sun, a 2 minute and 23 second difference is observed between the perihelion and 320 | * aphelion radii using the USNO algorithm, but only 1 minute and 6 seconds difference using the NOAA algorithm. 321 | * Areas farther north show an even greater difference. Note that these test are not real valid test cases because 322 | * they show the extreme difference on days that are not the perihelion or aphelion, but are shown for illustrative 323 | * purposes only. 324 | * 325 | * @return The sun's radius in arcminutes. 326 | */ 327 | public double getSolarRadius() { 328 | return this.solarRadius; 329 | } 330 | 331 | /** 332 | * Method to set the sun's radius. 333 | * 334 | * @param solarRadius 335 | * The sun's radius in arcminutes. 336 | * @see #getSolarRadius() 337 | */ 338 | public void setSolarRadius(double solarRadius) { 339 | this.solarRadius = solarRadius; 340 | } 341 | 342 | /** 343 | * @see java.lang.Object#clone() 344 | * @since 1.1 345 | */ 346 | public Object clone() { 347 | AstronomicalCalculator clone = null; 348 | try { 349 | clone = (AstronomicalCalculator) super.clone(); 350 | } catch (CloneNotSupportedException cnse) { 351 | System.out.print("Required by the compiler. Should never be reached since we implement clone()"); 352 | } 353 | return clone; 354 | } 355 | } 356 | -------------------------------------------------------------------------------- /src/main/java/com/kosherjava/zmanim/util/NOAACalculator.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Zmanim Java API 3 | * Copyright (C) 2004-2025 Eliyahu Hershfeld 4 | * 5 | * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General 6 | * Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) 7 | * any later version. 8 | * 9 | * This library is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied 10 | * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 11 | * details. 12 | * You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to 13 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA, 14 | * or connect to: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html 15 | */ 16 | package com.kosherjava.zmanim.util; 17 | 18 | import java.util.Calendar; 19 | 20 | /** 21 | * Implementation of sunrise and sunset methods to calculate astronomical times based on the NOAA algorithm. This calculator uses the Java algorithm based on the implementation by NOAA - National Oceanic and Atmospheric Administration's Surface Radiation Research Branch. NOAA's implementation is based on equations from Astronomical Algorithms by Jean Meeus. Added to the algorithm is an adjustment of the zenith 28 | * to account for elevation. The algorithm can be found in the Wikipedia Sunrise Equation article. 30 | * 31 | * @author © Eliyahu Hershfeld 2011 - 2025 32 | */ 33 | public class NOAACalculator extends AstronomicalCalculator { 34 | 35 | /** 36 | * The Julian day of January 1, 2000, known as 37 | * J2000.0. 38 | */ 39 | private static final double JULIAN_DAY_JAN_1_2000 = 2451545.0; 40 | 41 | /** 42 | * Julian days per century. 43 | */ 44 | private static final double JULIAN_DAYS_PER_CENTURY = 36525.0; 45 | 46 | /** 47 | * An
enum to indicate what type of solar event ({@link #SUNRISE SUNRISE}, {@link #SUNSET SUNSET},
48 | * {@link #NOON NOON} or {@link #MIDNIGHT MIDNIGHT}) is being calculated.
49 | * .
50 | */
51 | protected enum SolarEvent {
52 | /**SUNRISE A solar event related to sunrise*/SUNRISE, /**SUNSET A solar event related to sunset*/SUNSET,
53 | /**NOON A solar event related to noon*/NOON, /**MIDNIGHT A solar event related to midnight*/MIDNIGHT
54 | }
55 |
56 | /**
57 | * Default constructor of the NOAACalculator.
58 | */
59 | public NOAACalculator() {
60 | super();
61 | }
62 |
63 | /**
64 | * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getCalculatorName()
65 | */
66 | public String getCalculatorName() {
67 | return "US National Oceanic and Atmospheric Administration Algorithm"; // Implementation of the Jean Meeus algorithm
68 | }
69 |
70 | /**
71 | * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCSunrise(Calendar, GeoLocation, double, boolean)
72 | */
73 | public double getUTCSunrise(Calendar calendar, GeoLocation geoLocation, double zenith, boolean adjustForElevation) {
74 | double elevation = adjustForElevation ? geoLocation.getElevation() : 0;
75 | double adjustedZenith = adjustZenith(zenith, elevation);
76 | double sunrise = getSunRiseSetUTC(calendar, geoLocation.getLatitude(), -geoLocation.getLongitude(),
77 | adjustedZenith, SolarEvent.SUNRISE);
78 | sunrise = sunrise / 60;
79 | return sunrise > 0 ? sunrise % 24 : sunrise % 24 + 24; // ensure that the time is >= 0 and < 24
80 | }
81 |
82 | /**
83 | * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCSunset(Calendar, GeoLocation, double, boolean)
84 | */
85 | public double getUTCSunset(Calendar calendar, GeoLocation geoLocation, double zenith, boolean adjustForElevation) {
86 | double elevation = adjustForElevation ? geoLocation.getElevation() : 0;
87 | double adjustedZenith = adjustZenith(zenith, elevation);
88 | double sunset = getSunRiseSetUTC(calendar, geoLocation.getLatitude(), -geoLocation.getLongitude(),
89 | adjustedZenith, SolarEvent.SUNSET);
90 | sunset = sunset / 60;
91 | return sunset > 0 ? sunset % 24 : sunset % 24 + 24; // ensure that the time is >= 0 and < 24
92 | }
93 |
94 | /**
95 | * Return the Julian day from a Java Calendar.
96 | *
97 | * @param calendar
98 | * The Java Calendar
99 | * @return the Julian day corresponding to the date Note: Number is returned for the start of the Julian
100 | * day. Fractional days / time should be added later.
101 | */
102 | private static double getJulianDay(Calendar calendar) {
103 | int year = calendar.get(Calendar.YEAR);
104 | int month = calendar.get(Calendar.MONTH) + 1;
105 | int day = calendar.get(Calendar.DAY_OF_MONTH);
106 | if (month <= 2) {
107 | year -= 1;
108 | month += 12;
109 | }
110 | int a = year / 100;
111 | int b = 2 - a + a / 4;
112 | return Math.floor(365.25 * (year + 4716)) + Math.floor(30.6001 * (month + 1)) + day + b - 1524.5;
113 | }
114 |
115 | /**
116 | * Convert Julian day to centuries since J2000.0.
118 | *
119 | * @param julianDay
120 | * the Julian Day to convert
121 | * @return the centuries since 2000 Julian corresponding to the Julian Day
122 | */
123 | private static double getJulianCenturiesFromJulianDay(double julianDay) {
124 | return (julianDay - JULIAN_DAY_JAN_1_2000) / JULIAN_DAYS_PER_CENTURY;
125 | }
126 |
127 | /**
128 | * Returns the Geometric Mean Longitude of the Sun.
129 | *
130 | * @param julianCenturies
131 | * the number of Julian centuries since J2000.0.
133 | * @return the Geometric Mean Longitude of the Sun in degrees
134 | */
135 | private static double getSunGeometricMeanLongitude(double julianCenturies) {
136 | double longitude = 280.46646 + julianCenturies * (36000.76983 + 0.0003032 * julianCenturies);
137 | return longitude > 0 ? longitude % 360 : longitude % 360 + 360; // ensure that the longitude is >= 0 and < 360
138 | }
139 |
140 | /**
141 | * Returns the Geometric Mean Anomaly of the Sun in degrees.
142 | *
143 | * @param julianCenturies
144 | * the number of Julian centuries since J2000.0.
146 | * @return the Geometric Mean Anomaly of the Sun in degrees
147 | */
148 | private static double getSunGeometricMeanAnomaly(double julianCenturies) {
149 | return 357.52911 + julianCenturies * (35999.05029 - 0.0001537 * julianCenturies);
150 | }
151 |
152 | /**
153 | * Return the unitless eccentricity of earth's orbit.
154 | *
155 | * @param julianCenturies
156 | * the number of Julian centuries since J2000.0.
158 | * @return the unitless eccentricity
159 | */
160 | private static double getEarthOrbitEccentricity(double julianCenturies) {
161 | return 0.016708634 - julianCenturies * (0.000042037 + 0.0000001267 * julianCenturies);
162 | }
163 |
164 | /**
165 | * Returns the equation of center for the sun in degrees.
166 | *
167 | * @param julianCenturies
168 | * the number of Julian centuries since J2000.0.
170 | * @return the equation of center for the sun in degrees
171 | */
172 | private static double getSunEquationOfCenter(double julianCenturies) {
173 | double m = getSunGeometricMeanAnomaly(julianCenturies);
174 | double mrad = Math.toRadians(m);
175 | double sinm = Math.sin(mrad);
176 | double sin2m = Math.sin(mrad + mrad);
177 | double sin3m = Math.sin(mrad + mrad + mrad);
178 | return sinm * (1.914602 - julianCenturies * (0.004817 + 0.000014 * julianCenturies)) + sin2m
179 | * (0.019993 - 0.000101 * julianCenturies) + sin3m * 0.000289;
180 | }
181 |
182 | /**
183 | * Return the true longitude of the sun.
184 | *
185 | * @param julianCenturies
186 | * the number of Julian centuries since J2000.0.
188 | * @return the sun's true longitude in degrees
189 | */
190 | private static double getSunTrueLongitude(double julianCenturies) {
191 | double sunLongitude = getSunGeometricMeanLongitude(julianCenturies);
192 | double center = getSunEquationOfCenter(julianCenturies);
193 | return sunLongitude + center;
194 | }
195 |
196 | /**
197 | * Return the apparent longitude of the sun.
198 | *
199 | * @param julianCenturies
200 | * the number of Julian centuries since J2000.0.
202 | * @return sun's apparent longitude in degrees
203 | */
204 | private static double getSunApparentLongitude(double julianCenturies) {
205 | double sunTrueLongitude = getSunTrueLongitude(julianCenturies);
206 | double omega = 125.04 - 1934.136 * julianCenturies;
207 | double lambda = sunTrueLongitude - 0.00569 - 0.00478 * Math.sin(Math.toRadians(omega));
208 | return lambda;
209 | }
210 |
211 | /**
212 | * Returns the mean obliquity of the ecliptic (Axial tilt).
213 | *
214 | * @param julianCenturies
215 | * the number of Julian centuries since J2000.0.
217 | * @return the mean obliquity in degrees
218 | */
219 | private static double getMeanObliquityOfEcliptic(double julianCenturies) {
220 | double seconds = 21.448 - julianCenturies
221 | * (46.8150 + julianCenturies * (0.00059 - julianCenturies * (0.001813)));
222 | return 23.0 + (26.0 + (seconds / 60.0)) / 60.0;
223 | }
224 |
225 | /**
226 | * Returns the corrected obliquity of the ecliptic (Axial
227 | * tilt).
228 | *
229 | * @param julianCenturies
230 | * the number of Julian centuries since J2000.0.
232 | * @return the corrected obliquity in degrees
233 | */
234 | private static double getObliquityCorrection(double julianCenturies) {
235 | double obliquityOfEcliptic = getMeanObliquityOfEcliptic(julianCenturies);
236 | double omega = 125.04 - 1934.136 * julianCenturies;
237 | return obliquityOfEcliptic + 0.00256 * Math.cos(Math.toRadians(omega));
238 | }
239 |
240 | /**
241 | * Return the declination of the sun.
242 | *
243 | * @param julianCenturies
244 | * the number of Julian centuries since J2000.0.
246 | * @return
247 | * the sun's declination in degrees
248 | */
249 | private static double getSunDeclination(double julianCenturies) {
250 | double obliquityCorrection = getObliquityCorrection(julianCenturies);
251 | double lambda = getSunApparentLongitude(julianCenturies);
252 | double sint = Math.sin(Math.toRadians(obliquityCorrection)) * Math.sin(Math.toRadians(lambda));
253 | double theta = Math.toDegrees(Math.asin(sint));
254 | return theta;
255 | }
256 |
257 | /**
258 | * Return the Equation of Time - the difference between
259 | * true solar time and mean solar time
260 | *
261 | * @param julianCenturies
262 | * the number of Julian centuries since J2000.0.
264 | * @return equation of time in minutes of time
265 | */
266 | private static double getEquationOfTime(double julianCenturies) {
267 | double epsilon = getObliquityCorrection(julianCenturies);
268 | double geomMeanLongSun = getSunGeometricMeanLongitude(julianCenturies);
269 | double eccentricityEarthOrbit = getEarthOrbitEccentricity(julianCenturies);
270 | double geomMeanAnomalySun = getSunGeometricMeanAnomaly(julianCenturies);
271 | double y = Math.tan(Math.toRadians(epsilon) / 2.0);
272 | y *= y;
273 | double sin2l0 = Math.sin(2.0 * Math.toRadians(geomMeanLongSun));
274 | double sinm = Math.sin(Math.toRadians(geomMeanAnomalySun));
275 | double cos2l0 = Math.cos(2.0 * Math.toRadians(geomMeanLongSun));
276 | double sin4l0 = Math.sin(4.0 * Math.toRadians(geomMeanLongSun));
277 | double sin2m = Math.sin(2.0 * Math.toRadians(geomMeanAnomalySun));
278 | double equationOfTime = y * sin2l0 - 2.0 * eccentricityEarthOrbit * sinm + 4.0 * eccentricityEarthOrbit * y
279 | * sinm * cos2l0 - 0.5 * y * y * sin4l0 - 1.25 * eccentricityEarthOrbit * eccentricityEarthOrbit * sin2m;
280 | return Math.toDegrees(equationOfTime) * 4.0;
281 | }
282 |
283 | /**
284 | * Return the hour angle of the sun in
285 | * radians at for the latitude.
286 | *
287 | * @param latitude
288 | * the latitude of observer in degrees
289 | * @param solarDeclination
290 | * the declination angle of sun in degrees
291 | * @param zenith
292 | * the zenith
293 | * @param solarEvent
294 | * If the hour angle is for {@link SolarEvent#SUNRISE SUNRISE} or {@link SolarEvent#SUNSET SUNSET}
295 | * @return hour angle of sunrise in radians
296 | */
297 | private static double getSunHourAngle(double latitude, double solarDeclination, double zenith, SolarEvent solarEvent) {
298 | double latRad = Math.toRadians(latitude);
299 | double sdRad = Math.toRadians(solarDeclination);
300 | double hourAngle = (Math.acos(Math.cos(Math.toRadians(zenith)) / (Math.cos(latRad) * Math.cos(sdRad))
301 | - Math.tan(latRad) * Math.tan(sdRad)));
302 |
303 | if (solarEvent == SolarEvent.SUNSET) {
304 | hourAngle = -hourAngle;
305 | }
306 | return hourAngle;
307 | }
308 |
309 | /**
310 | * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getSolarElevation(Calendar, GeoLocation)
311 | */
312 | public double getSolarElevation(Calendar calendar, GeoLocation geoLocation) {
313 | return getSolarElevationAzimuth(calendar, geoLocation, false);
314 |
315 | }
316 |
317 | /**
318 | * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getSolarAzimuth(Calendar, GeoLocation)
319 | */
320 | public double getSolarAzimuth(Calendar calendar, GeoLocation geoLocation) {
321 | return getSolarElevationAzimuth(calendar, geoLocation, true);
322 | }
323 |
324 | /**
325 | * Return the Solar Elevation or
326 | * Solar Azimuth at the given location
327 | * and time. Can be negative if the sun is below the horizon. Elevation is based on sea-level and is not
328 | * adjusted for altitude.
329 | *
330 | * @param calendar
331 | * time of calculation
332 | * @param geoLocation
333 | * The location for calculating the elevation or azimuth.
334 | * @param isAzimuth
335 | * true for azimuth, false for elevation
336 | * @return solar elevation or azimuth in degrees.
337 | *
338 | * @see #getSolarElevation(Calendar, GeoLocation)
339 | * @see #getSolarAzimuth(Calendar, GeoLocation)
340 | */
341 | private double getSolarElevationAzimuth(Calendar calendar, GeoLocation geoLocation, boolean isAzimuth) {
342 | double latitude = geoLocation.getLatitude();
343 | double longitude = geoLocation.getLongitude();
344 |
345 | Calendar cloned = (Calendar) calendar.clone();
346 | int offset = - adjustHourForTimeZone(cloned);
347 | cloned.add(Calendar.MILLISECOND, offset);
348 | int minute = cloned.get(Calendar.MINUTE);
349 | int second = cloned.get(Calendar.SECOND);
350 | int hour = cloned.get(Calendar.HOUR_OF_DAY);
351 | int milli = cloned.get(Calendar.MILLISECOND);
352 |
353 | double time = (hour + (minute + (second + (milli / 1000.0)) / 60.0) / 60.0 ) / 24.0;
354 | double julianDay = getJulianDay(cloned) + time;
355 | double julianCenturies = getJulianCenturiesFromJulianDay(julianDay);
356 | double eot = getEquationOfTime(julianCenturies);
357 | double theta = getSunDeclination(julianCenturies);
358 |
359 | double adjustment = time + eot / 1440;
360 | double trueSolarTime = ((adjustment + longitude / 360) + 2) % 1; // adding 2 to ensure that it never ends up negative
361 | double hourAngelRad = trueSolarTime * Math.PI * 2 - Math.PI;
362 | double cosZenith = Math.sin(Math.toRadians(latitude)) * Math.sin(Math.toRadians(theta))
363 | + Math.cos(Math.toRadians(latitude)) * Math.cos(Math.toRadians(theta)) * Math.cos(hourAngelRad);
364 | double zenith = Math.toDegrees(Math.acos(cosZenith > 1 ? 1 : cosZenith < -1 ? -1 : cosZenith));
365 | double azDenom = Math.cos(Math.toRadians(latitude)) * Math.sin(Math.toRadians(zenith));
366 | double refractionAdjustment = 0;
367 | double elevation = 90.0 - (zenith - refractionAdjustment);
368 | double azimuth = 0;
369 | double azRad = (Math.sin(Math.toRadians(latitude)) * Math.cos(Math.toRadians(zenith))
370 | - Math.sin(Math.toRadians(theta))) / azDenom;
371 | if(Math.abs(azDenom) > 0.001) {
372 | azimuth = 180 - Math.toDegrees(Math.acos(azRad > 1 ? 1 : azRad < -1? -1 : azRad)) * (hourAngelRad > 0 ? -1 : 1) ;
373 | } else {
374 | azimuth = latitude > 0 ? 180 : 0;
375 | }
376 | return isAzimuth ? azimuth % 360 : elevation;
377 | }
378 |
379 | /**
380 | * Returns the hour of day adjusted for the timezone and DST. This is needed for the azimuth and elevation
381 | * calculations.
382 | * @param calendar the Calendar to extract the hour from. This must have the timezone set to the proper timezone.
383 | * @return the adjusted hour corrected for timezone and DST offset.
384 | */
385 | private int adjustHourForTimeZone(Calendar calendar) {
386 | int offset = calendar.getTimeZone().getRawOffset();
387 | int dstOffset = calendar.getTimeZone().getDSTSavings();
388 | if(calendar.getTimeZone().inDaylightTime(calendar.getTime())) {
389 | offset = offset + dstOffset;
390 | }
391 | return offset;
392 | }
393 |
394 | /**
395 | * Return the Universal Coordinated Time (UTC)
396 | * of solar noon for the given day at the given location
397 | * on earth. This implementation returns true solar noon as opposed to the time halfway between sunrise and sunset.
398 | * Other calculators may return a more simplified calculation of halfway between sunrise and sunset. See The Definition of Chatzos for details on
400 | * solar noon calculations.
401 | * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCNoon(Calendar, GeoLocation)
402 | * @see #getSolarNoonMidnightUTC(double, double, SolarEvent)
403 | *
404 | * @param calendar
405 | * The Calendar representing the date to calculate solar noon for
406 | * @param geoLocation
407 | * The location information used for astronomical calculating sun times. This class uses only requires
408 | * the longitude for calculating noon since it is the same time anywhere along the longitude line.
409 | * @return the time in minutes from zero UTC
410 | */
411 | public double getUTCNoon(Calendar calendar, GeoLocation geoLocation) {
412 | double noon = getSolarNoonMidnightUTC(getJulianDay(calendar), -geoLocation.getLongitude(), SolarEvent.NOON);
413 | noon = noon / 60;
414 | return noon > 0 ? noon % 24 : noon % 24 + 24; // ensure that the time is >= 0 and < 24
415 | }
416 |
417 | /**
418 | * Return the Universal Coordinated Time
419 | * (UTC) of the solar midnight for the end of the given civil
420 | * day at the given location on earth (about 12 hours after solar noon). This implementation returns true solar
421 | * midnight as opposed to the time halfway between sunrise and sunset. Other calculators may return a more
422 | * simplified calculation of halfway between sunrise and sunset. See The Definition of Chatzos for details on
424 | * solar noon / midnight calculations.
425 | * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCNoon(Calendar, GeoLocation)
426 | * @see #getSolarNoonMidnightUTC(double, double, SolarEvent)
427 | *
428 | * @param calendar
429 | * The Calendar representing the date to calculate solar noon for
430 | * @param geoLocation
431 | * The location information used for astronomical calculating sun times. This class uses only requires
432 | * the longitude for calculating noon since it is the same time anywhere along the longitude line.
433 | * @return the time in minutes from zero UTC
434 | */
435 | public double getUTCMidnight(Calendar calendar, GeoLocation geoLocation) {
436 | double midnight = getSolarNoonMidnightUTC(getJulianDay(calendar), -geoLocation.getLongitude(), SolarEvent.MIDNIGHT);
437 | midnight = midnight / 60;
438 | return midnight > 0 ? midnight % 24 : midnight % 24 + 24; // ensure that the time is >= 0 and < 24
439 | }
440 |
441 | /**
442 | * Return the Universal Coordinated Time (UTC)
443 | * of the current day solar noon or the the upcoming
444 | * midnight (about 12 hours after solar noon) of the given day at the given location on earth.
445 | *
446 | * @param julianDay
447 | * The Julian day since J2000.0.
449 | * @param longitude
450 | * The longitude of observer in degrees
451 | * @param solarEvent
452 | * If the calculation is for {@link SolarEvent#NOON NOON} or {@link SolarEvent#MIDNIGHT MIDNIGHT}
453 | *
454 | * @return the time in minutes from zero UTC
455 | *
456 | * @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCNoon(Calendar, GeoLocation)
457 | * @see #getUTCNoon(Calendar, GeoLocation)
458 | */
459 | private static double getSolarNoonMidnightUTC(double julianDay, double longitude, SolarEvent solarEvent) {
460 | julianDay = (solarEvent == SolarEvent.NOON) ? julianDay : julianDay + 0.5;
461 | // First pass for approximate solar noon to calculate equation of time
462 | double tnoon = getJulianCenturiesFromJulianDay(julianDay + longitude / 360.0);
463 | double equationOfTime = getEquationOfTime(tnoon);
464 | double solNoonUTC = (longitude * 4) - equationOfTime; // minutes
465 |
466 | // second pass
467 | double newt = getJulianCenturiesFromJulianDay(julianDay + solNoonUTC / 1440.0);
468 | equationOfTime = getEquationOfTime(newt);
469 | return (solarEvent == SolarEvent.NOON ? 720 : 1440) + (longitude * 4) - equationOfTime;
470 | }
471 |
472 | /**
473 | * Return the Universal Coordinated Time (UTC)
474 | * of sunrise or sunset in minutes for the given day at the given location on earth.
475 | * @todo Possibly increase the number of passes for improved accuracy, especially in the Arctic areas.
476 | *
477 | * @param calendar
478 | * The calendar
479 | * @param latitude
480 | * The latitude of observer in degrees
481 | * @param longitude
482 | * Longitude of observer in degrees
483 | * @param zenith
484 | * Zenith
485 | * @param solarEvent
486 | * If the calculation is for {@link SolarEvent#SUNRISE SUNRISE} or {@link SolarEvent#SUNSET SUNSET}
487 | * @return the time in minutes from zero Universal Coordinated Time (UTC)
488 | */
489 | private static double getSunRiseSetUTC(Calendar calendar, double latitude, double longitude, double zenith,
490 | SolarEvent solarEvent) {
491 | double julianDay = getJulianDay(calendar);
492 |
493 | // Find the time of solar noon at the location, and use that declination.
494 | // This is better than start of the Julian day
495 | // TODO really not needed since the Julian day starts from local fixed noon. Changing this would be more
496 | // efficient but would likely cause a very minor discrepancy in the calculated times (likely not reducing
497 | // accuracy, just slightly different, thus potentially breaking test cases). Regardless, it would be within
498 | // milliseconds.
499 | double noonmin = getSolarNoonMidnightUTC(julianDay, longitude, SolarEvent.NOON);
500 |
501 | double tnoon = getJulianCenturiesFromJulianDay(julianDay + noonmin / 1440.0);
502 |
503 | // First calculates sunrise and approximate length of day
504 | double equationOfTime = getEquationOfTime(tnoon);
505 | double solarDeclination = getSunDeclination(tnoon);
506 | double hourAngle = getSunHourAngle(latitude, solarDeclination, zenith, solarEvent);
507 | double delta = longitude - Math.toDegrees(hourAngle);
508 | double timeDiff = 4 * delta;
509 | double timeUTC = 720 + timeDiff - equationOfTime;
510 |
511 | // Second pass includes fractional Julian Day in gamma calc
512 | double newt = getJulianCenturiesFromJulianDay(julianDay + timeUTC / 1440.0);
513 | equationOfTime = getEquationOfTime(newt);
514 |
515 | solarDeclination = getSunDeclination(newt);
516 | hourAngle = getSunHourAngle(latitude, solarDeclination, zenith, solarEvent);
517 | delta = longitude - Math.toDegrees(hourAngle);
518 | timeDiff = 4 * delta;
519 | timeUTC = 720 + timeDiff - equationOfTime;
520 | return timeUTC;
521 | }
522 | }
523 |
--------------------------------------------------------------------------------