Kirjautuminen

Haku

Tehtävät

Keskustelu: Koodit: PHP: Sekunnit selkokieleksi

Sivun loppuun

ajv [09.09.2004 23:37:40]

#

Laitan tän nyt tännekkin, jos joku vaikka tarttis. Itse ainakin olen tarvinnut tätä useamminkin.
PHP:llä sählätessä aikaa useimmiten käsitellään sekunneissa. Funktio siis syö sekunteja ja antaa ulos vuosia, päiviä, tunteja, minuutteja sekä sekunteja.

Käyttötarkoituksia, nyt voisi olla esimerkiksi foorumin "Viimeksi sisällä 1 tunti 2 minuuttia 55 sekuntia sitten" -toteutuksen tai jonkun äänestyksen jäljellä olevan ajan tulostaminen.

Vuosien pituisten aikojen mittaukseen tuo ei hirveän tarkasti sovellu, sillä en jaksanut tehdä/tarvinnut karkausvuosien laskemista tuohon. Lisäksi jostain kumman syystä suurin aika, jonka tuolla pystyy tulostamaan on "68 vuotta 35 päivää 3 tuntia 14 minuuttia 7 sekuntia" (sekuntteina 2147483647).

http://ajv.lautatarha.com/testing/sec2string.php

Vaikka koodi ei välttämättä ihan yksinkertaiselta ensisilmäyksellä näytä, niin sitä se kuitenkin on ja toimii varmemmin kuin junan vessa.

EDIT 12.09.2004:
Muutettu $sec-muuttujan tyyppi alussa int -> float. Nyt pystyy laskemaan vuosiakin enemmän kuin tuon 68. Lisäksi lisätty kommenttina karkausvuosien vähentäminen

<?php

//muuttaa sekunnit vuosiksi, päiviksi, tunneiksi, minuuteiksi ja sekunneiksi
//ottaa argumentikseen ajan sekunteina
//jos annetaan toinen parametri, niin palauttaa tuloksen assosiatiivisessa taulukossa

function sec2string($sec,$array_mode=0){

   if(!is_numeric($sec)) return false; //ei syödä kuin numeroita
   $sec = (float) abs($sec); //ja niistäkin vain positiivisia kokonaislukuja
   // Seuraava vähentää karkausvuodet epätarkasti. Jos syötetyt sekunnit lasketaan vähentämällä
   //kaksi päivämäärää toisistaan (mktime), tämä vääristää tulosta. Poista kommentit, jos tarvitset.
   //if($sec/31536000 >= 4) $sec -= floor(($sec/31536000/4)*86400);
   $arr['year'] = $arr['day'] = $arr['hr'] = $arr['min'] = $arr['sec'] = 0; //asetetaan alkuarvot
   $str = "";

   if($sec >= 31536000){ //ainakin yksi vuosi
      $c = floor($sec/31536000); //jaetaan vuodella sekuntteina => saadaan kokonaiset vuodet
      $arr['year'] = $c; // tulos talteen assosiatiiviseen taulukkoon
      $str = $c . ($c > 1 ? " vuotta " : " vuosi "); //tulos talteen merkkijonoon
      $sec -= (int) $c*31536000;//miinustetaan sekunneista vuodet
   }
   if($sec >= 86400){//ainakin yksi päivä
      $c = floor($sec/86400);
      $arr['day'] = $c; $str .= $c . ($c > 1 ? " päivää " : " päivä ");
      $sec -= (int) $c*86400;
   }
   if($sec >= 3600){//ainakin yksi tunti
      $c = floor($sec/3600);
      $arr['hr'] = $c; $str .= $c . ($c > 1 ? " tuntia " : " tunti ");
      $sec -= (int) $c*3600;
   }
   if($sec >= 60){//ainakin yksi minuutti
      $c = floor($sec/60);
      $arr['min'] = $c; $str .= $c . ($c > 1 ? " minuuttia " : " minuutti ");
      $sec -= (int) $c*60;
   }
   if($sec != 0){// + sekunnit päälle
      $arr['sec'] = $sec; $str .= $sec . ($sec > 1 ? " sekuntia " : " sekunti ");
   }

   return $array_mode == 0 ? trim($str) : $arr; //palautetaan joko taulukko tai merkkijono
}


