Il VBA e la gestione delle date

Abbiamo già detto che in Excel esistono due modi per poter lavorare : inserendo formule e funzioni direttamente nelle celle del foglio di lavoro, oppure attraverso l'inserimento di opportune istruzioni sfruttando il codice, il VBA, usando l'Editor di Visual Basic. Parlando di date, è necessario vedere le differenze tra i due modi di operare.

Premesso che Excel pone come inizio calendario la data del 01/01/1900, e che vede una data inserita in una cella come UN NUMERO che rappresenta la differenza DEI GIORNI tra la data immessa e l'inizio calendario, parlando di gestione date, è necessario capire che esistono due modi di interpretazione delle date in Excel:

  • Excel considera come primo giorno la data 01/01/1900

  • il VBA invece parte dal 30/12/1899

In questa sezione ci occuperemo delle date viste dal VBA, va precisato comunque che in programmazione, la conversione tra i due sistemi non impegna il programmatore: se si inserisce tramite una textbox (in una UserForm) una variabile di tipo "Date" nella cella del foglio di lavoro, la data che ivi apparirà sarà quella giusta.

Per memorizzare una data o un orario, e necessario definire una variabile di tipo Date e ricorrere per assegnare un valore alla variabile, ad una sintassi particolare che prevede di inserire la data tra due caratteri "cancelletto" (#): per esempio, il 10 agosto 2002, può essere scritto:

Private Sub CommandButton1_Click()
Dim Mydate As Date
Mydate = #08/10/2002#  
'Mese/Giorno/Anno
TextBox1 = Mydate
WorkSheets(1).Range("A1") = TextBox1
End Sub

Come si può notare il formato prevede l'inserimento della data alla maniera inglese, invertito rispetto alla maniera italiana, cioè Mese/Giorno/Anno, questo perchè, lo ripeto, le istruzioni in codice vba devono essere compilate in inglese. Ci penserà lo stesso VisualBasic a convertire immediatamente nel formato standard, per cui nella TextBox1 noi vedremmo la data scritta all'italiana : 10/08/2002 e lo stesso nella cella A1 del foglio di lavoro. (la visualizzazione del formato data con anno a 4 cifre, dipende dalle impostazioni di settaggio contenute in "Pannello di Controllo/Impostazioni Internazionali/Data, voce:"Formato data breve": se è impostato gg/MM/aa tutte le visualizzazioni delle date saranno con l'anno a due cifre (le ultime due), se impostato gg/MM/aaaa invece l'anno sarà visualizzato a quattro cifre. Questo come visualizzazione standard in tutti i programmi; le celle di un foglio di Excel consentono di scegliere il formato data voluto, indipendentemente dalle impostazioni di sistema. Gli ultimi S.O. sono normalmente settati con anno a quattro cifre).

Vorrei richiamare l'attenzione su un particolare: nell'esempio sopra, la parola "Date" è stata usata per definire il tipo di variabile (Dim Mydate è una Data), ma la parola "Date" è anche una Funzione e un'istruzione, e può essere usata per definire la data di sistema (data del giorno in cui si lavora). Vediamo l'esempio sopra modificato in questo senso:

Private Sub CommandButton1_Click()
TextBox1 = Date
WorkSheets(1).Range("A1") = TextBox1
End Sub

E nella textbox1 e in A1 vedremmo la data del giorno nel formato italiano. Questa istruzione ci consente di evitare di scrivere la data in tutte le occasioni in cui la registrazione di una data corrisponde alla data del giorno. (nel vba l'istruzione Date equivale alla Funzione =OGGI() inserita in una cella del foglio di lavoro).

Ritornando al primo esempio (usato per definire la sintassi del formato data), notiamo che l'istruzione non ci consente di gestire liberamente l'inserimento di una data, in quanto la data è fissata via codice; (questo andrebbe bene se dovessimo inserire sempre una stessa data, e avremmo potuto fare a meno della textbox1, bastava infatti scrivere : WorkSheets(1).Range("A1") = Mydate ), come fare allora per far capire che stiamo inserendo una data e al tempo stesso usare la textbox per inserire date a nostro piacere? Possiamo usare usare due metodi : il primo sarà quello di assegnare una variabile di tipo Date alla textbox interessata, il secondo di impostare la formattazione della textbox in "formato data (Format)" prima della scrittura sul foglio di lavoro. Vediamo i due metodi:

primo metodo:

Private Sub CommandButton1_Click()
Dim Mydate As Date
Mydate = TextBox1
WorkSheets(1).Range("A1") = Mydate
End Sub

Con l'istruzione sopra otterremo il riconoscimento da parte di Excel della data come tale, e verrà rispettato anche l'eventuale formato cella preimpostato: abbiamo scelto per A1 il formato cella : categoria Data, e come tipo : Data 14 marzo 2001 (cioè con la data scritta col nome del mese e l'anno a 4 cifre), avremmo questo risultato anche se nella textbox abbiamo scritto la data usando il formato classico:

Non solo, ma l'istruzione precedente passa la data (immessa nella textbox1) ad Excel COME DATA, ed Excel l'avrebbe riconosciuta come tale, anche se avessimo lasciato la cella A1 con il formato cella impostato a "Generale", che è il Formato cella di default di tutte le celle. Sarebbe solo stata scritta con il formato data tradizionale, cioè 15/10/02, MA, sottolineo, come DATA, e non come diversamente potrebbe avvenire, come TESTO; visivamente avremmo visto lo stesso in A1 scritto 15/10/02, ma a sinistra nella cella (come un testo) e cosa più importante, sarebbero sorti problemi se avessimo utilizzato il dato contenuto in A1 per conteggi su date. Definirei l'istruzione sopra, come l'istruzione TIPO da usare quando si usino textbox per trasferire DATE al foglio di lavoro. Se poi vorremo un tipo di Formato data personalizzato, lo IMPOSTEREMO non  via codice (Format), ma come Formato cella. Nel caso si avesse più di una textbox dove si immettono date e si userà un unico comando per trasferire le date sul foglio di lavoro, sarà sufficiente dimensionare la variabile una volta sola (Dim), assegnandogli più variabili (X,Y,Z), ,una per ogni textbox usata. Vediamo un esempio:

Private Sub CommandButton1_Click()
Dim X, Y, Z As Date
X = TextBox1
Y = TextBox2
Z = TextBox3
WorkSheets(1).Range("A1") = X
WorkSheets(1).Range("A2") = Y
WorkSheets(1).Range("A3") = Z
End Sub


secondo metodo:
Introduciamo esempi in cui si fa uso della Funzione "Format". E' infatti possibile definire il Formato di date personalizzato utilizzando la parola Format e, tra parentesi, il valore da formattare (textbox1), seguito dal tipo di formato dati inserito tra doppi apici. Vista la molteplicità dei formati, riporto di seguito una tabella completa e loro significato:

Carattere

Descrizione

( : )

Separatore di ora. In alcune impostazioni internazionali, possono essere utilizzati come separatori di ora altri caratteri. Il separatore di ora separa ore, minuti e secondi quando vengono formattati i valori di ora. Il carattere effettivo utilizzato come separatore di ora nell'output formattato è determinato dalle impostazioni del sistema in uso.

( / )

Separatore di data. In alcune impostazioni internazionali possono essere utilizzati come separatori di data altri caratteri. Il separatore di data separa giorno, mese e anno quando vengono formattati i valori di data. Il carattere effettivo utilizzato come separatore di data nell'output formattato è determinato dalle impostazioni del sistema in uso.

c

Visualizza la data come ddddd e l'ora come ttttt, nell'ordine. Visualizza solo informazioni relative alla data se nel numero seriale della data non è presente una parte frazionaria; visualizza solo le informazioni relative all'ora se non è presente alcuna parte intera.

d

Visualizza il giorno come numero senza zero iniziale (1 – 31).

dd

Visualizza il giorno come numero con zero iniziale (01 – 31).

ddd

Visualizza il giorno abbreviato (dom – sab).

dddd

Visualizza il giorno per esteso (domenica – sabato).

ddddd

Visualizza una data completa (comprendente giorno, mese e anno), formattata in base alle impostazioni relative al formato di data breve del sistema in uso. Il formato di data breve predefinito è d/m/yy.

dddddd

Visualizza il numero seriale di una data come data completa (comprendente giorno, mese e anno), formattata in base alle impostazioni relative alla data estesa del sistema in uso. Il formato di data estesa predefinito è dd mmmm, yyyy.

aaaa

Equivale a dddd, ma corrisponde alla versione localizzata della stringa.

w

Visualizza il giorno della settimana come numero (1 per domenica-7 per sabato).

ww

Visualizza la settimana di un anno come numero (1 – 54).

m

Visualizza il mese come numero senza zero iniziale (1 – 12). Se m segue immediatamente h o hh, viene visualizzato il valore relativo ai minuti anziché il mese.

mm

Visualizza il mese come numero con zero iniziale (01 – 12). Se m segue immediatamente h o hh, viene visualizzato il valore relativo ai minuti anziché il mese.

mmm

Visualizza il mese abbreviato (gen – dic).

mmmm

Visualizza il mese con il relativo nome per esteso (gennaio – dicembre).

q

Visualizza il trimestre dell'anno come numero (1 – 4).

y

Visualizza il giorno dell'anno come numero (1 – 366).

yy

Visualizza l'anno come numero di due cifre (00 – 99).

yyyy

Visualizza l'anno come numero di quattro cifre (100 – 9999).

h

Visualizza l'ora come numero senza zero iniziale (0 – 23).

Hh

Visualizza l'ora come numero con zero iniziale (00 – 23).

N

Visualizza i minuti come numero senza zero iniziale (0 – 59).

Nn

Visualizza i minuti come numero con zero iniziale (00 – 59).

S

Visualizza i secondi come numero senza zero iniziale (0 – 59).

Ss

Visualizza i secondi come numero con zero iniziale (00 – 59).

t t t t t

Visualizza un'ora come ora completa, ovvero vengono indicati l'ora, i minuti e i secondi, formattata con il separatore di ora definito dal formato di ora impostato nel sistema in uso. Viene visualizzato uno zero iniziale se è selezionata l'opzione Zero iniziale e l'ora è antecedente alle 10.00. Il formato di ora predefinito è h:mm:ss.

AM/PM

Utilizza il formato 12 ore e visualizza AM in maiuscolo accanto alle ore precedenti mezzogiorno e PM in maiuscolo accanto alle ore comprese fra mezzogiorno e le 23.59.

am/pm

Utilizza il formato 12 ore e visualizza am in minuscolo accanto alle ore precedenti mezzogiorno e pm in minuscolo accanto alle ore comprese fra mezzogiorno e le 23.59.

A/P

Utilizza il formato 12 ore e visualizza una A maiuscola accanto alle ore precedenti mezzogiorno e una P maiuscola accanto alle ore comprese fra mezzogiorno e le 23.59.

a/p

Utilizza il formato 12 ore e visualizza una a minuscola accanto alle ore precedenti mezzogiorno e una p minuscola accanto alle ore comprese fra mezzogiorno e le 23.59.

AMPM

Utilizza il formato 12 ore e visualizza la stringa di caratteri AM definita dal sistema in uso accanto alle ore precedenti mezzogiorno e la stringa di caratteri PM definita dal sistema in uso accanto alle ore comprese fra mezzogiorno e le 23.59. AM e PM possono essere sia in maiuscolo che in minuscolo, tuttavia le maiuscole e minuscole della stringa visualizzata vengono adeguate alla stringa definita dalle impostazioni del sistema in uso. Il formato predefinito è AM/PM.

Formati predefiniti di data e ora  (come Funzione Format)

General Date

Visualizza una data e/o un'ora. Per i numeri reali visualizza una data e un'ora (ad esempio 3/4/93 17.34); se la parte frazionaria non è presente visualizza solo una data (ad esempio 3/4/93); se la parte intera non è presente visualizza solo l'ora (ad esempio 17.34). La visualizzazione della data è determinata dalle impostazioni del sistema in uso.

Long Date

Visualizza una data in base al formato di data estesa del sistema in uso.

Medium Date

Visualizza una data utilizzando il formato di data breve adeguato della versione dell'applicazione host.(Qualsiasi applicazione che supporta l'utilizzo di Visual Basic, Applications Edition. Ad esempio, Microsoft Excel, Microsoft Project e così via.)

Short Date

Visualizza una data utilizzando il formato di data breve del sistema in uso.

Long Time

Visualizza un'ora utilizzando il formato di ora estesa del sistema in uso. Include le ore, i minuti e i secondi.

Medium Time

Visualizza l'ora nel formato breve di 12 ore, indicando le ore, i minuti e l'identificatore AM/PM.

Short Time

Visualizza l'ora utilizzando il formato di 24 ore (ad esempio 17.45).

gli esempi :

Private Sub CommandButton1_Click()
TextBox1 = Format(TextBox1, "mm/dd/yy")
WorkSheets(1).Range("A1") = TextBox1
End Sub

va bene anche :

Private Sub CommandButton1_Click()
X = Format(TextBox1, "dd/mmm/yy")
WorkSheets(1).Range("A1") = X
End Sub

Con le due istruzioni sotto, se si dichiara una variabile (X) come Date e gli si assegna un Formato, il Formato NON viene considerato e passa solo il formato Date della variabile che è quello classico 12/03/02, a meno che non si sia predisposto il Formato cella diversamente.

Private Sub CommandButton1_Click()
Dim X As Date
X = Format(TextBox1, "dd/mmm/yy")
WorkSheets(1).Range("A1") = X
End Sub

oppure

Private Sub CommandButton1_Click()
Dim X As Date
X = Format(TextBox1, "General Date")
WorkSheets(1).Range("A1") = X
End Sub

Dal momento che si comincia a fare confusione, e non credo per colpa mia, ho riassunto nella tabella sotto i risultati che si ottengo usando sia l'istruzione che ho definito TIPO (con dimensionamento come Date), sia l'istruzione Format (senza dimensionamento come Date) e proveremo a trarre delle conclusioni. Nella colonna A ci sono i risultati delle date scritte nella textbox1, sempre scritti col formato classico: 12/02/02 ecc., nella colonna B, se è stata usata la Variabile Date o la formattazione e di che tipo, nella colonna C si trova il "formato cella" impostato in A, e nella colonna D , dove è stato impostato il formato cella a Data, il tipo di formato data. Nella colonna A le date riconosciute da excel come data, sono scritte a destra nella cella, le altre scritte sulla sinistra, vengono impostate come TESTO (senza triangolino verde in alto a sinistra nella cella) e dove appare il triangolino, Excel rileva che è necessaria una precisazione: confermare un formato data, considerarlo errore, ignorare e lo considera testo. La colonna C, dove si legge "generale > data", vuol dire che il formato cella, prima dell'inserimento della data, era impostato a "generale", dopo l'inserimento viene aggiornato da excel il formato cella in "data" e quindi si desume che la formattazione via codice è stata riconosciuta giusta.

Non mi sono dilungato a proporre tutti i formati, per non diventare scemo. Chi vuole si farà tutte le prove che vorrà. Questa è una panoramica su uno degli aspetti di Excel che giudico molto confusionari, ma tant'è.

Rimane ancora una cosa da vedere : fino ad ora abbiamo visto come impostare il codice per il trasferimento di una data dalla textbox al foglio di lavoro, ma cosa dobbiamo fare se vogliamo che anche la data inserita nella textbox  assuma il formato voluto? Dovremo usare l'istruzione Format inserita in un evento della textbox. Non è possibile ottenere una formattazione durante la digitazione, e gli unici eventi utilizzabili sono l'evento AfterUpdate oppure l'evento Exit (simile a LostFocus del Vb). Ecco due esempi:

Private Sub TextBox1_AfterUpdate()
TextBox1 = Format(TextBox1, "dd-mm-yy")
End Sub

Private Sub TextBox1_Exit(ByVal Cancel As MSForms.ReturnBoolean)
TextBox1 = Format(TextBox1, "dd-mmmm-yy")
End Sub

Entrambi gli eventi si verificano quando si toglie il focus dalla textbox1: o spostandoci su un'altra textbox, oppure nell'istante in cui si preme sul commandbutton. ATTENZIONE!! Questo tipo di formattazione serve solo per la visualizzazione della data nella textbox, per cui NON bisogna usare il sistema inglese (Mese/Giorno/Anno), ma il nostro sistema (Giorno/Mese/Anno). Esiste un altro evento che potremo però utilizzare SOLO se usiamo una textbox per mostrarci delle date presenti in una cella : è l'evento Change della textbox, quindi useremo un'istruzione per caricare la textbox con il contenuto della cella A1, usando l'evento Activate della UserForm,

Private Sub UserForm_Activate()
TextBox1 = Worksheets(1).Range("A1").Value
End Sub

e nell'evento Change della textbox1 metteremo l'istruzione per visualizzare la data nel formato che avremo scelto e che può essere diverso dal formato data presente in A1:

Private Sub TextBox1_Change()
TextBox1 = Format(TextBox1, "dd-mmmm-yy")
End Sub

Volendo evitare di scrivere due istruzioni, potremo saltare l'evento Change e usare la formattazione nell'evento Activate dell'UserForm, così:

Private Sub UserForm_Activate()
TextBox1 = Worksheets(1).Range("A1").Value
TextBox1 = Format(TextBox1, "dd-mmmm-yy")
End Sub

ComboBox e le date

Ancora una precisazione: Quando si lavora usando delle ComboBox prese da "Strumenti di Controllo" (o casella degli strumenti), essendo oggetti che fanno parte delle ActiveX e quindi tipici del VisualBasic piuttosto che di Excel, è necessario provvedere alla "formattazione" dei dati che ricevono dal foglio di lavoro. Trattando di date, una ComboBox il cui ListFillRange va a "pescare" dati in una colonna di date, mostrerà nel menù a tendina, le date così come sono scritte nelle celle del foglio di lavoro, MA, nel momento in cui selezioniamo una di queste date per averla nella cella collegata (LinkedCell), le date presenti nel menù si trasformano in numeri : sono i numeri seriali con i quali excel "vede" le date, anche se sul foglio noi vediamo le date scritte con il giusto formato, e come tali le passa alla combobox. Per ovviare a questo comportamento, sono necessarie delle istruzioni che consentano il mantenimento del formato data scelto nelle celle, e, evitando di collegare la proprietà LinkedCell della combobox a nessuna cella, usare a suo posto una riga di istruzione. Nell'esempio sotto vediamo il costrutto : sfruttiamo l'evento Change della combobox in modo che ad ogni selezione di una data presente nel menù della stessa, si attivi l'esecuzione del codice: prima impostiamo la formattazione della data nel formato 5-feb-02 (d-mmm-yy)(scritto col formato italiano ma usando i termini inglesi), poi dichiariamo la variabile mydate di tipo Date, poi assegniamo la variabile alla combobox, indi gli diciamo quale cella del foglio di lavoro dovrà essere collegata (LinkedCell) al dato selezionato nella combobox. Le celle del foglio di lavoro interessate alle date dovranno avere il formato cella impostato uguale al formato scelto per formattare la combobox (personalizzato : g-mmm-aa)(sul foglio di lavoro i termini sono solo in italiano) :

Private Sub ComboBox1_Change()
ComboBox1 = Format(ComboBox1, "d-mmm-yy")
Dim mydate As Date
mydate = ComboBox1
Range("G5") = mydate 
'G5 è la cella che va a sostituire la proprietà  LinkedCell della combobox
End Sub

Per la trattazione degli orari, è previsto un nuovo paragrafo che seguirà questo. Buon appetito, gente!