2008-10-03 6 views
3

Donc, je construis un tableau de différentes dates. Anniversaires, anniversaires et jours fériés. Je voudrais commander le tableau par lequel on se passe à côté, trier essentiellement Octobre à Septembre (emballage à l'année prochaine)Dates de commande par à venir

donc si mon tableau est

$a = ([0]=>"1980-04-14", [1]=>"2007-06-08", 
    [2]=>"2008-12-25", [3]=>"1978-11-03") 

Je voudrais trier si il est disposé

$a = ([0]=>"1978-11-03", [1]=>"2008-12-25", 
    [2]=>"1980-04-14", [3]=>"2007-06-08") 

parce que « événement » de novembre est celui qui va se passer ensuite (sur la base étant en ce moment octobre).

J'essaie usort où ma fonction cmp est

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

Je ne suis pas sûr de savoir comment modifier cela pour obtenir mon effet désiré.

+0

Le 78 juin? –

+0

J'aime cette date. Clairement une faute de frappe. maintenant corrigé. –

Répondre

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

Ne pas comparer les chaînes, utilisez plutôt secondes depuis 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; 

Aussi je ne sais pas PHP, mais je pense que l'essentiel est correct.

Éditer: La fonction de comparaison est encapsulée pour effectuer une comparaison, rien de plus. Pour commander une liste concernant la question initiale, trier un tableau avec la date du jour incluse, localiser la date du jour dans le tableau, puis déplacer les éléments avant cette position jusqu'à la fin dans l'ordre croissant par position.

+0

Nulle part près de corriger, j'ai peur. –

+0

Mmm oui tu devrais modulo ticks1 et ticks2 par 31536000 pour éliminer des années mais ça marcherait correctement, j'ai peur. – cfeduke

1

Je serais tenté d'établir l'année d'origine de l'événement, puis d'y ajouter suffisamment d'années entières pour m'assurer que la valeur est supérieure à votre date de référence (normalement la date d'aujourd'hui). Ou, éventuellement, supérieur ou égal à la date de référence. Vous pouvez ensuite trier dans un ordre de date simple.

Edité pour ajouter:

Je ne suis pas assez couramment en PHP pour donner une réponse, mais voici une solution 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); 

De toute évidence, ce n'est pas efficace affreusement - pour le rendre efficace, vous souhaitez calculer la valeur date_on_or_after() une fois, et puis trier sur ces valeurs. La comparaison de Perl est un peu particulière - les variables $ a et $ b sont magiques et apparaissent comme si elles venaient de nulle part.

Lors de l'exécution, le script produit:

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 

Notez que les canards en grande partie la question de ce qui se passe avec le 29 Février, parce qu'il « fonctionne » de le faire. Fondamentalement, il va générer la «date» 2009-02-29, qui se compare correctement dans l'ordre. L'anniversaire du 2000-02-28 serait inscrit avant l'anniversaire du 2008-02-29 (si le 2000-02-28 était inclus dans les données).

+0

Je pense que le mien n'est pas très efficace non plus. Mais je ne me sens pas non plus comme le mien a un bug 02-29. –

0

Alors il m'est apparu juste d'ajouter 12 à n'importe quel mois qui est inférieur à mon mois cible. Qui fonctionne maintenant.

donc la fonction finale

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

Cela ne fonctionnera pas si vous essayez d'inclure un jour du mois déjà passé (par exemple, si vous incluez le 10/1/2000 dans votre liste). – Randy

+0

Bon point. J'ai changé le comparer au mois-jour. –

+0

Que se passe-t-il lorsque l'une des dates est 2008-02-29? –

0

utilisation strtotime() pour convertir les toutes les dates à un horodatage avant de les ajouter au tableau, vous pouvez trier le tableau en ordre croissant (aussi chronologique) commande. Maintenant, tout ce que vous avez à faire est de traiter avec les dates dans le passé qui se fait facilement en les comparant à un horodatage courant

à savoir

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

Il manque le point de la question. –

0

Aucune raison de réinventer la roue. Si vous ne vous souciez pas des touches, vous pouvez l'utiliser.

$a = array_combine(array_map('strtotime', $a), $a); 
ksort($a); 

Ou si vous souhaitez définir votre propre rappel.

function dateCmp($date1, $date2) { 
    return (strtotime($date1) > strtotime($date2))?1:-1; 
} 

usort($a, 'dateCmp'); 

Si vous souhaitez conserver les clés associées, appelez uasort à la place. J'ai effectué un contrôle rapide de la vitesse et les fonctions de rappel étaient plus lentes.

Questions connexes