即将订购的日期
因此,我建立了各种日期的数组。生日,纪念日和节假日。我想订购下一个发生的数组,基本上是从十月到九月(包装到明年)
所以如果我的数组是
$a = ([0]=>"1980-04-14", [1]=>"2007-06-08", [2]=>"2008-12-25", [3]=>"1978-11-03")
我想对它进行排序,以便安排
$a = ([0]=>"1978-11-03", [1]=>"2008-12-25", [2]=>"1980-04-14", [3]=>"2007-06-08")
因为11月的"事件"是下一个将要发生的事件(基于现在是10月)。
我正在尝试在我的cmp函数所在的地方使用usort
function cmp($a, $b) { $a_tmp = split("-", $a); $b_tmp = split("-", $b); return strcmp($a_tmp[1], $b_tmp[1]); }
我不确定如何修改它以获得我想要的效果。
解决方案
不要比较字符串,而是使用自1970年以来的秒数(整数):
$date1 = split("-", $a); $date2 = split("-", $b); $seconds1 = mktime(0,0,0,$date1[1],$date1[2],$date1[0]); $seconds2 = mktime(0,0,0,$date2[1],$date2[2],$date2[0]); // eliminate years $seconds1 %= 31536000; $seconds2 %= 31536000; return $seconds1 - $seconds2;
我也不懂PHP,但是我认为要点是正确的。
编辑:比较功能被封装以执行比较,仅此而已。要对有关原始问题的列表进行排序,请对包含今天的日期的数组进行排序,在数组中找到今天的日期,然后将该位置之前的元素按位置升序移动到末尾。
我很想确定活动的原始年份,然后再添加足够的整年,以确保该值大于参考日期(通常是今天的日期)。或者,可能大于或者等于参考日期。然后,我们可以按简单的日期顺序进行排序。
编辑添加:
我对PHP的流利程度不够,无法给出答案,但这是Perl解决方案。
#!/bin/perl -w # Sort sequence of dates by next occurrence of anniversary. # Today's "birthdays" count as low (will appear first in sequence) use strict; my $refdate = "2008-10-05"; my @list = ( "1980-04-14", "2007-06-08", "2008-12-25", "1978-11-03", "2008-10-04", "2008-10-05", "2008-10-06", "2008-02-29" ); sub date_on_or_after { my($actdate, $refdate) = @_; my($answer) = $actdate; if ($actdate lt $refdate) # String compare OK with ISO8601 format { my($act_yy, $act_mm, $act_dd) = split /-/, $actdate; my($ref_yy, $ref_mm, $ref_dd) = split /-/, $refdate; $ref_yy++ if ($act_mm < $ref_mm || ($act_mm == $ref_mm && $act_dd < $ref_dd)); $answer = "$ref_yy-$act_mm-$act_dd"; } return $answer; } sub anniversary_compare { my $r1 = date_on_or_after($a, $refdate); my $r2 = date_on_or_after($b, $refdate); return $r1 cmp $r2; } my @result = sort anniversary_compare @list; print "Before:\n"; print "* $_\n" foreach (@list); print "Reference date: $refdate\n"; print "After:\n"; print "* $_\n" foreach (@result);
显然,这并不能使其高效,要计算一次date_on_or_after()值,然后对这些值进行排序。 Perl的比较稍微有点奇怪,变量$ a和$ b都是魔术,并且看起来像是无处不在。
运行时,脚本将产生:
Before: * 1980-04-14 * 2007-06-08 * 2008-12-25 * 1978-11-03 * 2008-10-04 * 2008-10-05 * 2008-10-06 * 2008-02-29 Reference date: 2008-10-05 After: * 2008-10-05 * 2008-10-06 * 1978-11-03 * 2008-12-25 * 2008-02-29 * 1980-04-14 * 2007-06-08 * 2008-10-04
请注意,它在很大程度上避免了2月29日发生的问题,因为这样做是"可行的"。基本上,它将生成"日期" 2009-02-29,按顺序正确比较。 2000-02-28周年纪念日将在2008-02-29周年纪念日之前列出(如果数据中包含2000-02-28)。
因此,对我来说,只是在比我的目标月份少的任何月份加上12.
现在正在工作。
所以最后一个功能
function cmp($a, $b) { $a_tmp = explode('-', $a['date']); $b_tmp = explode('-', $b['date']); if ($a_tmp[1] < date('m')) { $a_tmp[1] += 12; } if ($b_tmp[1] < date('m')) { $b_tmp[1] += 12; } return strcmp($a_tmp[1] . $a_tmp[2], $b_tmp[1] . $b_tmp[2]); }
function relative_year_day($date) { $value = date('z', strtotime($date)) - date('z'); if ($value < 0) $value += 365; return $value; } function cmp($a, $b) { $aValue = relative_year_day($a); $bValue = relative_year_day($b); if ($aValue == $bValue) return 0; return ($aValue < $bValue) ? -1 : 1; } $a = array("1980-04-14", "2007-06-08", "2008-12-25", "1978-11-03"); usort($a, "cmp");
使用strtotime()将所有日期转换为时间戳,然后再将它们添加到数组中,然后可以按升序(也按时间顺序)对数组进行排序。现在我们要做的就是处理过去的日期,只需将它们与当前时间戳进行比较即可轻松完成
IE。
for ($i=0; $i<count($a); $i++){ if ($currentTimestamp > $a[$i]){ unset($a[$i]); } }
没有理由重新发明轮子。如果我们不关心密钥,则可以使用它。
$a = array_combine(array_map('strtotime', $a), $a); ksort($a);
或者,如果我们想定义自己的回调。
function dateCmp($date1, $date2) { return (strtotime($date1) > strtotime($date2))?1:-1; } usort($a, 'dateCmp');
如果要保持正确关联的密钥,请致电uasort。
uasort($a, 'dateCmp');
我进行了快速速度检查,并且回调函数的速度降低了一个数量级。