//HAVAINNOLLISTAVA ESIMERKKI:
$aika_sillon = mktime(23,30,00,9,9,2004); //aikaleima siitä ajasta, jolloin vinkki postattiin putkaan
$aika_nyt = time(); //tämän hetken aikaleima
$erotus = $aika_nyt - $aika_sillon; //ajankohtien ero sekuntteina

echo "<p>Tämän koodivinkin ikä: ".sec2string($erotus)."</p>";

//ja sama taulukkomuodossa
echo "<pre>";
print_r(sec2string($erotus,1));
echo "</pre>";
//Ajan mittaus tulevaisuuteen  käy yhtä helposti:
echo "<p>Uuteenvuoteen ".(date("Y")+1)." on aikaa: ".sec2string(mktime(0,0,0,1,1,date("Y")+1)-time())."</p>";
?>
<?php

/*
   Tässä yksinkertaistettu versio, joka palauttaa "noin-arvon"
   Sekunnit kun menettävät merkityksensä heti kun aikaa on
   kulunut vähänkin pidempi aika
*/
function sec2string($sec){

   if(!is_numeric($sec)) return false; //ei syödä kuin numeroita
   if($sec < 60) return $sec.' s';
   $str = "";

   if($sec >= 31536000){ //ainakin yksi vuosi
      $c = floor($sec/31536000); //jaetaan vuodella sekunteina => saadaan kokonaiset vuodet
      $str = $c . ($c > 1 ? " vuotta " : " vuosi "); //tulos talteen merkkijonoon
      $sec -= (int) $c*31536000;//miinustetaan sekunneista vuodet
   }
   if($sec >= 86400){//ainakin yksi päivä
      $c = floor($sec/86400);
      $str .= $c . ($c > 1 ? " päivää " : " päivä ");
      $sec -= (int) $c*86400;
      return trim($str);
   }
   if($sec >= 3600){//ainakin yksi tunti
      $c = floor($sec/3600);
      $str .= $c . ($c > 1 ? " tuntia " : " tunti ");
      $sec -= (int) $c*3600;
      return trim($str);
   }
   if($sec >= 60){//ainakin yksi minuutti
      $c = floor($sec/60);
      $str .= $c . ($c > 1 ? " minuuttia " : " minuutti ");
      $sec -= (int) $c*60;
   }

   return trim($str);

}
?>

tsuriga [10.09.2004 18:15:14]

#

Näyttää varsin hyvältä, sopivan verran kommentteja. Funktion olisi voinut kommentoida phpdocumentorin ohjeiden mukaisesti jos jotai paranneltavaa. Se "kumman syy" on integerin maksimiarvo, mktime kun palauttaa intin.

Antti Laaksonen [10.09.2004 21:51:50]

#

Hyvä esimerkki, noin se kannattaa tehdä. Taitavat muuten karkausvuodetkin mennä oikein, kun kaksi päivämäärää vähennetään toisistaan. Sen verran kielenhuoltoa, että oikea kirjoitusasu on "sekunti" ja "sekuntia".

Metabolix [10.09.2004 21:56:07]

#

Ihan kiva, mutta eikö olisi helpompaa käyttää valmista funktiota:
date("H:i:s d.m.y", time());
Siitä saa sitten aika helposti muokattua haluamansa vuosiluvun.

ajv [10.09.2004 22:19:43]

#

tsuriga kirjoitti:

Funktion olisi voinut kommentoida phpdocumentorin ohjeiden mukaisesti

Tuon voisi mahdollisesti korjata. Mistähän moiset ohjeet mahtavat löytyä?

Antti Laaksonen kirjoitti:

Sen verran kielenhuoltoa, että oikea kirjoitusasu on "sekunti" ja "sekuntia".

Korjattu

Metabolix kirjoitti:

eikö olisi helpompaa käyttää valmista funktiota:
date("H:i:s d.m.y", time());

Ei palvele samaa asiaa tuo date()-funktio. Se ottaa argumentikseen Unix-aikaleiman, joka siis on aika sekunteina vuoden 1970 alusta. Tämä siis ottaa argumentikseen sekunteja ja muuttaa ne järkevämpään muotoon. Ja tähän ei tietääkseni PHP:ssä ole valmista funktiota.

tsuriga [10.09.2004 22:54:31]

#

Ohjeethan löytyvät osoitteesta http://www.phpdoc.org/. Ajattelin lähinnä sinua :), eli jos joku tahtoo käyttää skriptiä niin sen kopioidessaan hänen on helppo kopioida myös copyright-tiedot, jotka tulevat sijaitsemaan funktion kommentissa. Fame.

