Salagir's Blog

Old name was: "Why do witches burn?"

This is french section. Go to English version

Ecrire du temps de façon confortable pour les humains. Voilà qui est intéressant.
En effet qui veut lire « Il reste 0h 00mn 18s » à la fin d’un décompte qui a commencé il y a une heure ?

Là où strftime() faisait l’affaire, de jolies améliorations sont possibles.
En gros, quand un temps dépasse l’heure, j’ecris le nombre d’heures, mais sinon, je n’écris pas « 0 heures ». Et cela vaut pour chaque unité.

J’ai fini par garder sur moi deux fonctions d’affichages de temps.
J’ai fais deux fonctions pour les rendres plus lisibles. Elles sont fusionnable, mais je n’avais pas envie qu’elles soient bordéliques. De plus, je n’utilise chacune d’entre elle que sur un seul site.

Ecrire le temps en plusieurs langues

Là mes amis : deux solutions. Soit vous utilisez date() ou strftime() et vous faites confiance aux locales installées, ainsi qu’à votre programme PHP pour utiliser la bonne.
Soit vous utilisez cette fonction, qui marchera partout sans se soucier des locales, MAIS limitées en nombre de langues assurément (vous pouvez en ajouter à l’envie).

//! Ecrit un temps, multilangue ($lg = 'fr|en|es|it')
function write_temps_internationnal($secondes, $lg='fr') {
    static $trads = array(
            's' => array('fr'=>'secondes',  'en'=>'seconds', 'it'=>'secunde', 'es'=>'segundos'),
            'ms'=> array('fr'=>'%Mmn, %Ss', 'en'=>'%Mmn, %Ss', 'it'=>'%Mmn, %Ss', 'es'=>'%Mm., %Ss.'),
            'h' => array('fr'=>'%kh, ', 'en'=>'%kh, ', 'it'=>'%kh', 'es'=>'%kh.'),
            'j' => array('fr'=>'%e jours, ', 'en'=>'%e days, ', 'it'=>'%e giorni', 'es'=>'%e dias'),
            );
    if ($secondes<60) {
        return $secondes.' '.$trads['s'][$lg];
    }
    if ($secondes<3600) {
        return gmstrftime($trads['ms'][$lg], $secondes);
    }
    if ($secondes<86400) {
        return gmstrftime($trads['h'][$lg].$trads['ms'][$lg], $secondes);
    }
    // + D'un jour: pb. En effet %e/%d donnent 1 jour de base puisque c'est le *1* janvier 1970
    // Donc on enleve un jour puisqu'on compte du temps ici
    $secondes -= 86400;
    return gmstrftime($trads['j'][$lg].$trads['h'][$lg].$trads['ms'][$lg], $secondes);
}

Il n’y a pas de vérification de la langue parce que je la limite toujours quand je la récupère, en début de mes programmes.

Ecrire le temps en l’arrondissant

Cette version là permet d’être plus vague dans l’information temporelle. S’il reste 5 jours, 3h, 8min et 9 secondes, ça écrit juste « 5 jours ». On reste sur l’essentiel.
C’est pratique pour le jeu 2Fight.net, où je ne veux pas que les joueurs sachent à la seconde près quand tel événement sera fini.

/** Affiche un temps ecrit joliment.
 
  \param $secondes (int) nombre de secondes.
 
  \param $detail peut être un booleen ou les strings suivant: 'h', 'mn',
  qui disent qu'on veut le détail à l'heure ou la minute. false est des fois trop évasif.
 
  \return (string)
  */
