Даты заказа по предстоящим

Итак, я создаю массив различных дат. Дни рождения, юбилеи и праздники. Я хотел бы заказать массив, по которому происходит следующий, по существу сортировать с октября по сентябрь (перенос на следующий год)

так что если мой массив

$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")

потому что ноябрьское "событие" - это то, что произойдет дальше (на нем будет октябрь).

Я пытаюсь использовать, где моя функция cmp

function cmp($a, $b)
{
 $a_tmp = split("-", $a);
 $b_tmp = split("-", $b);
 return strcmp($a_tmp[1], $b_tmp[1]);
}

Я не уверен, как изменить это, чтобы получить желаемый эффект.

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");


У меня возникнет соблазн установить исходный год мероприятия, а затем добавить к нему достаточно целых лет, чтобы убедиться, что значение больше, чем ваша контрольная дата (обычно сегодняшняя дата). Или, возможно, больше или равно контрольной дате. Затем вы можете сортировать в простой порядок дат.

Отредактировано для добавления:

Я недостаточно свободно владею PHP, чтобы дать ответ в этом, но вот решение Perl.

#!/bin/perl -w
# Sort sequence of dates by next occurrence of anniversary.
# Today "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 являются волшебными и выглядят как бы из ниоткуда.

При запуске script создает:

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

Обратите внимание, что это в значительной степени утилит вопрос о том, что происходит с 29 февраля, потому что оно "работает" для этого. В принципе, он сгенерирует "дату" 2009-02-29, которая будет правильно соответствовать последовательно. Юбилей 2000-200-28 годов будет включен в список до юбилея на 2008-02-29 годы (если в данные были включены данные 2000-02-28).


используйте strtotime(), чтобы преобразовать все даты в метку времени, прежде чем добавлять их в массив, тогда вы можете отсортировать массив в порядке возрастания (также хронологического). Теперь все, что вам нужно сделать, это иметь дело с датами в прошлом, что легко сделать, сравнивая их с текущей меткой времени

то есть.

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


Нет причин изобретать велосипед. Если вам не нужны клавиши, вы можете использовать это.

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

Я сделал быструю проверку скорости, и функции обратного вызова были более медленными.


Поэтому мне просто потребовалось добавить 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]);
}


Не сравнивайте строки, вместо этого используйте секунды с 1970 года (ints):

$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, но я думаю, что суть верна.

Изменить: функция сравнения инкапсулирована для выполнения сравнения, не более того. Чтобы упорядочить список в отношении исходного вопроса, выполните сортировку массива с включенной датой даты, найдите сегодняшнюю дату в массиве, а затем переместите элементы до этой позиции до конца в порядке возрастания по положению.

licensed under cc by-sa 3.0 with attribution.