T.M. [10.09.2004 23:25:39]

#

Hyvä tekele.

Pientä optimointia/selvennystä ehdottelisin:

if($sec >= 86400){//ainakin yksi päivä
   $c = explode(".",($sec/86400));
   $arr['day'] = $c[0]; $str .= $c[0] . ($c[0] > 1 ? " päivää " : " päivä ");
   $sec -= $c[0]*86400;
}

Muotoon:

if($sec >= 86400){//ainakin yksi päivä
   $c = floor($sec/86400);
   $arr['day'] = $c;
   $str .= $c . ($c > 1 ? " päivää " : " päivä ");
   $sec -= $c*86400;
}

Eli turhaan käyttää explode() funktiota jos saman saa floor() funkkarilla, joka on tietenkin nopeampi ja selvempi. Myös (int)$luku tyylillä voi tehdä tuon saman...
Laitoin myös omille riveilleen jokaisen lausekkeen, joka on tietenkin selvempää koodin kannalta :)
Hyvä tekele jokatapauksessa.

ajv [11.09.2004 01:34:43]

#

T.M.: Totta turiset! Päivitin vinkkiä. Ei tosiaan tullu noin yksinkertainen ratkaisu mieleen.

Kun teen funktioita, niin on tapana tunkea kaikki koodi mahdollisimman pieneen tilaan. Sen takia nuo palautusarvojen asettamiset tapahtuu samalla rivillä.

Metabolix [12.09.2004 12:39:14]

#

PHP-tulkki huomauttaa päivien kohdalla siitä, että $str ei ole välttämättä määritelty (koska sen määrittely on if-lauseen sisällä). $str voisi olla hyvä määritellä jo ennen vuosia, vaikka koodi toimiikin myös tuollaisena.
Karkausvuodet voisi vielä huomioida, pyöreästi joka neljäs. Ylimääräisiä päiviä tulee yli vuoden mittaisissa, koska vuodeksi lasketaan aina 365 päivää.
Lisäksi, tällä hetkellä funtio tukee vain 2^31 sekuntia, koska alussa on intval. Voisi jättää alusta intvalin pois ja lisätä sen vasta minuuttien laskemisen jälkeen, koska siinähän sillä vasta on merkitystä.

ajv [13.09.2004 00:17:44]

#

Metabolix kirjoitti:

PHP-tulkki huomauttaa päivien kohdalla siitä, että $str ei ole välttämättä määritelty (koska sen määrittely on if-lauseen sisällä). $str voisi olla hyvä määritellä jo ennen vuosia, vaikka koodi toimiikin myös tuollaisena.

Korjattu. Lisäksi taulukon solujen oletusarvo on nyt myös asetettu nollaksi.

Metabolix kirjoitti:

tällä hetkellä funtio tukee vain 2^31 sekuntia, koska alussa on intval. Voisi jättää alusta intvalin pois ja lisätä sen vasta minuuttien laskemisen jälkeen, koska siinähän sillä vasta on merkitystä.

Korjattu.

Metabolix kirjoitti:

Karkausvuodet voisi vielä huomioida, pyöreästi joka neljäs. Ylimääräisiä päiviä tulee yli vuoden mittaisissa, koska vuodeksi lasketaan aina 365 päivää.

Tämän toteuttaminen on hieman kyseenalaista. Kuten Antti sanoi vähennettäessä kaksi päivämäärää toisistansa mktime():n avulla nuo karkausvuosien sekunnit on jo kerran poistettu.
Lisäsin karkausvuosien laskemisen kommenttina, jos joku sitä tarvitsee.

NiLon [19.02.2005 01:45:13]

#

$aika = 34534;
$yksikot = array(86400, 3600, 60, 1);
$ajat = array();
foreach($yksikot as $yksikko) {
  if($aika >= $yksikko) {
    $temp = ($aika / $yksikko);
    $aika = $aika % $yksikko;
    $ajat[] = (float) $temp;
  }
  else { $ajat[] = 0; }
}

Hoitaa aikapitkälle saman asian

Metabolix [10.12.2011 02:02:01]

#

Onpa kyllä hankalasti toteutettu, kun näin myöhemmin katsoo. NiLonin tyylinen silmukka olisi parempi.


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta