Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: VBA: Kahden päivämäärän välinen ero

Sivun loppuun

Mamma [27.05.2011 10:46:35]

#

Olen yrittänyt laskea datediffillä kahden päivämäärän välistä eroa niin, että se antaa päivämäärien välisen ajan kuukausina eli näin kesto = DateDiff("m", alkupvm, loppupvm). Jos alkupvm on 1.1.2011 ja loppupvm 1.2.2011 funktio antaa tulokseksi 1. Mutta jos alkupvm on tuo sama ja loppupvm on 31.1.2011, funktio antaa nolla. Minä tarvitsisin fuktion, joka antaa tulokseksi yksi, vaikka loppupvm olisi 31.1.2011. Yritin lisätä dateadd-funktiolla yhden päivän jos päivä on 30 tai 31, mutta sitten tarvitsisi ottaa huomioon myös helmikuu ja karkausvuodet, joten totesin sen huonoksi ideaksi.
Miten muokkaan koodia, jotta saisin tämän onnistumaan?

Torgo [27.05.2011 11:18:41]

#

Eikö tämä aikaisemmassa keskustelussa ollut kaava toimi? Jos ei suoraan, niin ei se kovin suurta muokkausta tarvinne.

Mamma [27.05.2011 11:58:55]

#

Kiitos vastauksestasi Torgo. Tuo aiempi kaava antaa juuri saman vastauksen eli tapauksessa 1.1.2011-31.1.2011 se antaa 30 päivää, vaikka haluaisin, että tulokseksi tulisi yksi kuukausi. Olen yrittänyt muokata tätäkin kaavaa, mutta taitoni eivät yksinkertaisesti riitä! Onko jotain ehdotuksia, miten muokkaisin tuota kaavaa?

Torgo [27.05.2011 12:04:26]

#

No, koska haluat kaavan laskevan kuukauden täydeksi, kun siitä puuttuu vielä yksi päivä, niin eikö yksinkertaisinta olisi siirtää loppupäivää yhdellä eteenpäin? Ketjussa on jo annettu kaava miten päivämäärään lisätään 6kk, joten siitä pitäisi saada helposti muokattua sopiva versio.

Mamma [27.05.2011 12:33:25]

#

Minä kokeilinkin lisätä yhden päivän jos tuo toinen päivämäärä on 31. Mutta siinä tulee se ongelma, että toisissa kuukausissa on 30 päivää, jos laitan kaavaan ehdon, että lisää yhden päivän jos päivien määrä on 30, niin silloinhan se lisää sen päivän myös esim. 30.1.2011, jolloin se on väärin. Sitten on vielä helmikuu ja karkausvuosi, jotka myös pitäisi huomioida. Minulla on siis tilanne, että listassa on kaikenlaisia päivämääriä kahdessa eri sarakkeessa ja minun pitäisi laskea noiden kahden päivämäärän välinen ero kuukausissa. Tuo kaava tai datediff toimii hyvin ja oikein, jos laskee vaikka 13.1.2011-28.4.2011 välisen keston kuukausissa eli se antaa 3 kuukautta ja 15 päivää. Mutta sitten kun toisena päivämääränä on kuun viimeinen päivä, se ei anna vastaukseksi sitä yhtä kuukautta.

Torgo [27.05.2011 12:57:07]

#

=DATE(YEAR(A1);MONTH(A1);DAY(A1)+1)

Hyvin tuo tuntuu lisäävän päivän riippumatta alkupäivästä.

Mamma [27.05.2011 13:15:29]

#

Niin kyllä tuo lisää oikein yhden päivän, mutta kun sen ei saisi lisätä sitä kuin vain silloin kun loppupäivämääränä on kuukauden viimeinen päivä? Tämä sen takia, että kun haluan saada kahden päivämäärän välisen eron kuukausissa, niin se antaisi tilanteessa 1.1.2011-31.1.2011 tulokseksi 1 kuukautta, niin tässä tapauksessa sen pitäisi lisätä tuohon 31.1.2011 yksi päivä, jolloin tulokseksi tulisi se yksi kuukausi. Muissa tapauksissa päivämäärien välinen ero tulee kaavalla oikein. Eli nyt se kaava siinä aikaisemmassa keskustelussa antaa tällaiset vastaukset:
13.1.2011-28.4.2011 3 kuukautta 15 päivää
1.1.2011-31.1.2011 0 kuukautta 30 päivää
Eli haluaisin, että tuon viimeisen päivämäärien ero pitäisi olla 1 kuukautta 0 päivää.

