Visual Basic: Työajan jakaminen Yö-, Päivä-, Iltavuoro
Minulla on ohjemapätkä jossa:
StartTime ja EndTime.
Kokonaistunnit otan lauseesta:
Kokonaistunnit = DateDiff("s", txtStartTime, txtEndTime) / 60 / 60
(tarvitsen sekunnin osat myös).
Esim. työntekijä 1 aloittaa työt 20.04.2017 19:57:19 ja lopettaa 21.04.2017 21:35:42
Nyt näistä tiedoista tarvitsisi saada tehdyt vuorotunnit:
Yötunnit (aikaväli #00:00:00# - #06:00:00#)
Päivätunnit (aikaväli #06:00:00# - #18:00:00#)
Iltatunnit (aikaväli #18:00:00# - #24:00:00#)
Löytyisikö vanhasta keskustelusta jotain vinkkejä?
https://www.ohjelmointiputka.net/keskustelu/
Itse kirjoittelisin jonkunlaiset funktiot bittimerkkijonojen käsittelyä varten, joissa olisi riittävä toiminnallisuus tätä tehtävää varten.
Jännä muuten, miten moni nykypäivän kieli ei bittimerkkijonoja suoraan tue vaikka nämä helpottaisivat suuresti erilaisten joukkojen käsittelyä.
Alla PL/I toteutus, saako joku toteutettua helpommin?
*PROCESS MARGINS(1,180) LIBS(SINGLE,STATIC); *PROCESS OPTIMIZE(2) DFT(REORDER); test: proc options(main); dcl nightmask bit(24) init('111111000000000000000000'b); dcl eveningmask bit(24) init('000000000000000000111111'b); dcl daymask bit(24) init('000000111111111111000000'b); dcl hourmask bit(24); dcl result bit(24); dcl seconds fixed decimal(15,0); dcl (started, ended) char (19); dcl 1 worktime, 2 hours pic'99', 2 * char(1) init(':'), 2 minutes pic'99', 2 * char(1) init(':'), 2 seconds pic'99'; /* Aikaleimat */ started = '20.04.2017 05:35:01'; ended = '20.04.2017 20:47:10'; /* tehdyt tunnit */ hourmask = copy('0'b, substr(started, 12, 2)) || copy('1'b, substr(ended, 12, 2)-substr(started, 12, 2)); /* Päivätunnit */ result = hourmask & daymask; seconds = (tally(result, '1'b)) * 3600; if substr(daymask, wrap(substr(started, 12, 2)+1), 1) = '1'b then seconds -= substr(started, 15, 2) * 60 + substr(started, 18, 2); if substr(daymask, wrap(substr(ended, 12, 2)+1), 1) = '1'b then seconds += substr(ended, 15, 2) * 60 + substr(ended, 18, 2); worktime.hours = seconds/3600; worktime.minutes = mod(seconds, 3600)/60; worktime.seconds = mod(seconds, 60); put skip list('Day time : ' || string(worktime)); /* Iltatunnit */ result = hourmask & eveningmask; seconds = (tally(result, '1'b)) * 3600; if substr(eveningmask, wrap(substr(started, 12, 2)+1), 1) = '1'b then seconds -= substr(started, 15, 2) * 60 + substr(started, 18, 2); if substr(eveningmask, wrap(substr(ended, 12, 2)+1), 1) = '1'b then seconds += substr(ended, 15, 2) * 60 + substr(ended, 18, 2); worktime.hours = seconds/3600; worktime.minutes = mod(seconds, 3600)/60; worktime.seconds = mod(seconds, 60); put skip list('Evening time: ' || string(worktime)); /* Yötunnit */ result = hourmask & nightmask; seconds = (tally(result, '1'b)) * 3600; if substr(nightmask, wrap(substr(started, 12, 2)+1), 1) = '1'b then seconds -= substr(started, 15, 2) * 60 + substr(started, 18, 2); if substr(nightmask, wrap(substr(ended, 12, 2)+1), 1) = '1'b then seconds += substr(ended, 15, 2) * 60 + substr(ended, 18, 2); worktime.hours = seconds/3600; worktime.minutes = mod(seconds, 3600)/60; worktime.seconds = mod(seconds, 60); put skip list('Night time : ' || string(worktime)); wrap: proc(x) returns(fixed bin(31)); dcl x fixed bin(31); if x < 1 | x > 24 then x = 1; return(x); end wrap; end test;
Pistetään vielä HotBasic versio, minkä pohjalta Visual Basic version tekeminen ei tuottane ongelmia:
$APPTYPE CONSOLE: $TYPECHECK ON type TIMEFIELD hours as long minutes as long seconds as long end TIMEFIELD declare function calc(mask as string) as long declare function wrap(x as long) as long dim started as TIMEFIELD, ended as TIMEFIELD defstr nightmask = "111111000000000000000000" defstr eveningmask = "000000000000000000111111" defstr daymask = "000000111111111111000000" defstr hourmask deflng seconds ' Aseta työaika started.hours = 05 started.minutes = 35 started.seconds = 01 ended.hours = 20 ended.minutes = 47 ended.seconds = 10 ' Rakenna maski tehdyille tunneille hourmask = string$(started.hours, "0") hourmask.append string$(ended.hours - started.hours, "1") hourmask.append string$(24 - hourmask.len, "0") ' Päivätunnit seconds = calc(daymask) print "day hours:" print str$(seconds \ 3600)+ ":" + str$((seconds mod 3600) \ 60)+ ":" + str$(seconds mod 60) + crlf ' Iltatunnit seconds = calc(eveningmask) print "evening hours:" print str$(seconds \ 3600)+ ":" + str$((seconds mod 3600) \ 60)+ ":" + str$(seconds mod 60) + crlf ' Yötunnit seconds = calc(nightmask) print "night hours:" print str$(seconds \ 3600)+ ":" + str$((seconds mod 3600) \ 60)+ ":" + str$(seconds mod 60) + crlf pause end function calc(mask as string) as long deflng i, seconds defstr res = "" for i = 1 to 24 if hourmask[i] = mask[i] then res.append hourmask[i] else res.append "0" end if next i seconds = 0 for i = 1 to 24 if res[i] = "1" then inc(seconds) end if next i seconds = seconds * 3600 if mask[wrap(started.hours + 1] = "1" then seconds = seconds - started.minutes * 60 - started.seconds end if if mask[wrap(ended.hours + 1)] = "1" then seconds = seconds + ended.minutes * 60 + ended.seconds end if result = seconds end function function wrap(x as long) as long if x < 1 or x > 24 then result = 1 else result = x end if end function
... Ja vielä .NET toteutuskin kirjoiteltuna X#:llä. En .NET ohjelmointia ole harrastellut, joten varmaan löytyy tehokkaampia tapoja tehdä useimpia asioita.
EDIT: koodi poistettu, postitan uudelleen kunhan saan virheen korjattua...
Tuossa bittikikkailussa nyt ei ole mitään järkeä. Koodi rajoittuu tasatunteihin ja varmaankin käsittelee karkausvuodet ym. väärin.
Järkevä ratkaisu olisi irrottaa alku- ja loppuajankohdista päivämäärät, käydä silmukassa läpi päivät tuolta väliltä ja jokaisen päivän kohdalla tarkastaa kunkin kolmen työaikalajin määrä.
Siis tähän tapaan (kuvitteellisilla funktioilla, koska en tunne VB:n päivämääräfunktioita):
# käydään läpi eri päivät: For p = alku.Date To loppu.Date # lasketaan jokainen työaikalaji tuolta päivältä: t_y = t_y + Max(0, DateDiff(Max(alku, p + " 00:00"), Min(loppu, p + " 06:00"))) t_p = t_p + Max(0, DateDiff(Max(alku, p + " 06:00"), Min(loppu, p + " 18:00"))) t_i = t_i + Max(0, DateDiff(Max(alku, p + " 18:00"), Min(loppu, p + " 24:00"))) Next p
Koodiakin tulee noin 80 riviä vähemmän kuin jalskin viritelmillä.
Metabolix kirjoitti:
Tuossa bittikikkailussa nyt ei ole mitään järkeä. Koodi rajoittuu tasatunteihin ja varmaankin käsittelee karkausvuodet ym. väärin.
Koodiakin tulee noin 80 riviä vähemmän kuin jalskin viritelmillä.
Ei rajoitu tasatunteihin, vaan laskee sekunnin tarkkuudella. Karkaussekunnit pitää käsitellä tietysti erikseen tarvittaessa, jos ei voi elää mahdollisen 1-2 sekunnin virheen kanssa vuorokauden kestossa sekunteina.
Mitä koodin pituuteen tulee, niin tuo muutama rivi PL/I-koodia mikä laskemiseen tarvitaan on varmasti pienempi kuin DateDiff() funktion sisältämä määrä koodia.
Kuinka itse kirjoittaisit funktion mikä laskee tuntilajin määrän vuorokaudessa käyttämättä valmista kirjastofunktiota ja saisitko sen lyhemmäksi kuin omat alla olevat neljä riviä PL/I-koodia?
result = hourmask & daymask; seconds = (tally(result, '1'b)) * 3600; if substr(daymask, wrap(substr(started, 12, 2)+1), 1) = '1'b then seconds -= substr(started, 15, 2) * 60 + substr(started, 18, 2); if substr(daymask, wrap(substr(ended, 12, 2)+1), 1) = '1'b then seconds += substr(ended, 15, 2) * 60 + substr(ended, 18, 2);
Mistä vaan saa one linerin jos ei vaan paina enteriä.
Mutta mitä tulee yleisesti esim .Netillä aika-alueiden käsittelyyn, on TimePeriod suosittu kirjasto, jolla nuo saa kaivettua näppärästi ilman mitään kikkailuja.
Mitään tarvetta bittimaskeille en kyllä näe vaikka kirjastoa ei käyttäisikään, miksi ei tekisi suoraan tarkistusta sille kuuluuko tunti mihinkin vuoroon, ja laskee siitä TimeSpanin suhteessa toteutuneeseen työaikaan? Lopuksi sitte ynnää eri vuoroihin saadut max tunnin mittaiset TimeSpanit yhteen vaikka millisekunnin tarkkuudella jos tykkää.
Aihe on jo aika vanha, joten et voi enää vastata siihen.