Moro,
Tarkoitus olisi laatia automaattinen kaava virallisten arkipyhien päivämäärien listaamiseen vuodesta riippumatta.
Olen nyt onnistunut laatimaan automaation muiden pyhäpäivien osalta, mutta vielä auki olisivat juhannuspäivä ja pyhäinpäivä.
Esimerkiksi pääsiäisen pyhiin liittyvän kaavan onnistuin laatimaan seuraavan koodin pohjalta:
function paasiaispaiva($vuosi) { return strtotime('+' . easter_days($vuosi) . ' day', mktime(0, 0, 0, 3, 21, $vuosi)); } $vuosi_aloitus = date('Y') - 5; $vuosi_lopetus = $vuosi_aloitus + 10; for ($vuosi = $vuosi_aloitus; $vuosi <= $vuosi_lopetus; $vuosi++) { $paasiaispaiva[] = date('d.m.Y', paasiaispaiva($vuosi)); } print_r($paasiaispaiva);
En kuitenkaan onnistu hyödyntämään kyseistä koodia enää yllä mainitsemiini pyhäpäiviin, joiden päivämäärät muuttuvat vuodesta riippuen.
Olisiko vinkkiä tällaisen kaavan luomiseen?
Juhannus on välillä 20.6.–26.6. oleva lauantai, ja pyhäinpäivä on välillä 31.10.–6.11. oleva lauantai.
$juhannus = strtotime("{$vuosi}-06-20 saturday"); $pyhäinpäivä = strtotime("{$vuosi}-10-31 saturday");
Pääsiäisen laskuakin voi vähän selventää:
$pääsiäispäivä = easter_date($vuosi);
Loistavaa, kiitos avusta jälleen kerran :)
Jatkoa ongelmaan arkipyhien kanssa. (Viittaus: https://www.ohjelmointiputka.net/koodivinkit/30229-php-aikavälien-päällekkäisyyden-laskenta)
Seuraava koodi laskee viikonlopun ja arkipyhien tunnit normaalisti, mikäli aloitus ja lopetus vain ovat ko. vuorokauden sisällä:
//*/ Vuoron lauantaitunnit //////////////////////////////////////////////////////////////////////////////////////////////////////*// $$vuoron_lauantailisa_rivi = (paallekkain_int_viikonpaiva($$vuoron_aloitus_rivi, $$vuoron_lopetus_rivi, "saturday") / 3600); //*/ Vuoron sunnuntaitunnit //////////////////////////////////////////////////////////////////////////////////////////////////////*// $$vuoron_sunnuntailisa_rivi = (paallekkain_int_viikonpaiva($$vuoron_aloitus_rivi, $$vuoron_lopetus_rivi, "sunday") / 3600); //*/ Vuoron sunnuntain iltatunnit //////////////////////////////////////////////////////////////////////////////////////////////////////*// $$vuoron_sunnuntaiiltalisa_rivi = (paallekkain_int_viikonpaiva($$vuoron_aloitus_rivi, $$vuoron_lopetus_rivi, "sunday", $iltalisa_alkaa, $iltalisa_paattyy) / 3600); //*/ Vuoron sunnuntain yötunnit //////////////////////////////////////////////////////////////////////////////////////////////////////*// $$vuoron_sunnuntaiyolisa_rivi = (paallekkain_int_viikonpaiva($$vuoron_aloitus_rivi, $$vuoron_lopetus_rivi, "sunday", $vuorokausi_alkaa, $yolisa_paattyy) / 3600); $$vuoron_sunnuntaiyolisa_rivi += (paallekkain_int_viikonpaiva($$vuoron_aloitus_rivi, $$vuoron_lopetus_rivi, "sunday", $yolisa_alkaa, $vuorokausi_paattyy) / 3600); //*/ Vuoron 100 % tunnit //////////////////////////////////////////////////////////////////////////////////////////////////////*// foreach($sataprosenttiakorvaukset as $sataprosenttiakorvaus) { $sataprosenttiakorvaus_tunnit_aloitus = strtotime("{$sataprosenttiakorvaus} $vuorokausi_alkaa"); $sataprosenttiakorvaus_tunnit_lopetus = strtotime("{$sataprosenttiakorvaus} $vuorokausi_paattyy"); // Maanantai, tiistai, keskiviikko, torstai, perjantai, lauantai if ($sataprosenttiakorvaus_tunnit_aloitus == strtotime("this monday", $sataprosenttiakorvaus_tunnit_aloitus) || $sataprosenttiakorvaus_tunnit_aloitus == strtotime("this tuesday", $sataprosenttiakorvaus_tunnit_aloitus) || $sataprosenttiakorvaus_tunnit_aloitus == strtotime("this wednesday", $sataprosenttiakorvaus_tunnit_aloitus) || $sataprosenttiakorvaus_tunnit_aloitus == strtotime("this thursday", $sataprosenttiakorvaus_tunnit_aloitus) || $sataprosenttiakorvaus_tunnit_aloitus == strtotime("this friday", $sataprosenttiakorvaus_tunnit_aloitus) || $sataprosenttiakorvaus_tunnit_aloitus == strtotime("this saturday", $sataprosenttiakorvaus_tunnit_aloitus)) { $$vuoron_sataprosenttialisa_rivi += (paallekkain_int($$vuoron_aloitus_rivi, $$vuoron_lopetus_rivi, $sataprosenttiakorvaus_tunnit_aloitus, $sataprosenttiakorvaus_tunnit_lopetus) / 3600); $$vuoron_sataprosenttiailtalisa_rivi += (paallekkain_int($$vuoron_aloitus_rivi, $$vuoron_lopetus_rivi, strtotime("{$sataprosenttiakorvaus} $iltalisa_alkaa"), strtotime("{$sataprosenttiakorvaus} $iltalisa_paattyy")) / 3600); $$vuoron_sataprosenttiayolisa_rivi += (paallekkain_int($$vuoron_aloitus_rivi, $$vuoron_lopetus_rivi, strtotime("{$sataprosenttiakorvaus} $vuorokausi_alkaa"), strtotime("{$sataprosenttiakorvaus} $yolisa_paattyy")) / 3600); $$vuoron_sataprosenttiayolisa_rivi += (paallekkain_int($$vuoron_aloitus_rivi, $$vuoron_lopetus_rivi, strtotime("{$sataprosenttiakorvaus} $yolisa_alkaa"), strtotime("{$sataprosenttiakorvaus} $vuorokausi_paattyy")) / 3600); } // Lauantai if ($sataprosenttiakorvaus_tunnit_aloitus == strtotime("this saturday", $sataprosenttiakorvaus_tunnit_aloitus)) { $$vuoron_lauantailisa_rivi -= (paallekkain_int($$vuoron_aloitus_rivi, $$vuoron_lopetus_rivi, $sataprosenttiakorvaus_tunnit_aloitus, $sataprosenttiakorvaus_tunnit_lopetus) / 3600); } } //*///////////////////////////////////////////////////////////////////////////////////////////////////////*//
Esimerkiksi KVTES:n mukaan pyhälisät ja arkipyhälisät alkavat jo edellisen vuorokauden puolella klo 18:00 alkaen.
Olen yrittänyt seuraavan lisäkoodin avulla suorittaa laskentaa kyseisessä tilanteessa:
//*/ Vuoron lauantaitunnit //////////////////////////////////////////////////////////////////////////////////////////////////////*// $$vuoron_lauantailisa_rivi -= (paallekkain_int_viikonpaiva($$vuoron_aloitus_rivi, $$vuoron_lopetus_rivi, "saturday", $vuorokausi_alkaa, "06:00") / 3600); $$vuoron_lauantailisa_rivi -= (paallekkain_int_viikonpaiva($$vuoron_aloitus_rivi, $$vuoron_lopetus_rivi, "saturday", "18:00", $vuorokausi_paattyy) / 3600); //*/ Vuoron sunnuntaitunnit //////////////////////////////////////////////////////////////////////////////////////////////////////*// $$vuoron_sunnuntailisa_rivi += (paallekkain_int_viikonpaiva($$vuoron_aloitus_rivi, $$vuoron_lopetus_rivi, "saturday", "18:00", $vuorokausi_paattyy) / 3600); //*/ Vuoron sunnuntain iltatunnit //////////////////////////////////////////////////////////////////////////////////////////////////////*// $$vuoron_sunnuntaiiltalisa_rivi += (paallekkain_int_viikonpaiva($$vuoron_aloitus_rivi, $$vuoron_lopetus_rivi, "saturday", "18:00", $iltalisa_paattyy) / 3600); //*/ Vuoron sunnuntain yötunnit //////////////////////////////////////////////////////////////////////////////////////////////////////*// $$vuoron_sunnuntaiyolisa_rivi += (paallekkain_int_viikonpaiva($$vuoron_aloitus_rivi, $$vuoron_lopetus_rivi, "saturday", $yolisa_alkaa, $vuorokausi_paattyy) / 3600); //*/ Vuoron 100 % tunnit //////////////////////////////////////////////////////////////////////////////////////////////////////*// foreach($sataprosenttiakorvaukset as $sataprosenttiakorvaus) { $sataprosenttiakorvaus_tunnit_aloitus = strtotime("{$sataprosenttiakorvaus} $vuorokausi_alkaa"); $sataprosenttiakorvaus_tunnit_lopetus = strtotime("{$sataprosenttiakorvaus} $vuorokausi_paattyy"); // Maanantai, tiistai, keskiviikko, torstai, perjantai if ($sataprosenttiakorvaus_tunnit_aloitus == strtotime("this tuesday", $sataprosenttiakorvaus_tunnit_aloitus) || $sataprosenttiakorvaus_tunnit_aloitus == strtotime("this wednesday", $sataprosenttiakorvaus_tunnit_aloitus) || $sataprosenttiakorvaus_tunnit_aloitus == strtotime("this thursday", $sataprosenttiakorvaus_tunnit_aloitus) || $sataprosenttiakorvaus_tunnit_aloitus == strtotime("this friday", $sataprosenttiakorvaus_tunnit_aloitus) || $sataprosenttiakorvaus_tunnit_aloitus == strtotime("this saturday", $sataprosenttiakorvaus_tunnit_aloitus)) { $$vuoron_sataprosenttialisa_rivi += (paallekkain_int($$vuoron_aloitus_rivi, $$vuoron_lopetus_rivi, strtotime("{$sataprosenttiakorvaus} 18:00 - 1 day"), strtotime("{$sataprosenttiakorvaus} $vuorokausi_paattyy - 1 day")) / 3600); $$vuoron_sataprosenttiailtalisa_rivi += (paallekkain_int($$vuoron_aloitus_rivi, $$vuoron_lopetus_rivi, strtotime("{$sataprosenttiakorvaus} 18:00 - 1 day"), strtotime("{$sataprosenttiakorvaus} $iltalisa_paattyy - 1 day")) / 3600); $$vuoron_sataprosenttiayolisa_rivi += (paallekkain_int($$vuoron_aloitus_rivi, $$vuoron_lopetus_rivi, strtotime("{$sataprosenttiakorvaus} $yolisa_alkaa - 1 day"), strtotime("{$sataprosenttiakorvaus} $vuorokausi_paattyy - 1 day")) / 3600); } // Lauantai if ($sataprosenttiakorvaus_tunnit_aloitus == strtotime("this saturday", $sataprosenttiakorvaus_tunnit_aloitus)) { $$vuoron_sataprosenttialisa_rivi -= (paallekkain_int($$vuoron_aloitus_rivi, $$vuoron_lopetus_rivi, strtotime("{$sataprosenttiakorvaus} 18:00"), strtotime("{$sataprosenttiakorvaus} $vuorokausi_paattyy")) / 3600); $$vuoron_sataprosenttiailtalisa_rivi -= (paallekkain_int($$vuoron_aloitus_rivi, $$vuoron_lopetus_rivi, strtotime("{$sataprosenttiakorvaus} 18:00"), strtotime("{$sataprosenttiakorvaus} $iltalisa_paattyy")) / 3600); $$vuoron_sataprosenttiayolisa_rivi -= (paallekkain_int($$vuoron_aloitus_rivi, $$vuoron_lopetus_rivi, strtotime("{$sataprosenttiakorvaus} $yolisa_alkaa"), strtotime("{$sataprosenttiakorvaus} $vuorokausi_paattyy")) / 3600); } // Lauantai if ($sataprosenttiakorvaus_tunnit_aloitus == strtotime("this saturday", $sataprosenttiakorvaus_tunnit_aloitus)) { $$vuoron_lauantailisa_rivi += (paallekkain_int($$vuoron_aloitus_rivi, $$vuoron_lopetus_rivi, strtotime("{$sataprosenttiakorvaus} $vuorokausi_alkaa"), strtotime("{$sataprosenttiakorvaus} 06:00")) / 3600); $$vuoron_lauantailisa_rivi += (paallekkain_int($$vuoron_aloitus_rivi, $$vuoron_lopetus_rivi, strtotime("{$sataprosenttiakorvaus} 18:00"), strtotime("{$sataprosenttiakorvaus} $vuorokausi_paattyy")) / 3600); } } //*///////////////////////////////////////////////////////////////////////////////////////////////////////*//
Sunnuntain pyhätuntien ja lauantaituntien osalta laskenta vaikuttaa menevän jälkimmäisellä lisäyksellä oikein, mutta ongelmaksi muodostuvat arkipyhät mikäli niitä on useampi peräkkäin.
Esimerkiksi jos vuoro alkaa maanantaina 25.12.2017 klo 18:00 ja päättyy seuraavana aamuna klo 06:00 antaa koodi tuloksen 18 tuntia, vaikka sen tulisi olla 12. Samoin iltatunneiksi tulee 8, vaikka sen tulisi olla 4 ja yötunneiksi 10, vaikka sen tulisi olla 8. Tunteja tulee siis liikaa ko. vuoroon.
Milläköhän laskutoimituksella tuon saisi korjattua?
juplin kirjoitti:
Esimerkiksi jos vuoro alkaa maanantaina 25.12.2017 klo 18:00 ja päättyy seuraavana aamuna klo 06:00 antaa koodi tuloksen 18 tuntia, vaikka sen tulisi olla 12. Samoin iltatunneiksi tulee 8, vaikka sen tulisi olla 4 ja yötunneiksi 10, vaikka sen tulisi olla 8. Tunteja tulee siis liikaa ko. vuoroon.
Milläköhän laskutoimituksella tuon saisi korjattua?
Mitä jos käsittelet ma ja ti tunnit erikseen: 18:00 - 24:00 ja 00:00 - 06:00
Ainakin oma PL/I koodini antaa oikean vastauksen...
Kiitos vastauksesta, ongelmahan tässä on nyt ollut, kun en ole onnistunut muodostamaan toimivaa koodia tähän :)
ps: Lisäksi tuo ongelma toistuu myös muina arkipäivinä, mikäli sattuu useampi arkipyhä peräkanaa.
Kun sääntöjä tulee lisää paljon, tuolla lähestymistavalla tulee helposti ongelmia, kun jokin ajankohta täyttää monta ehtoa (esim. juuri iltalisä ja arkipyhälisä). On helpompi käydä läpi työvuoroa koskevat päivät ja tutkia joka päivän kohdalla, mitkä säännöt pätevät eli onko pyhäpäivä, onko ympärillä pyhäpäiviä jne. Ehtoja tulee muutama, mutta ainakin on helppo estää päällekkäisyydet.
En ole varma, huomasinko kaikki ehdot, mutta seuraavassa koodissa on huomioitu ainakin joitain. Koodista tuli mielestäni aika paljon selvempi näin.
<?php function päällekkäin_int($a_alku, $a_loppu, $b_alku, $b_loppu) { return max(0, min($b_loppu, $a_loppu) - max($a_alku, $b_alku)); } function ryhmittele($alku, $loppu) { $ryhmät = (object)[]; $ryhmät->arki = 0; $ryhmät->ilta = 0; $ryhmät->yö = 0; $ryhmät->lauantai = 0; $ryhmät->sunnuntai = 0; $ryhmät->arkipyhä = 0; $ryhmät->aatto = 0; // TODO: Arkipyhät pitäisi kaikki keksiä! $arkipyhät = [ "2017-12-06", "2017-12-25", "2017-12-26", "2018-01-01", "2018-01-06", ]; // TODO: Aattolisään oikeuttavat päivät: pääsiäislauantai, juhannusaatto, jouluaatto $aatot = [ "2017-12-24", ]; // Käydään läpi kaikki päivät. $pv_0 = strtotime("today", $alku); $pv_max = strtotime("today", $loppu); for ($pv = $pv_0; $pv <= $pv_max; $pv = strtotime("tomorrow", $pv)) { $pv_huomenna = strtotime("tomorrow", $pv); // Testataan muutamia päivien ominaisuuksia: la, su, pyhä, ... $su_nyt = date("N", $pv) == 7; $su_huomenna = date("N", $pv) == 6; $la_nyt = date("N", $pv) == 6; $arkipyhä_nyt = in_array(date("Y-m-d", $pv), $arkipyhät); $arkipyhä_huomenna = in_array(date("Y-m-d", $pv_huomenna), $arkipyhät); $aatto_nyt = in_array(date("Y-m-d", $pv), $aatot); // Apufunktio tämän päivän aikavälien laskentaan kellonaikojen mukaan. $t = function($klo1, $klo2) use($alku, $loppu, $pv) { return päällekkäin_int($alku, $loppu, strtotime($klo1, $pv), strtotime($klo2, $pv)) / 3600; }; if ($su_nyt) { // sunnuntai $ryhmät->sunnuntai += $t("00:00", "24:00"); } elseif ($arkipyhä_nyt) { // arkipyhä $ryhmät->arkipyhä += $t("00:00", "24:00"); } elseif ($aatto_nyt) { // aatto $ryhmät->aatto += $t("00:00", "18:00"); if ($su_huomenna) { $ryhmät->sunnuntai += $t("18:00", "24:00"); } else { $ryhmät->arkipyhä += $t("18:00", "24:00"); } } elseif ($la_nyt) { // lauantai // TODO: Onko klo 06–07 la vai yö? $ryhmät->yö += $t("00:00", "06:00"); $ryhmät->lauantai += $t("06:00", "18:00"); $ryhmät->sunnuntai += $t("18:00", "24:00"); } else { // ma-pe (ei arkipyhä) $ryhmät->yö += $t("00:00", "07:00"); $ryhmät->arki += $t("07:00", "18:00"); if ($arkipyhä_huomenna) { $ryhmät->arkipyhä += $t("18:00", "24:00"); } else { $ryhmät->ilta += $t("18:00", "22:00"); $ryhmät->yö += $t("22:00", "24:00"); } } } return $ryhmät; }
$vuoron_alku = "2017-12-01 12:00:00"; $vuoron_loppu = "2018-01-02 12:00:00"; $ryhmät = ryhmittele(strtotime($vuoron_alku), strtotime($vuoron_loppu)); echo "alku: $vuoron_alku\nloppu: $vuoron_loppu\n"; print_r($ryhmät);
jalski: Ongelmahan ei enää ole tuntien määrän laskenta vaan se, miten hommaan pultataan tunnistus siitä, että eilinen tai huominen on pyhäpäivä.
Onko tämä yhdistettävissä aikaisempaan esimerkki funktioosi, vai onko tämä kokonaan uusi ja huomioiko myös edellisen määritykset? Suoraan sanottuna oma koodausosaamiseni ei nyt riitä kokonaisuuden hahmottamiseen.
Toki olen kiitollinen yrityksestäsi auttaa tässä, mutta olisiko tuota alkuperäistä antamaasi funktiota (viittaus koodivinkkeihin) hyödyntäen mahdollista saada tuota laskuvirhettä korjattua ilman, että joudun kokonaan koodin uudelleen rakentamaan?
Metabolix kirjoitti:
jalski: Ongelmahan ei enää ole tuntien määrän laskenta vaan se, miten hommaan pultataan tunnistus siitä, että eilinen tai huominen on pyhäpäivä.
Omassa bittimerkkijonopohjaisessa toteutuksessani tuo ei ole ongelma. Rakennan aluksi nopeasti bittimerkkijonot tarkasteltavan vuoden kaikista lauantaista, sunnuntaista, arkipyhistä, talvi -ja kesäaikaan siirtymisistä, yms. Lisäksi minulla on bittimerkkijonot tuntilajien ja mahdollisten lisien määrittämistä varten.
Näin saan yksinkertaisesti bittioperaatioilla tuntilajit ja lisät suoraan tunnin tarkkuudella. Tarkastettavaksi jää enää mihin tuntilajiin tai lisään minuutit ja sekunnit kuuluvat ja lisätä ne lopputulokseen.
Bonuksena, jos haluan hakea vaikka kaikki tehdyt sunnuntai päivät tietyllä aikavälillä niin saan ne suoraan and operaatiolla tehtyjen työpäivien ja sunnuntaiden välillä. Laskemalla ykkösbittien määrä saadaan tehtyjen sunnuntaiden määrä. Tarkastamalla bitin järjestysnumero bittimerkkijonossa saadaan suoraan selville päivämäärä.
juplin: Tämä on erillinen kokonaisuus, jossa yksi funktio ryhmittelee annetun työvuoron erilaisiin työaikalajeihin. Testiesimerkkinä nyt on 32 päivän "työvuoro" ihan vain mallina siitä, mitä tunteja joulukuun ja vuodenvaihteen ajalle kertyy. Huomioidut erikoistapaukset on varsin selvästi if-lauseissa lueteltu.
Aiemman koodisi korjaukseen ei ole mitään parin rivin kikkaa, koska tuon edelliseltä illalta alkavan sunnuntailisän (arkipyhälisän) takia ratkaisussa pitää huomioida kaikki erilaiset peräkkäisten päivien yhdistelmät eikä edeltävissä funktioissa ole tähän mitään valmista keinoa.
jalski: Ei toki ongelma ollut minullekaan ongelma vaan kysyjälle. Ja mielestäni ei ole mitään erityisen kehuttavaa siinä ratkaisussa, että kaikki vuoden hetket on listattu (vaikka sitten bittijonoina). Sehän on ongelmaan ns. brute force -ratkaisu, vaikka toki vuoden 8760 tuntia hyvin mahtuvatkin muistiin.
Metabolix kirjoitti:
jalski: Ei toki ongelma ollut minullekaan ongelma vaan kysyjälle. Ja mielestäni ei ole mitään erityisen kehuttavaa siinä ratkaisussa, että kaikki vuoden hetket on listattu (vaikka sitten bittijonoina). Sehän on ongelmaan ns. brute force -ratkaisu, vaikka toki vuoden 8760 tuntia hyvin mahtuvatkin muistiin.
En nyt sentään vuoden kaikkia tunteja bittimerkkijonona tallenna. Vuoden päivistä, vuoden pyhistä, vuoden lauantai päivistä, vuoden sunnuntai päivistä ja vuorokautena tehdyistä tunneista muodostan bittimerkkijonot. Lisäksi löytyy läjä bittimaskeja, joita yhdistelemällä saan tuntilajikkeet ja lisät suoraan selville.
Kiitos jälleen Metabolix. Alan pikkuhiljaa hahmottamaan tuota koodia ja vaikuttaa lupaavalta.
Aihe on jo aika vanha, joten et voi enää vastata siihen.