【问题标题】:Order dates by upcoming按即将到来的订购日期
【发布时间】:2010-09-15 03:22:53
【问题描述】:

所以我构建了一个包含各种日期的数组。生日、纪念日和节假日。我想订购接下来发生的数组,基本上是从 10 月到 9 月(包装到明年)

所以如果我的数组是

$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]);
} 

我不确定如何修改它以获得我想要的效果。

【问题讨论】:

  • 我喜欢那个日期。明显是笔误。现已更正。

标签: php arrays sorting


【解决方案1】:

所以我想到只要在比我的目标月份少的月份加上 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]);
} 

【讨论】:

  • 如果您尝试在一个月中包含已经过去的一天(例如,如果您在列表中包含 2000 年 10 月 1 日),这将不起作用。
  • 当日期之一是 2008-02-29 时会发生什么?
  • 鉴于今天是 2008-10-05,您的代码如何显示句柄条目 2008-10-04、2008-10-05、2008-10-06?特别要注意的是,2008 年 10 月 4 日的下一次庆祝活动晚于所有其他正在考虑的日期。您尚未定义今天的日期是今年还是明年。
  • 而且,麻烦了——我之前评论中的“表演”是多余的。
  • 有可能显示 10-04。在这一点上,我并不关心它。我可能不得不在当前一天的数字之上的任何一天添加 30。此时只是字符串排序,所以像 02-29 和 10-45 这样的日子无关紧要,它只是按顺序排序。
【解决方案2】:

没有理由重新发明轮子。如果您不关心密钥,您可以使用它。

$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');

我做了一个快速的速度检查,回调函数慢了一个数量级。

【讨论】:

    【解决方案3】:

    不要比较字符串,而是使用自 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,但我认为要点是正确的。

    编辑:封装比较函数进行比较,仅此而已。要对原始问题的列表进行排序,请对包含今天日期的数组进行排序,在数组中找到今天的日期,然后按位置升序将该位置之前的元素移动到末尾。

    【讨论】:

    • 嗯,是的,您必须将 ticks1 和 ticks2 取模 31536000 才能消除年份,但恐怕它可以正常工作。
    【解决方案4】:

    我很想确定事件的原始年份,然后添加足够的整年,以确保该值大于您的参考日期(通常是今天的日期)。或者,可能大于或等于参考日期。然后您可以按简单的日期顺序进行排序。

    编辑添加

    我的 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 包含在数据中)。

    【讨论】:

    • 我怀疑我的也不是很有效。但我也不觉得我的有 02-29 错误。
    【解决方案5】:

    使用 strtotime() 将所有日期转换为时间戳,然后再将它们添加到数组中,然后您可以将数组按升序(也按时间顺序)排序。现在您所要做的就是处理过去的日期,这很容易通过将它们与当前时间戳进行比较来完成

    for ($i=0; $i<count($a); $i++){
      if ($currentTimestamp > $a[$i]){
        unset($a[$i]);
      }
    }
    

    【讨论】:

      【解决方案6】:
      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");
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-09-07
        • 2021-11-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-13
        相关资源
        最近更新 更多