function write_temps($secondes, $detail=true) {
    if ($secondes<60) {
        if ($detail==='h') return "moins d'une heure";
        if ($detail==='mn') return "moins d'une minute";
        return write_temps_sub($secondes, 'seconde');
    }
    if ($secondes<3600) {
        if ($detail==='h') return "moins d'une heure";
        if (!$detail or $detail==='mn') return write_temps_sub($secondes/60, 'minute');
        return gmstrftime('%Mmn, %Ss', $secondes);
    }
    if ($secondes<86400) {
        if (!$detail or $detail==='h') return write_temps_sub($secondes/3600, 'heure');
        if ($detail==='mn') return gmstrftime('%kh, %Mmn', $secondes);
        return gmstrftime('%kh, %Mmn, %Ss', $secondes);
    }
    if (!$detail) return round($secondes/86400).' jours';
    // Lorsqu'on a plus d'un jour arrivent des problèmes.
    // En effet %e/%d donnent 1 jour de base puisque c'est le *1* janvier 1970.*
    // Donc enlever un jour puisqu'on compte du temps ici
    $secondes -= 86400;
    if ($detail==='mn') return gmstrftime('%e jours, %kh, %Mmn', $secondes);
    if ($detail==='h') return gmstrftime('%e jours et %kh', $secondes);
    return gmstrftime('%e jours, %kh, %Mmn, %Ss', $secondes);
}
function write_temps_sub($nbre, $quoi) {
    $nbre = round($nbre);
    if ($nbre>1) return $nbre.' '.$quoi.'s';
    return $nbre.' '.$quoi;
}

Maintenant que vous affichez un temps de toute beauté, je vous propose de passer à…

La barre d’avancement

Bon en fait ce n’est pas une barre. Dessiner un truc qui se remplit en faisant attention à la largeur de votre ligne de commande, très peu pour moi. Par contre, vous aurez droit à l’écriture du pourcentage d’avancement du script et le temps probable restant.

Cas simple : l’un de vos scripts doit tester 20 000 fois quelque chose. Vous voudriez savoir où il en est de temps en temps.
Les plus habitués auront fait, à la fin de la boucle, le petit code classique suivant :

echo "Avancement: ".(100*($cpt++)/20000)." % \n";

Et bien moi c’est la même chose, en mieux.

//! fonction géniale qui écrit l'avancement d'un process en temps et en pourcent.
/** Au premier appel on donne le "max" qui est le nombre de fois que cette fonction sera appelée */
function write_avancement($max=null) {
    static $cpt;
    if ($max!==null) {
        $cpt = array(
                'debut' => time(),
                'cpt' => 0,
                'max' => (int) $max,
                );
    }
    if (!$cpt) die("\n[write_avancement] Mauvais parametres.\n");
 
    $cpt['cpt']++;
    $elapsed = time()-$cpt['debut'];
    $restant = round($cpt['max']*$elapsed/$cpt['cpt']) - $elapsed;
    $pourcent = round(100*$cpt['cpt']/$cpt['max']);
    echo "  -+-  $pourcent%  -  Temps pris: ".
        write_temps($elapsed)."  -  Temps restant: ".write_temps($restant)."     \r";
}

Cette fonction est dépendante de write_temps(), donnée juste au-dessus.

Utilisation :

$max = nombre_delements_a_regarder_ou_lignes_du_fichier();
write_avancement($max); // ceci n'ecrit rien
while ( boucle_qui_regarde_chaque_element ) {
    tout_le_process();
    write_avancement();
}
echo "\n"; // si vous ne voulez pas avoir votre prompt dans l'affichage de la "barre"

5 Responses to “Fonction temporelles: Affichage, barre d’avancement.”

  1. J’ai trouvé un bug dans l’avancement !
    En effet si $max est égal à zéro, ca kille tout le script, c’est con.

    remplacez :
    if ($max) {
    par :
    if ($max!==null) {

    Salagir

  2. Après deux mois sans nouvelles de ton blog ,tu nous fais deux articles en une semaine ? ^^ T’es de nouveau sur la bonne voie ….

    Mitrch

  3. Très beaux articles. On attendait sans nouvelle du site mais là je suis comblé ^^
    @Mitrch ; T’es là aussi Toi ?

    T-wow

  4. Oui ca a été un beau vide. Là j’ai updaté WP et préparé des articles d’avance, ca devrait aller :)

    Salagir

  5. […] vous avais parlé de fonctions PHP permettant d’écrire des temps restant, soit dans plusieurs langues, soit en arrondit. Et bien j’ai craqué, j’ai fait la […]

    Why do witches burn? » Blog Archive » Fonctions temporelles 2 : le retour

Leave a Reply