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); } ?>
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.
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".
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.
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.
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.
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.
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ä.
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ä.
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.
$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
Onpa kyllä hankalasti toteutettu, kun näin myöhemmin katsoo. NiLonin tyylinen silmukka olisi parempi.
Aihe on jo aika vanha, joten et voi enää vastata siihen.