-
Notifications
You must be signed in to change notification settings - Fork 66
Description
Description:
The sun's position at a given date/time (reported by SunPosition.at
), and the date/time of that same position (reported by SolarTime.Calculator.sunset
/sunrise
) differ by several minutes.
Expected behavior
These methods should complete a "round trip" when used together.
Testing:
public class SunPositionTest1
{
@Test
public void test_SunPosition()
{
// setup
double latitude = 33.45579;
double longitude = -111.9485; // Phoenix, AZ
int altitude = 360;
TimeZone tz = TimeZone.getTimeZone("UTC");
Calendar date0 = Calendar.getInstance(tz);
date0.setTimeInMillis(1639791526); // Dec 17, 2021
PlainDate plainDate = calendarToPlainDate(date0, tz);
SolarTime solarTime = SolarTime.ofLocation(latitude, longitude, altitude, StdSolarCalculator.TIME4J);
SolarTime.Calculator calculator = solarTime.getCalculator();
// find elevation at given date/time
Moment moment0 = TemporalType.JAVA_UTIL_DATE.translate(date0.getTime());
net.time4j.calendar.astro.SunPosition position0 = net.time4j.calendar.astro.SunPosition.at(moment0, solarTime);
double elevation0 = position0.getElevation();
// find date/time of given elevation
double geodeticAngle = StdSolarCalculator.TIME4J.getGeodeticAngle(latitude, altitude);
double zenith = 90 + geodeticAngle - elevation0;
Moment moment1 = calculator.sunset(plainDate, latitude, longitude, zenith);
Calendar date1 = momentToCalendar(moment1, tz);
double toleranceMs = 1 * 60 * 1000; // 1min
long difference = date1.getTimeInMillis() - date0.getTimeInMillis();
assertTrue("expected time near " + date0.getTimeInMillis() + " (within " + (toleranceMs / (1000d * 60d)) + " min); " + date1.getTimeInMillis() + " differs by " + (difference / (1000d * 60d)) + " min",
Math.abs(difference) < toleranceMs);
}
public static Calendar momentToCalendar(Moment moment, TimeZone timezone)
{
Calendar retValue = null;
if (moment != null)
{
retValue = new GregorianCalendar();
retValue.setTimeZone(timezone);
retValue.setTime(TemporalType.JAVA_UTIL_DATE.from(moment));
}
return retValue;
}
public static PlainDate calendarToPlainDate(Calendar input, TimeZone timezone)
{
Moment moment = TemporalType.JAVA_UTIL_DATE.translate(input.getTime());
ZonalOffset zonalOffset = ZonalOffset.ofTotalSeconds(timezone.getOffset(input.getTimeInMillis()) / 1000);
return moment.toZonalTimestamp(zonalOffset).toDate();
}
}
This test produces:
java.lang.AssertionError: expected time near 1639791526 (within 1.0 min); 1639978000 differs by 3.1079 min
In addition to the above, I wrote some additional tests that explore this another way. For example, test_sunPositionAtCivilRise
results in:
java.lang.AssertionError: elevation should be near -6.0 (within 0.01); -6.597174049855876
Additional context
I noticed this bug several years ago while debugging Suntimes. I really should have filed this report back then, but somehow it slipped by. I also remember thinking "its close enough". 🤷♂️ Recently though a user opened this bug report, forrestguice/SuntimesWidget#844, which describes the symptoms that can be observed from Suntimes.
Version Info
implementation group: 'net.time4j', name: 'time4j-android', version: '4.8-2021a'