Newsgroup: it.comp.lang.visual-basic
Titolo: [?!] DateAdd() e DateDiff()

Vorrei che qualcuno mi spiegasse perchè, quando si devono fare operazioni
con le date e i giorni, tutti si ostinino ad usare le funzioni in oggetto!
Se qualcuno ha una vaga idea di come è fatto il tipo di dato Date (se no
vedi <F1>) sa che per aggiungere/togliere/calcolare giorni (ma anche ore,
minuti ecc.) basta fare:
Data2 = Data + giorni
Data2 = Data - giorni
giorni = Data2 - Data
Non vedo quale arcano vantaggio avrei nel richiamare una funzione
(sintatticamente complessa, tra l'altro) anzichè usare gli operatori
matematici già belli e pronti (e più digeribili per il compilatore).
Ciao

 giorni = Data2 - Data
Mah, io non ho mai usato DateAdd o DateDiff, perň se devi sommare un anno ad
una data, non puoi certo scrivere:
Data2 = Data + 365
Personalmente mi rifugio in DateSerial...
Ciao,
Carlo.

matematici già belli e pronti (e più digeribili per il compilatore).
Questo e' vero, soprattutto per la DateDiff, mentre tra DateAdd e l' operatore
di somma c'e' poca differenza.
Dato il seguente programmino, compilato in codice nativo ottimizzato per velocita'
e con simboli di debug:
Option Explicit
Public Sub Main()
    Dim dt1 As Date
    Dim dt2 As Date
    Dim foo As Variant
 
    dt1 = Date
    dt2 = DateSerial(2001, 10, 3)
 
    foo = DateAdd("d", 10, dt1)
    foo = DateDiff("d", dt2, dt1)
 
    foo = dt1 + 10
    foo = dt2 - dt1
End Sub
Si ottiene, in particolare, il seguente assembly:
12:       foo = DateAdd("d", 10, dt1)
004016AA   lea         edx,[unnamed_var1]
004016AD   lea         eax,[unnamed_var1]
004016B0   push        edx
004016B1   push        40240000h
004016B6   push        esi
004016B7   lea         ecx,[dt1]
004016BA   mov         edi,4007h
004016BF   push        offset ___vba@0019F4F8 (0040135c)
004016C4   push        eax
004016C5   mov         dword ptr [ebp-48h],ecx
004016C8   mov         dword ptr [unnamed_var1],edi
004016CB   call        dword ptr [__imp____vba@0019F594 (00401018)]
004016D1   mov         esi,dword ptr [__imp_@__vbaVarMove (00401008)]
004016D7   lea         edx,[unnamed_var1]
004016DA   lea         ecx,[foo]
004016DD   call        esi
contro:
15:       foo = dt1 + 10
00401714   fld         qword ptr [dt1]
00401717   fadd        qword ptr [__real@8@4002a000000000000000 (004010b0)]
0040171D   sub         esp,8
00401720   fnstsw      ax
00401722   test        al,0Dh
00401724   jne         $L56+11h (0040178c)
00401726   fstp        qword ptr [esp]
00401729   call        dword ptr [__imp____vbaDateR8 (00401034)]
0040172F   fstp        qword ptr [ebp-48h]
00401732   mov         dword ptr [unnamed_var1],7
00401739   lea         edx,[unnamed_var1]
0040173C   lea         ecx,[foo]
0040173F   call        esi
Invece, per quanto concerne la differenza fra date:
13:       foo = DateDiff("d", dt2, dt1)
004016DF   lea         ecx,[dt1]
004016E2   lea         edx,[dt2]
004016E5   mov         dword ptr [ebp-58h],ecx
004016E8   push        1
004016EA   lea         eax,[unnamed_var1]
004016ED   push        1
004016EF   lea         ecx,[unnamed_var1]
004016F2   mov         dword ptr [ebp-48h],edx
004016F5   push        eax
004016F6   push        ecx
004016F7   lea         edx,[unnamed_var1]
004016FA   push        offset ___vba@0019F4F8 (0040135c)
004016FF   push        edx
00401700   mov         dword ptr [unnamed_var1],edi
00401703   mov         dword ptr [unnamed_var1],edi
00401706   call        dword ptr [__imp_@__vba@0019F55C (0040101c)]
0040170C   lea         edx,[unnamed_var1]
0040170F   lea         ecx,[foo]
00401712   call        esi
contro:
16:       foo = dt2 - dt1
00401741   fld         qword ptr [dt2]
00401744   fsub        qword ptr [dt1]
00401747   lea         edx,[unnamed_var1]
0040174A   lea         ecx,[foo]
0040174D   mov         dword ptr [unnamed_var1],5
00401754   fstp        qword ptr [ebp-48h]
00401757   fnstsw      ax
00401759   test        al,0Dh
0040175B   jne         $L56+11h (0040178c)
0040175D   call        esi
0040175F   wait
00401760   push        offset $L56 (0040177b)
00401765   jmp         $L27 (00401771)
Nota curiosa: cosa c'entrera' mai quell' istruzione wait a 0040175F ?
Se invece misuriamo il tempo impiegato su in un ciclo di 10000 iterazioni:
Public Sub Main()
    Dim dt1 As Date
    Dim dt2 As Date
    Dim foo As Variant
    Dim t As Long
    Dim inizio As Single
    Dim tempoDateFunc As Single
    Dim tempoOp As Single
 
    dt1 = Date
    dt2 = DateSerial(2001, 10, 3)
 
    inizio = Timer
    For t = 0 To 9999
        foo = DateAdd("d", 10, dt1)
        foo = DateDiff("d", dt2, dt1)
    Next t
    tempoDateFunc = Timer - inizio
 
    inizio = Timer
    For t = 0 To 9999
        foo = dt1 + 10
        foo = dt2 - dt1
    Next t
    tempoOp = Timer - inizio
 
    MsgBox "tempoDateFunc = " & tempoDateFunc & vbLf & _
            "tempoOp = " & tempoOp
 
End Sub
Il messaggio che ho ottenuto su un portatile con un pIII 800 ha dato:
tempoDateAdd = 0,063125
tempoDateDiff = 0,070625
tempoOpPlus = 0,0003125        -- 202 volte piu' veloce
tempoOpMinus = 0,0003125     -- 226 volte piu' veloce
cioe' il ricorso agli operatori e' maledettamente piu' performante!
Imho se l' obiettivo e' la flessibilita' del codice continuerei ad usare le chiamate 
a DateAdd e DateDiff, ma se serve la velocita' penso che le prove fatte
parlino chiaro.
-- 
ciao, hal.asm

tempoOpMinus = 0,0003125     -- 226 volte piu' veloce
[snip]
-- 
ciao hal.obfuscate()

 Il vero codice che ha misurato i tempi e' stato questo:
[...]
Ho qualche dubbio...
Prendiamo ad esempio il caso della DateAdd (l'altro è analogo).
Innanzitutto provando più volte il test si evidenziano subito fenomeni di
caching sui dati che portano a fluttuazioni sui risultati su tempoOpPlus ma
NON su tempoDateAdd (io ho aumentato il ciclo a 100'000, ma credo sia lo
stesso):
tempoOpPlus = 0.05 (prima esecuzione)
tempoOpPlus = 0.02 (seconda esecuzione)
tempoOpPlus = 0.011 (terza) e così via...
---
tempoDateAdd = 0.61 - 0.62 per ogni esecuzione
Dunque il dubbio è lecito: siamo sicuri che il caching non venga applicato
anche all'interno dei cicli, invalidando i risultati?
Prova ad esempio a sostituire alla costante 10 un valore random: i dati si
stabilizzano (sul mio PC ho tempoOpPlus = 0.07 circa, costante ad ogni
esecuzione e tempoDateAdd = 0.70 circa, costante. Allora sì che si può
azzardare un paragone.
Diciamo dunque che il rapporto fra i due valori è 1/10, non 1/200.
Secondo dubbio:
E' ovvio che per sommare x giorni ad una data è assurdo usare DateAdd.
Paragonarlo all'operatore "+" per sommare giorni è un po' come paragonare
una TextBox ad una griglia dati per scrivere una singola parola.
Il paragone va condotto invece sulla base delle complete funzionalità
offerte da DateAdd. Mi spiego meglio:
DateAdd ("m", 2, Date) = 24/1/02 (corretto)
Date + 60 = 25/1/02 (errato)
Per sommare correttamente due mesi nel secondo caso non possiamo ovviamente
prescindere dal controllo del mese in cui siamo, pena un bell'errore.
Allora un test comparato sulle prestazioni di DateAdd e "+" potrebbe essere
(ma ovviamente si potrebbe trovare codice più performante...):
[...] (uguale al codice di hal)
For t = 0 To 99999
    foo = DateAdd("m", 1, dt1)
Next t
[...]
For t = 0 to 99999
    Select Case Month(dt1)
    Case 1, 3, 5, 7, 8, 10, 12
        foo = dt1 + 31
    Case 2
        foo = dt1 + 28
    Case 4, 6, 9, 11
        foo = dt1 + 30
    End Select
Next t
Risultati (sul mio PC):
tempoDateAdd = 0.66 - tempoOpPlus = 0.40 (circa per entrambi)
Usando valori costanti, andrebbe verificato anche qui il caching, ma non ho
voglia ;-) (e a prima vista sembra tutto ok)
Il rapporto è sceso ad un valore accettabile, in funzione anche del codice
in più che va scritto. Non dimentichiamo infatti che andrebbe aggiunta
ancora la parte relativa al controllo dell'anno bisestile!
Ciao,
Carlo.

 prescindere dal controllo del mese in cui siamo, pena un bell'errore.
Come te, preferisco la DateSerial().
Forse è anche più affidabile (è solo un'impressione) e a intuito sempre più
performante di DateAdd()/Diff().
Ciao

 performante di DateAdd()/Diff().
Triconcordo.
Test performances? Detto... fatto:
DateAdd:    0.66
Op.Plus:      0.40
DateSerial:  0.55
Perň se l'intervallo da sommare č composito (x mesi e y giorni o simile) non
c'č storia!
Ciao,
Carlo.