Skip to content

Commit b93eea3

Browse files
authored
NOAACalculator - Implement getUTCMidnight
- Implement getUTCMidnight - add default constructor to avoid warnings on recent JDKs - Update JavaDocs to avoid warnings on recent JDKs and other tweaks
1 parent 1223dd0 commit b93eea3

File tree

1 file changed

+67
-29
lines changed

1 file changed

+67
-29
lines changed

src/main/java/com/kosherjava/zmanim/util/NOAACalculator.java

Lines changed: 67 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* Zmanim Java API
3-
* Copyright (C) 2004-2024 Eliyahu Hershfeld
3+
* Copyright (C) 2004-2025 Eliyahu Hershfeld
44
*
55
* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General
66
* Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option)
@@ -17,7 +17,6 @@
1717

1818
import java.util.Calendar;
1919

20-
2120
/**
2221
* Implementation of sunrise and sunset methods to calculate astronomical times based on the <a
2322
* href="https://noaa.gov">NOAA</a> algorithm. This calculator uses the Java algorithm based on the implementation by <a
@@ -29,9 +28,10 @@
2928
* to account for elevation. The algorithm can be found in the <a
3029
* href="https://en.wikipedia.org/wiki/Sunrise_equation">Wikipedia Sunrise Equation</a> article.
3130
*
32-
* @author &copy; Eliyahu Hershfeld 2011 - 2024
31+
* @author &copy; Eliyahu Hershfeld 2011 - 2025
3332
*/
3433
public class NOAACalculator extends AstronomicalCalculator {
34+
3535
/**
3636
* The <a href="https://en.wikipedia.org/wiki/Julian_day">Julian day</a> of January 1, 2000, known as
3737
* <a href="https://en.wikipedia.org/wiki/Epoch_(astronomy)#J2000">J2000.0</a>.
@@ -44,12 +44,20 @@ public class NOAACalculator extends AstronomicalCalculator {
4444
private static final double JULIAN_DAYS_PER_CENTURY = 36525.0;
4545

4646
/**
47-
* An enum to indicate what type of solar event is being calculated.
47+
* An <code>enum</code> 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+
* .
4850
*/
4951
protected enum SolarEvent {
50-
/**SUNRISE A solar event related to sunrise*/SUNRISE, /**SUNSET A solar event related to sunset*/SUNSET
51-
// possibly add the following in the future, if added, an IllegalArgumentException should be thrown in getSunHourAngle
52-
// /**NOON A solar event related to noon*/NOON, /**MIDNIGHT A solar event related to midnight*/MIDNIGHT
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();
5361
}
5462

5563
/**
@@ -84,7 +92,7 @@ public double getUTCSunset(Calendar calendar, GeoLocation geoLocation, double ze
8492
}
8593

8694
/**
87-
* Return the <a href="https://en.wikipedia.org/wiki/Julian_day">Julian day</a> from a Java Calendar
95+
* Return the <a href="https://en.wikipedia.org/wiki/Julian_day">Julian day</a> from a Java Calendar.
8896
*
8997
* @param calendar
9098
* The Java Calendar
@@ -297,7 +305,7 @@ private static double getEquationOfTime(double julianCenturies) {
297305
* @param zenith
298306
* the zenith
299307
* @param solarEvent
300-
* If the hour angle is for sunrise or sunset
308+
* If the hour angle is for {@link SolarEvent#SUNRISE SUNRISE} or {@link SolarEvent#SUNSET SUNSET}
301309
* @return hour angle of sunrise in <a href="https://en.wikipedia.org/wiki/Radian">radians</a>
302310
*/
303311
private static double getSunHourAngle(double latitude, double solarDeclination, double zenith, SolarEvent solarEvent) {
@@ -380,7 +388,7 @@ public static double getSolarAzimuth(Calendar calendar, double latitude, double
380388
* "https://kosherjava.com/2020/07/02/definition-of-chatzos/">The Definition of <em>Chatzos</em></a> for details on
381389
* solar noon calculations.
382390
* @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCNoon(Calendar, GeoLocation)
383-
* @see #getSolarNoonUTC(double, double)
391+
* @see #getSolarNoonMidnightUTC(double, double, SolarEvent)
384392
*
385393
* @param calendar
386394
* The Calendar representing the date to calculate solar noon for
@@ -390,56 +398,81 @@ public static double getSolarAzimuth(Calendar calendar, double latitude, double
390398
* @return the time in minutes from zero UTC
391399
*/
392400
public double getUTCNoon(Calendar calendar, GeoLocation geoLocation) {
393-
double noon = getSolarNoonUTC(getJulianDay(calendar), -geoLocation.getLongitude());
401+
double noon = getSolarNoonMidnightUTC(getJulianDay(calendar), -geoLocation.getLongitude(), SolarEvent.NOON);
394402
noon = noon / 60;
395403
return noon > 0 ? noon % 24 : noon % 24 + 24; // ensure that the time is >= 0 and < 24
396404
}
405+
406+
/**
407+
* Return the <a href="https://en.wikipedia.org/wiki/Universal_Coordinated_Time">Universal Coordinated Time</a>
408+
* (UTC) of the <a href="https://en.wikipedia.org/wiki/Midnight">solar midnight</a> for the end of the given civil
409+
* day at the given location on earth (about 12 hours after solar noon). This implementation returns true solar
410+
* midnight as opposed to the time halfway between sunrise and sunset. Other calculators may return a more
411+
* simplified calculation of halfway between sunrise and sunset. See <a href=
412+
* "https://kosherjava.com/2020/07/02/definition-of-chatzos/">The Definition of <em>Chatzos</em></a> for details on
413+
* solar noon / midnight calculations.
414+
* @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCNoon(Calendar, GeoLocation)
415+
* @see #getSolarNoonMidnightUTC(double, double, SolarEvent)
416+
*
417+
* @param calendar
418+
* The Calendar representing the date to calculate solar noon for
419+
* @param geoLocation
420+
* The location information used for astronomical calculating sun times. This class uses only requires
421+
* the longitude for calculating noon since it is the same time anywhere along the longitude line.
422+
* @return the time in minutes from zero UTC
423+
*/
424+
public double getUTCMidnight(Calendar calendar, GeoLocation geoLocation) {
425+
double midnight = getSolarNoonMidnightUTC(getJulianDay(calendar), -geoLocation.getLongitude(), SolarEvent.MIDNIGHT);
426+
midnight = midnight / 60;
427+
return midnight > 0 ? midnight % 24 : midnight % 24 + 24; // ensure that the time is >= 0 and < 24
428+
}
397429

398430
/**
399431
* Return the <a href="https://en.wikipedia.org/wiki/Universal_Coordinated_Time">Universal Coordinated Time</a> (UTC)
400-
* of <a href="http://en.wikipedia.org/wiki/Noon#Solar_noon">solar noon</a> for the given day at the given location
401-
* on earth.
402-
* @todo Refactor to possibly use the getSunRiseSetUTC (to be renamed) and remove the need for this method.
432+
* of the current day <a href="http://en.wikipedia.org/wiki/Noon#Solar_noon">solar noon</a> or the the upcoming
433+
* midnight (about 12 hours after solar noon) of the given day at the given location on earth.
403434
*
404435
* @param julianDay
405-
* the Julian day since <a href=
436+
* The Julian day since <a href=
406437
* "https://en.wikipedia.org/wiki/Epoch_(astronomy)#J2000">J2000.0</a>.
407438
* @param longitude
408-
* the longitude of observer in degrees
409-
*
439+
* The longitude of observer in degrees
440+
* @param solarEvent
441+
* If the calculation is for {@link SolarEvent#NOON NOON} or {@link SolarEvent#MIDNIGHT MIDNIGHT}
442+
*
410443
* @return the time in minutes from zero UTC
411444
*
412445
* @see com.kosherjava.zmanim.util.AstronomicalCalculator#getUTCNoon(Calendar, GeoLocation)
413446
* @see #getUTCNoon(Calendar, GeoLocation)
414447
*/
415-
private static double getSolarNoonUTC(double julianDay, double longitude) {
448+
private static double getSolarNoonMidnightUTC(double julianDay, double longitude, SolarEvent solarEvent) {
449+
julianDay = (solarEvent == SolarEvent.NOON) ? julianDay : julianDay + 0.5;
416450
// First pass for approximate solar noon to calculate equation of time
417451
double tnoon = getJulianCenturiesFromJulianDay(julianDay + longitude / 360.0);
418452
double equationOfTime = getEquationOfTime(tnoon);
419-
double solNoonUTC = 720 + (longitude * 4) - equationOfTime; // minutes
453+
double solNoonUTC = (longitude * 4) - equationOfTime; // minutes
420454

421455
// second pass
422-
double newt = getJulianCenturiesFromJulianDay(julianDay - 0.5 + solNoonUTC / 1440.0);
456+
double newt = getJulianCenturiesFromJulianDay(julianDay + solNoonUTC / 1440.0);
423457
equationOfTime = getEquationOfTime(newt);
424-
return 720 + (longitude * 4) - equationOfTime;
458+
return (solarEvent == SolarEvent.NOON ? 720 : 1440) + (longitude * 4) - equationOfTime;
425459
}
426460

427461
/**
428462
* Return the <a href="https://en.wikipedia.org/wiki/Universal_Coordinated_Time">Universal Coordinated Time</a> (UTC)
429463
* of sunrise or sunset in minutes for the given day at the given location on earth.
430-
* @todo support solar noon and possibly increase the number of passes in the Arctic areas.
431-
* This would remove the need for getSolarNoonUTC.
464+
* @todo Possibly increase the number of passes for improved accuracy, especially in the Arctic areas.
432465
*
433466
* @param calendar
434-
* the calendar
467+
* The calendar
435468
* @param latitude
436-
* the latitude of observer in degrees
469+
* The latitude of observer in degrees
437470
* @param longitude
438-
* longitude of observer in degrees
471+
* Longitude of observer in degrees
439472
* @param zenith
440-
* zenith
473+
* Zenith
441474
* @param solarEvent
442-
* Is the calculation for sunrise or sunset
475+
* If the calculation is for {@link SolarEvent#SUNRISE SUNRISE} or {@link SolarEvent#SUNSET SUNSET}
443476
* @return the time in minutes from zero Universal Coordinated Time (UTC)
444477
*/
445478
private static double getSunRiseSetUTC(Calendar calendar, double latitude, double longitude, double zenith,
@@ -448,7 +481,12 @@ private static double getSunRiseSetUTC(Calendar calendar, double latitude, doubl
448481

449482
// Find the time of solar noon at the location, and use that declination.
450483
// This is better than start of the Julian day
451-
double noonmin = getSolarNoonUTC(julianDay, longitude);
484+
// TODO really not needed since the Julian day starts from local fixed noon. Changing this would be more
485+
// efficient but would likely cause a very minor discrepancy in the calculated times (likely not reducing
486+
// accuracy, just slightly different, thus potentially breaking test cases). Regardless, it would be within
487+
// milliseconds.
488+
double noonmin = getSolarNoonMidnightUTC(julianDay, longitude, SolarEvent.NOON);
489+
452490
double tnoon = getJulianCenturiesFromJulianDay(julianDay + noonmin / 1440.0);
453491

454492
// First calculates sunrise and approximate length of day

0 commit comments

Comments
 (0)