我正在尝试估算从日出到日落的白天长度,以及从日落到日出的夜晚长度。我目前的近似值是粗略的(它假设昨天和明天具有与今天相同的值),但现在我并不特别关心确定昨天的日落、今天的日出、今天的日落和明天的日出(还)。我的目标是根据每晚 12 小时(彼此相等的 12 小时,不等于标准小时或白天的小时)和每天 12 小时相等的时间进行计算。
我担心的是,在我的 iOS 应用程序中,计算很差;一分钟在 5-6(标准)秒的时间内飞逝。当我在此处的其他代码中使用未修改的时间时,时钟以标准速度移动,但是当我尝试让此代码提供时钟代码时,有些东西超出了范围。
作为近似值,我一直在处理的代码是:
NSDate *now = [[NSDate alloc] init];
NSDate *factory = [[NSDate alloc] init];
NSDate *summerSolstice2013 = [factory initWithTimeIntervalSinceReferenceDate:_referenceSummerSolstice];
double distanceAlong = [now timeIntervalSinceDate:summerSolstice2013];
double angleAlong = M_PI * 2 * distanceAlong / (2 * (_referenceWinterSolstice - _referenceSummerSolstice));
double currentHeight = cos(angleAlong) * _latitudeAngle + _tiltAngle;
...
if (_secondsAreNatural)
{
_secondsAreShadowed = FALSE;
double dayDuration = 12 * 60 * 60 + 12 * 60 * 60 * sin(currentHeight);
double midday = fmod(24 * 60 * 60 * _longitudeAngle / (2 * M_PI) + 12 * 60 * 60, 24 * 60 * 60);
double sunrise = midday - dayDuration / 2;
double sunset = midday + dayDuration / 2;
double seconds = fmod([now timeIntervalSinceReferenceDate], 24 * 60 * 60);
double proportionAlong = 0;
if (seconds < sunrise)
{
_naturalSeconds = (seconds - sunset - 24 * 60 * 60) / (sunrise - sunset - 24 * 60 * 60);
}
else if (seconds > sunset)
{
_naturalSeconds = 12 * 60 * 60 * (seconds - sunset) / (sunrise + 24 * 60 * 60 - sunset) + 18 * 60 * 60;
}
else
{
_naturalSeconds = 12 * 60 * 60 * (seconds - sunrise) / (sunset - sunrise) + 6 * 60 * 60;
}
}
是否有任何问题(考虑到这个近似值可能会在任何程度上改进)你可以在这段代码中查明吗?
谢谢,
- 编辑 -
我上面写的代码在呈现给阅读它的人的松散末端方面要求相当高。我试图再通过一次,用更简单的术语和更纯粹的数学模型重写它。我写了,评论补充说:
NSDate *now = [[NSDate alloc] init];
NSDate *summerSolstice2013 = [[NSDate alloc] initWithTimeIntervalSinceReferenceDate:_referenceSummerSolstice];
double distanceAlong = [now timeIntervalSinceDate:summerSolstice2013];
// How far along are we, in seconds, since the reference date?
double angleAlong = M_PI * 2 * distanceAlong / (2 * (_referenceWinterSolstice - _referenceSummerSolstice));
// What's the angle if 2 π radians corresponds to a whole year?
double currentHeight = cos(angleAlong) * _latitudeAngle + _tiltAngle;
// _latitudeAngle is the angle represented by our latitude; _tiltAngle is the angle of the earth's tilt.
NSInteger day = 24 * 60 * 60;
// 'day' could have been called secondsInADay, but it was mean to reduce the number of multiplicands represented in the code.
// If we are in the endless day or endless night around the poles, leave the user with standard clock hours.
if (currentHeight > M_PI / 2)
{
_secondsAreShadowed = TRUE;
}
else if (currentHeight < - M_PI / 2)
{
_secondsAreShadowed = TRUE;
}
// Otherwise, calculate the time this routine is meant to calculate. (This is the main intended use case.)
else if (_secondsAreNatural)
{
_secondsAreShadowed = FALSE;
// closestDay is intended to be the nearest midnight (or, in another hemisphere, midday), not exactly in hours offset from UTC, but in longitude offset from Greenwich.
double closestDay;
if (fmod(distanceAlong, day) < .5 * day)
{
closestDay = distanceAlong - fmod(distanceAlong, day);
}
else
{
closestDay = day + distanceAlong - fmod(distanceAlong, day);
}
// As we go through the calculations, for the most part we keep up information on the previous and next days, which will to some degree be consulted at the end.
double previousDay = closestDay - day;
double nextDay = closestDay + day;
// For the three days, what proportion of the way along are they from the solstices?
double closestDayAngleAlong = M_PI * 2 * closestDay / (2 * (_referenceWinterSolstice - _referenceSummerSolstice));
double previousDayAngleAlong = M_PI * 2 * previousDay / (2 * (_referenceWinterSolstice - _referenceSummerSolstice));
double nextDayAngleAlong = M_PI * 2 * nextDay / (2 * (_referenceSummerSolstice - _referenceSummerSolstice));
// What angle are we placed by on the year's cycle, between _latitudeAngle + _tiltAngle and -latitudeAngle + _tiltAngle?
double closestDayHeight = cos(closestDayAngleAlong) * _latitudeAngle + _tiltAngle;
double previousDayHeight = cos(previousDayAngleAlong) * _latitudeAngle + _tiltAngle;
double nextDayHeight = cos(nextDayAngleAlong) * _latitudeAngle + _tiltAngle;
// Based on that, what are the daylight durations for the three twenty-four hour days?
double closestDayDuration = day / 2 + (day / 2) * sin(closestDayHeight);
double previousDayDuration = day / 2 + (day / 2) * sin(previousDayHeight);
double nextDayDuration = day / 2 + (day / 2) * sin(nextDayHeight);
// Here we use both morning and evening for the closest day, and the previous day's morning and the next day's evening.
double closestDayMorning = closestDay + (day / 2) - (closestDayDuration / 2);
double closestDayEvening = closestDay + (day / 2) + (closestDayDuration / 2);
double previousDayEvening = previousDay + (day / 2) + (previousDayDuration / 2);
double nextDayMorning = nextDay + (day / 2) + (nextDayDuration / 2);
// We calculate the proportion along the day that we are between evening and morning (or morning and evening), along with the sooner endpoint of that interval.
double proportion;
double referenceTime;
if (distanceAlong < closestDayMorning)
{
proportion = (distanceAlong - previousDayEvening) / (closestDayMorning - previousDayEvening);
referenceTime = previousDay + day * 3 / 4;
}
else if (distanceAlong > closestDayEvening)
{
proportion = (distanceAlong - closestDayEvening) / (nextDayMorning - closestDayEvening);
referenceTime = closestDay + day * 3 / 4;
}
else
{
proportion = (distanceAlong - closestDayMorning) / (closestDayEvening - closestDayMorning);
referenceTime = closestDay + day * 1 / 4;
}
// Lastly, we take both that endpoint and the proportion of it, and we get the number of seconds according to the daylight / nighttime calculation intended.
_naturalSeconds = referenceTime + proportion * day / 2;
我希望使代码更清晰,更容易掌握,我想我做到了,但它显示出与我之前的尝试相似的行为:时钟指针以大约 10 倍的自然时间旋转,而它们应该在0.8 到 1.2 标准小时/分钟/秒。
有什么建议吗?我编辑的代码是否更清楚地说明了预期的内容或错误之处?
谢谢,