算法计算夜间月光小时



我已经在这个问题上咀嚼了一段时间。我认为我正在努力努力,不断绕圈跑来跑去。

问题

我需要在夜间计算月光的时间,即月亮在白天之外的时间。众所周知,在给定的日期中,UTC的日出/日落和月球/月球时间。最简单的(催眠)场景是以下内容:

sunrise:  06:45
sunset:   18:20
moonrise: 02:30
moonset:  19:50

计算月光小时的算法是:

if(moonrise<sunset && sunrise<moonset) {
   moonlighthours = (sunrise-moonrise)-(moonset-sunset);
}

同样容易:

sunrise:  06:45
sunset:   18:20
moonrise: 10:30
moonset:  19:50
if(moonrise>sunset && sunrise<moonset) {
   moonlighthours = (moonset-sunset);
}

但是,当我们与UTC打交道时,它可能会变得非常复杂,具体取决于时区,因为日出/套装和月亮/设定时间可能会在三个不同的日期延伸:

sunrise:  2014-02-05 23:30 
sunset:   2014-02-06 12:20
moonrise: 2014-02-06 11:00
moonset:  2014-03-07 00:50

因此,要计算2014-02-06的月光小时时间,我会尝试像这样的令人费解的东西:

if(sunrise<midnight)    { sunrise  = midnight;    }
if(sunset>midnight+24)  { sunset   = midnight+24; }
if(moonrise<midnight)   { moonrise = midnight;    }
if(moonset>midnight+24) { moonset  = midnight+24  }
if(moonrise<sunset && sunrise<moonset) {
   moonlighthours = (sunrise-moonrise)-(moonset-sunset);
} else if (moonrise>sunset && sunrise<moonset) {
   moonlighthours = (moonset-sunset);
} else if (moonrise<sunset && sunrise>moonset) {
   moonlighthours = (sunrise-moonrise);
} else {
   moonlighthours=0;
} 

试图通过使用if... else结构之间的月光和日光小时之间的相移是一场噩梦。因此,我希望有人对这个问题有重新考虑。任何帮助将不胜感激。

编辑

这是我根据@Ilmari Karonen的建议提出的解决方案。这将在给定日历日期(使用PHP语法)期间陷入月光小时:

$mid00  = midnight;          // unixtimestamp , e.g. 1391558400 (2014-02-05 00:00:00) 
$mid24  = midnight+86399;     // 1399075199 (2014-02-05 23:59:59)
$mr     = moonrise;
$ms     = moonset;
$sr     = sunrise;
$ss     = sunset;
$mr = $mr < $mid00 ? $mid00 : $mr;
$ms = $ms > $mid24 ? $mid24 : $ms;
$sr = $sr < $mid00 ? $mid00 : $sr;
$ss = $ss > $mid24 ? $Mid24 : $ss;
$ml_morn = 0;   // moonlight hours during morning night
$ml_even = 0;   // moonlight hours during evening night
if($ms > $mr) {                                      // moon set later than moon rise 
   $ml_morn = $mr < $sr ? $sr-$mr : 0;               // moon rises before sunrise?
   $ml_even = $ms > $ss ? $ms-$ss : 0;               // moon sets after sunset?
} else {                                             // moon set before moon rise
  $ml_even = $mr > $ss ? $mid24-$mr : $mid24 - $ss;  // moon rises before sunset?
  $ml_morn = $ms < $sr ? $ms-$mid00 : $sr - $mid00;  // moon sets before sunrise? 
}
moonlight_hours = $ml_morn = $ml_even;

当您有复杂的问题时,将其分解为更简单的步骤通常很有用:

步骤1:,如有必要,将太阳/月亮/设置时间转换为可以使用算术的适当的DateTime值。第二以来,自Unix时期以来(或千年开始以来的几分钟),但是如果您的语言具有适当的DateTime类或类型,我建议您使用它。

由于此转换可能是所有时间戳的相同,因此您可以编写一个单个功能,以任意时间值来执行此功能,并为您的每个输入调用它。

步骤2:找到月光期的开始和结束时间。

如果您想要一个夜间的月光小时,则可以计算月光期的开始和结束时期为:

start = max(moonrise, sunset)
end   = min(sunrise, moonset)

如果您确实需要一个日历日的月光小时 ,则可能会有两个单独的月光时期。您可以将它们各自的开始和结束时间计算为:

start1 = max(moonrise1, sunset1, midnight)
end1   = min(sunrise1, moonset1)
start2 = max(moonrise2, sunset2)
end1   = min(sunrise2, moonset2, midnight + 24 hours)

1中的变量名称表示给定的一天之前的夜晚的相应时间,而 2中结尾的变量名称表示定的一天。

步骤3:计算月光时期的长度(s)。

这只是简单的DateTime减法。请注意,如果在给定的夜晚完全不可见月球,则减法可能会产生负值,在这种情况下,您应将结果夹为零。

如果您要对特定日历日期进行计算,则应对上一个和第二天晚上重复此操作,然后将(非负)结果添加在一起。

您可以将其转换为自1970年以来的秒数并减去结果。

tm_sec + tm_min*60 + tm_hour*3600 + tm_yday*86400 +
(tm_year-70)*31536000 + ((tm_year-69)/4)*86400 -
((tm_year-1)/100)*86400 + ((tm_year+299)/400)*86400

如果您只想计算月光的量,例如如果D是日期对象,而D.yearD.monthD.dayD.hourD.minute代表日期的结构(这只是伪代码),那么让我们假设01-01-2010 00:00是时间0,则应计算为分钟计数器,为

计算

total_minutes = ((D.year - 2010) * 60*60*24*365) + (D.month * 60*60*24) + (D.day * 60*60) + (D.hour * 60) + D.minutes;

对您提到的所有4个日期执行此操作,从那里您可以执行您在帖子开头提到的计算。

最新更新