Torgo [27.05.2011 14:11:49]

#

Siis haluat että 1.1.2011-31.1.2011 tulokseksi tulisi 1 kk, mutta 2.1.2011-1.2.2011 tulokseksi tulisi 0 kk? Siinä tapauksessa laitat vain siihen päivän lisäykseen ehdon, että se tehdään vain jos kyseessä on kuun viimeinen päivä. Tarkistuksen voi tehdä tuhannella tapaa. Itselle ensimmäisenä tuli mieleen että päivän lisääminen tehdään, jos se vaihtaa samalla kuukautta. Googlella löytyy varmasti helpompiakin tapoja.

Edit.
Tässä vielä tuo oma ajatukseni pikaisesti kyhättynä. Siistimpiäkin tapoja varmasti on, mutta ainakin tämä toimi kun testasin:

=IF(MONTH(DATE(YEAR(A1);MONTH(A1);DAY(A1)+1))<>MONTH(A1);DATE(YEAR(A1);MONTH(A1);DAY(A1)+1);A1)

neau33 [28.05.2011 02:34:51]

#

Moi Mamma!

oheinen funktio palauttaa annetusta päivämäärästä kuukauden viimeisen päivän...

Private Function LastOfMonth(pDate As Date) As Integer
   Select Case Month (pDate)
      Case 1, 3, 5, 7, 8, 10, 12
         LastOfMonth = 31
      Case 4, 6, 9, 11
         LastOfMonth = 30
      Case 2
         If (Year(pDate) Mod 4) = 0 Then
            LastOfMonth = 29
         Else
           LastOfMonth = 28
         End If
   End Select
End Function

Private Sub Command1_Click()
    'testi...
    Dim Date1 As Date, Date2 As Date
    Date1 = "1.2.2011": Date2 = "28-2-2011"

    If Year(Date1) = Year(Date1) And Month(Date1) = Month(Date2) _
    And Day(Date1) = 1 And Day(Date2) = LastOfMonth(Date2) Then
        MsgBox ("1 kuukausi")
    ElseIf Year(Date1) = Year(Date1) And Month(Date1) = Month(Date2) _
    And Day(Date1) = 1 And Day(Date2) < LastOfMonth(Date2) Then
        MsgBox DateDiff("d", Date1, Date2) & " päivää"
    'ElseIf '....
       '...
    Else '...
    End If

End Sub

Hennkka [28.05.2011 12:10:46]

#

Pari korjausta neau33:n koodiin:
Karkausvuosi on joka neljäs vuosi PAITSI jos vuosi on jaollinen 400lla.

Toiseksi, miksi koodi tarkistaa Year(Date1) = Year(Date1)?

neau33 [28.05.2011 13:05:18]

#

MOI taas!

Henkka@
Year(Date1) = Year(Date1) copy/paste failuresta huolimatta toi funkkari palauttaa annetun päivämäärän kuukauden viimeisen päivän ainakin seuraavat 1000 vuotta mikäli käyttis sallii...

jtha [28.05.2011 19:40:43]

#

Ota ero päivinä ja pyöristä...

Äh, kirjoitin ennen kuin ajattelin, sorry.

Metabolix [28.05.2011 20:44:51]

#

Ongelman voisi kyllä määritellä vielä selvemmin. Onko tosiaan tarkoitus, että kuukauden ensimmäisestä päivästä viimeiseen on yksi kuukausi (esim. siis helmikussa 28 päivää mutta tammikuussa 31 päivää)? Silloinhan ongelma ratkeaa niin, että loppupäivämäärään lisätään aina yksi päivä. Kuitenkin ratkaisu vaikuttaa aika typerältä, koska sama määrä päiviä voi tuottaa eri määrän kuukausia.

Hennkka ja neau33 ovat molemmat väärässä karkausvuoden suhteen. 400:llä jaolliset ovat, muut 100:lla jaolliset eivät, loput 4:llä jaolliset ovat. Siispä neau33:n koodi toimisi vain vuoteen 2099 asti, koska vuoden 2100 ei kuulu olla karkausvuosi.

Hennkka [29.05.2011 09:13:46]

#

Niinpä onkin. Ei pitäisi yrittää vastata ulkomuistista asioihin, joista ei ole itsekkään varma. Täytyy varmaan vierittää syy puhelimen niskoille, koska ei voinut tarkistaa Wikipediasta:D

Mamma [30.05.2011 08:21:30]

#

Kiitos kaikille vastauksista. Kuten Metabolix sanoi, se vaikuttaa typerältä, että lisätään jokaiseen kuukauden viimeiseen päivään yksi päivä, jotta datediff antaisi vastauksen yksi kuukausi. Tässä tapauksessahan pitäisi tosiaankin ottaa huomioon kaikkien kuukausien päivien määrät ja karkausvuodet. On kyllä aika kummallista, että Excelissä ei ole valmiina sellaista funktiota, joka antaisi tulokseksi yhden kuukauden, jos ensimmäinen päivämäärä on kuun ensimmäinen päivä ja toinen kuun viimeinen päivä. Oikeastaanhan noiden kahden päivämäärän välinen ero on vain esim. 30 päivää, jos päivämääräväli on 1.1.2011-31.1.2011. Mutta jos ajatellaan vaikka työsuhteen kestoa, niin kyllä silloin lasketaan työsuhteen pituuteen myös tuo viimeinenkin päivä eli työsuhteen pituus on yksi kuukausi. Jos laskee datediffillä esim. 1.1.2011-16.1.2011, se antaa tulokseksi 15 päivää, vaikka jos taas ajatellaan esim. työsuhteen kestoa, niin silloin pitäisi kestoksi tulla 16 päivää. Eli näissäkin tapauksissa pitäisi lisätä yksi päivä tuohon loppupäivämäärään. Ei kai tässä ole muuta vaihtoehtoa, kuin koodata niin, että kaikkiin loppupäivämääriin lisätään yksi päivä ja sen jälkeen vasta vähennetään loppupäivästä alkupäivä?

Merri [30.05.2011 13:16:02]

#

Function Kesto(ByVal AloitusPvm As Date, ByVal LopetusPvm As Date) As String
    Dim Vuodet As Integer, Kuukaudet As Integer, Paivat As Integer
    Dim PaiviaKuukaudessa As Integer

    Vuodet = DateDiff("y", AloitusPvm, LopetusPvm)
    AloitusPvm = DateAdd("y", Vuodet, AloitusPvm)

    Kuukaudet = DateDiff("m", AloitusPvm, LopetusPvm)
    AloitusPvm = DateAdd("m", Kuukaudet, AloitusPvm)

    Paivat = DateDiff("d", AloitusPvm, LopetusPvm) + 1

    PaiviaKuukaudessa = Day(DateSerial(Year(AloitusPvm), Month(AloitusPvm) + 1, 0))

    If Paivat >= PaiviaKuukaudessa Then
        Kuukaudet = Kuukaudet + 1
        Paivat = Paivat - PaiviaKuukaudessa
    End If

    If Vuodet > 0 Then Kesto = Vuodet & " vuotta"
    If Kuukaudet > 0 Then
        If LenB(Kesto) <> 0 Then Kesto = Kesto & ", "
        Kesto = Kesto & Kuukaudet & " kuukautta"
    End If
    If Paivat Then
        If LenB(Kesto) <> 0 Then Kesto = Kesto & ", "
        Kesto = Kesto & Paivat & " päivää"
    End If
End Function

Testaamaton.

jtha [30.05.2011 13:31:25]

#

Entäs siten että otetaan kaksi vastausta ja valitaan niistä isompi.
Eka normaalisi, toinen siten että lisätään molempiin päivämääriin yksi.

Merri [31.05.2011 13:47:07]

#

Laitetaan tässä nyt eri päivän kahvitauolla sitten, että miten tuo edellisen viestini koodi toimii.

Yksinkertaisin kohta on vuosien ja kuukausien laskeminen, aloituspäivämäärään lisätään DateDiffin ilmoittama ero. DateDiff ilmoittaa muistaakseni vain täysin kokonaiset vuodet ja kuukaudet, joten tällä tavoin kasvattamalla AloitusPvm:ää jää jäljelle ensin vain kuukaudet ja sitten lopulta päivät.

Lopulta sama tehdään myös päiville, mutta lisätään aina yksi. Tämän jälkeen katsotaan montako päivää on viimeisessä kuukaudessa, joka aloituspäivämäärän kohdalle osuu. Jos lukema on kuukauden päivien määrä tai tätä isompi, niin kuukausien määrää kasvatetaan yhdellä ja vastaavasti päivien määrä pienenee kuukauden päivien määrällä.


Aikavälin 1.1.2011 - 1.2.2011 pitäisi siis antaa vastaukseksi "1 kuukautta, 1 päivää"

Karkausvuosia ym. ei tarvitse ottaa huomioon, koska VB:n päivämääräfunktioita käyttäen nämä asiat otetaan huomioon taustalla automaagisesti (olettaen että siellä ei ole bugeja).

Mamma [06.06.2011 14:57:25]

#

Nyt kokeilin tuota Merrin koodia, mutta se antaa aikavälin 1.1.2011-1.2.2011 vastaukseksi 31 vuotta 1 päivää. Yritin korjata koodia, mutta en oikein osannut?

Merri [07.06.2011 06:27:04]

#

"y" tilalle "yyyy"

"y" ilmoittaakin näemmä päivän vuoden järjestysnumeron. Sen takia tulee 31 vuotta.

Mamma [07.06.2011 09:56:03]

#

Kiitos kovasti Merri, nyt koodi toimii kuin unelma! Kiitos myös itse koodista, nyt pääsen eteenpäin hommassani!

Mamma [07.06.2011 13:53:59]

#

Nyt vielä testasin ja koodi antaa ajalle 1.1.2009-1.1.2010 tulokseksi 1 vuotta ja 1 päivää. Eli tuo vuoden vaihtuminen nyt jotenkin sekoittaa. Minä olen niin alussa tämän koodin kanssa, että en osaa korjata tätä?

Torgo [07.06.2011 14:21:55]

#

Eikös se toimi kuten pitääkin? 1.1.2009-31.12.2009 pitäisi olla 1 vuosi, jolloin 1.1.2009-1.1.2010 pitäisikin olla 1 vuosi ja 1 päivä. Vai ymmärsinkö jotain väärin?

Grez [07.06.2011 14:35:32]

#

Niin, 6.6. - 7.6. on 2 päivää, mutta päivien 6.6. ja 7.6. välinen ero on 1 päivä. Eli jos tässä nyt eroa kysytään niin 1.1.2009 vs 1.1.2010 on tasan 1 vuosi.

Eli ongelmahan poistuu, kun poistaa tämän rivin lopussa olevan +1:

Paivat = DateDiff("d", AloitusPvm, LopetusPvm) + 1

Mamma [07.06.2011 15:33:36]

#

Minä laitoin äsken väärän esimerkin eli se on mun mielestä oikein, mutta sitten kun laittaa välin 1.12.2009-31.1.2010 koodi antaa tulokseksi 1 vuotta. Tuon aikavälin erotushan on 2 kuukautta?

Merri [07.06.2011 16:01:06]

#

Hmm, jos DateDiffin vuosilaskuri antaa vastaukseksi 1 kun vuosilukema poikkeaa, niin sitten tuo koodi ei ymmärrettävästi toimi. Ei ole VB6 asennettuna, niin on testaaminen vähän heikossa.

Tuon ongelman ratkominen muuttuu vähän ongelmallisemmaksi, eli pitäisi tarkistaa ensin vuoden päivien määrä, verrata sitä päivien erotukseen vuosien välillä ja sitten vasta tietää, onko Vuodet tuossa tapauksessa 1 vai 0.

Grez [07.06.2011 16:07:46]

#

Niin, 1 vuosi, -10 kuukautta ja 2 päivää. Mutta toi ei näytä negatiivisia kuukausia.

Mites tällainen suht perverssin näköinen koodi:
(Tämähän piti kai olla Excelin VBA eikä VB6:ssa? Testattu Excelissä)

Function Kesto(ByVal AloitusPvm As Date, ByVal LopetusPvm As Date) As String
    Dim Vuodet As Integer, Kuukaudet As Integer, Paivat As Integer

    Dim Vu As Integer, Kk As Integer, Pv As Integer

    LopetusPvm = LopetusPvm +1 'Halutaan laskea sekä alku- että loppupäivä mukaan

    Vu = Year(AloitusPvm): Kk = Month(AloitusPvm): Pv = Day(AloitusPvm)

    Vuodet = Year(LopetusPvm) - Vu
    Kuukaudet = Month(LopetusPvm) - Kk

    If DateSerial(Vu + Vuodet, Kk, Pv) > LopetusPvm Then
        Vuodet = Vuodet - 1
        Kuukaudet = Kuukaudet + 12
    End If

    If DateSerial(Vu + Vuodet, Kk + Kuukaudet, Pv) > LopetusPvm Then Kuukaudet = Kuukaudet - 1

    Paivat = LopetusPvm - DateSerial(Vu + Vuodet, Kk + Kuukaudet, Pv)

    If Vuodet > 0 Then Kesto = Vuodet & " vuotta"
    If Kuukaudet > 0 Then
        If LenB(Kesto) <> 0 Then Kesto = Kesto & ", "
        Kesto = Kesto & Kuukaudet & " kuukautta"
    End If
    If Paivat Then
        If LenB(Kesto) <> 0 Then Kesto = Kesto & ", "
        Kesto = Kesto & Paivat & " päivää"
    End If
End Function

jalski [07.06.2011 17:41:10]

#

Eikös VB6:ssa ole mukana funktioita, joilla päivämäärän voisi muuttaa Lilian days muotoon ja siitä takaisin normaaliin päiväykseen?

Eli siis muotoon, joka esittää päivämäärään päivinä Gregoriaanisen kalenterin alusta.

Meinaan vaan, että tuosta saisi kivat luvut, joilla on helppo laskea kulunutta aikaa.

PL/I:llä ohjelmoidessa, näin nopeasti mietittynä lähtisin varmaan tuosta lähtökohdasta liikkeelle...

-jalski

Grez [07.06.2011 20:21:48]

#

Joo. Tuohon muotoon voi muuttaa funktiolla int() ja takaisin cdate(). Tai no, alkupäivänä on 1.1.1900 ja negatiiviset siitä taakse päin. En vaan hahmota miten olisi hyötyä koodata oma kalenteri, kun se on valmiinakin.

Merri [08.06.2011 09:20:55]

#

Grez: VBA ja VB6 on melkein sama asia perusfunktioiden kirjoittamisen kannalta :)

Torgo [08.06.2011 09:58:37]

#

Mamma kirjoitti:

Ei kai tässä ole muuta vaihtoehtoa, kuin koodata niin, että kaikkiin loppupäivämääriin lisätään yksi päivä ja sen jälkeen vasta vähennetään loppupäivästä alkupäivä?

Jos se kelpaa ratkaisuksi, niin sitten tuo alunperinkin ehdottamani Excel-kaava edellisestä ketjusta toimii oikein. Testailin Excel 2007:lla ja kaikki tässä ketjussa esitetyt esimerkkitapaukset toimii oikein.

Grez [08.06.2011 12:35:23]

#

Merri kirjoitti:

Grez: VBA ja VB6 on melkein sama asia perusfunktioiden kirjoittamisen kannalta :)

Tiedän. Tuo oli lähinnä kommentiksi sinulle:

Merri kirjoitti:

Ei ole VB6 asennettuna, niin on testaaminen vähän heikossa.

Ehkä tarkoitit sanoa ettei ole myöskään Exceliä asennettuna. Aloittaja kuitenkin ilmeisesti käyttää Exceliä, joten en ymmärtänyt miksi se ehdoin tahdoin VB6:ssa pitäisi testata.


Sivun alkuun

Vastaus

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

Tietoa sivustosta