Date e ore: conversione tra tipi dati diversi

Perché usare il tipo dati Data e Ora

Il tipo dati Date è stato introdotto nella V2R3 (ovvero all’incirca nel 1994), ma ancora oggi purtroppo sembra un tipo dati sconosciuto e “pericoloso” da usare.

Un po’ per tradizione e un po’ per pigrizia le date nelle tabelle del DB2 non sono altro che numeri scritti nei formati più svariati gestiti interamente dalla logica applicativa nei programmi.
Ma essendo per l’appunto nient’altro che campi numerici il DB2 ignora del tutto che il contenuto del campo sia una data. Il campo è un surrogato di una data. Quindi tutta la fatica di gestire correttamente le date è demandata alle capacità del programmatore per nulla sfruttando le potenzialità del DB2.

Conversione da data a altri tipi dati

Esistono numerosi metodi diversi per operare con le date: codici operativi e funzioni RPG, funzioni SQL, API, comandi di sistemi operativo: almeno 98 metodi!

Il requisito per poter usare questo tesoro di codici operativi e funzioni è che le variabili o i campi siano di tipo Data e Ora e non dei surrogati.

E’ importante quindi saper convertire una variabile o campo dal tipo dati alfanumerico o numerico al tipo dati Data e Ora e viceversa.

Da tipo data a tipo alfanumerico

La funzione %char converte una data, ora o timestamp in un campo alfanumerico
%char(date|time|timestamp {: format})
Se il primo parametro è una costante, la conversione viene eseguita a tempo di compilazione.
Il secondo parametro rappresenta il formato della data, ora o timestamp restituito. Nel risultato sono inclusi i separatori a meno che si aggiunga 0 al formato (p.es. *iso0).

Per esempio:

 dcl-s DataAlfa char(10);
 dcl-s MiaData date inz('2019-08-29');

 DataAlfa = %char(MiaData: *ISO0); // 20190829 

*iso0 indica che il formato sarà YYYYMMDD senza separatori. Invece *iso prevederebbe l’uso dei separatori.

ATTENZIONE: nel manuale ILE RPG reference nel capitolo della funzione %char è scritto un esempio per convertire un campo timestamp in alfanumerico senza separatori utilizzando nel secondo parametro la keyword *iso&; in realtà bisogna utilizzare la keyword *iso0.

Da tipo data a tipo numerico

La funzione %dec converte una data in campo numerico.

%dec(date time or timestamp expression {:format})

Il parametro format è disponibile da V5R3.

Per esempio:

dcl-s yyyymmdd packed(8);
dcl-s ddmmyy packed(6);
dcl-s MiaData date(*USA) inz(d'2019-10-20');

yyyymmdd = %dec(MiaData : *iso);
ddmmyy = %dec(MiaData : *dmy); 

Si può convertire una data in campo numerico anche utilizzando le funzioni %char e %uns. Per esempio:

 yyyymmdd = %uns(%char(MiaData : *iso0)); 

Può tornare comodo anche la funzione SQL varchar_format. P.es.:

  -- da data a campo numerico di 8 cifre
dec(varchar_format(current date), 'YYYYMMDD'), 8)
-- da data a campo numerico di 7 cifre
dec(varchar_format(current date), 'YYYYMMDD'), 8) - 19000000 

Anche la funzione SQL dec consente di effettuare la conversione da data/ora a campo numerico. P.es.:

dec(MyDate)
dec(MyTime)
dec(MyTimestamp)
dec(MyTimestamp, 26, 12)
dec(date(MyTimestamp))
dec(time(MyTimestamp), 6, 0) 

Conversione da altro tipo a data/ora/timestamp

Le funzioni %date, %time, %timestamp convertono una variabile in data, ora o timestamp. Introdotte nella V5R1 sostituiscono i codici operativi MOVE, MOVEL .

%DATE

 %date{(expression{:date-format})} 

Se non viene specificato il primo parametro, viene restituita la data corrente di sistema (N.B. NON la data del job come restituirebbe *DATE).
Il secondo parametro specifica il formato della data in input; se non specificato si assume sia *iso. Se il primo parametro è un timestamp, oppure *DATE o UDATE il secondo parametro non è necessario.
La data restituita è sempre in formato *iso.

%TIME

 %time{(expression{:time-format})} 

Se non viene specificato il primo parametro, viene restituita l’ora corrente di sistema.
Il secondo parametro specifica il formato dell’ora in input; se non specificato si assume sia *iso. Se il primo parametro è un timestamp, il secondo parametro non è necessario.
L’ora restituita è sempre in formato *iso.

%TIMESTAMP

%timestamp{(char-num-expression { : *ISO|*ISO0 : {fractional-seconds}})}
%timestamp{(date-timestamp-expression { : fractional-seconds})} 

Se non viene specificato il primo parametro o viene impostato a *sys, viene restituito il timestamp corrente di sistema (con solo le prime 3 cifre della porzione frazionale dei secondi).
Se il primo parametro è alfanumerico, si può specificare il formato nel secondo parametro.
Se il primo parametro è numerico l’unico formato ammesso è *iso.
Il timestamp restituito è sempre in formato *iso.

SQL way

Le funzioni precedenti sono ottime in un programma RPG, ma se si avesse bisogno di effettuare la conversione in un’istruzione SQL?
Nulla vieta di creare una funzione scalare SQL che con linguaggio SQL/PL o chiamando un programma RPG effettui la conversione.
Vediamo un esempio di conversione dal formato AAAAMMGG (campo packed di 8 cifre) a un tipo dati data. L’esempio poi può essere poi adattato ad effettuare la conversione con formati diversi o migliorato per gestire con un’unica funzione tutti i possibili casi.
La funzione che creeremo si basa sulla funzione SQL date che restituisce un campo di tipo data da un valore (numerico o alfanumerico) che rappresenta una data o un timestamp.

Istruzione SQL per creare la funzione cvtDate:

create or replace function CVTDATE (InpData dec(8, 0) )
 returns date
 language sql
 contains sql
 no external action
 global deterministic
 return null on null input
 not fenced
 return date(left(char(InpData), 4) concat '-' concat substr(char(InpData), 5, 2) concat '-' concat substr(char(InpData), 7, 2)); 

Altre funzioni SQL utili per effettuare la conversione del tipo data/ora/timestamp: time, timestamp, timestamp_iso, timestamp_format.

La funzione time restituisce un tipo dati time di un valore che rappresenta un’ora o un timestamp. Se il valore è una data time restituisce come ora la mezzanotte.

La funzione timestamp restituisce un tipo dati timestamp di un valore che rappresenta una data o un timestamp. Si può specificare solo il primo parametro come rappresentazione di un valore data o timestamp ed eventualmente nel secondo parametro si specifica un intero per indicare la precisione della porzione frazionale del timestamp. Altrimenti si può specificare nel primo parametro un valore che rappresenta una data e nel secondo parametro un valore che rappresenta un’ora.

Simile alla precedente è la funzione timestamp_iso che però può ricevere solo un parametro che rappresenta una data, un’ora o un timestamp. Se si specifica un valore che rappresenta un’ora, la porzione data viene impostata in base al registro CURRENT DATE.

La funzione timestamp_format (o le equivalenti to_date, to_timestamp) converte una stringa string_expression con formato format_string in un timestamp:

 timestamp_format(string_expression, format_string, precision-constant) 

I separatori consentiti in format_string sono: -, ., /, ,, ', ;, :, blank.
Gli elementi del formato specificabile in format_string sono riassunti nella tab. 53 (p. 619) del manuale SQL reference 7.3. Alcuni esempi:

values timestamp_format('1999-12-31 23:59:59', 'YYYY-MM-DD HH24:MI:SS');
values timestamp_format('15/12/98 13:48', 'DD/MM/RRRR HH24:MI');
values timestamp_format('9-3-2004 8:02', 'DD/MM/RRRR HH24:MI');
values timestamp_format('190718', 'YYMMDD');
values timestamp_format('2015-09-04 13:00:01', 'YYYY-MM-DD HH24:MI:SS');
values timestamp_format('Settembre:2015:12 01:00:13', 'Month:YYYY:DD SS:MI:HH24');
values timestamp_format('Nov:15:03 15', 'Mon:RR:DD HH24');
...where timestamp_format(char(DataPacked8), 'YYYYMMDD') between current date and (current date + 30 days)
...where timestamp_format(char(DataPacked7+19000000), 'YYYYMMDD') between current date and (current date + 30 days) 

Tabella di conversione delle date

Un altro metodo di “conversione” di una data memorizzata nel DB2 in un campo con tipo dati non appropriato consiste nell’utilizzare una tabella di conversione delle date.
Si tratta di una tabella “calendario” che contiene un record per ogni giorno in uno specifico range di date.
Ogni record contiene la stessa data rappresentata in diversi modi (data giuliana, data “vera”, giorno della settimana, data YYYYMMDD, data DDMMYY, excel…).
E’ documentata nel redbook SG24-7214-01 “Getting Started with DB2 Web Query for i” (appendix B p. 542-552).

La logica di utilizzo di questa tabella di conversione è metterla in join con la tabella contenente la data “legacy” tramite il campo più appropriato (p.es. per una data legacy memorizzata in un campo packed 8,0 in formato YYMMDD si utilizza DC_YYMD_DEC) e quindi utilizzare nel resto della query il campo data “vero” DC_DATE.

Garantito al 100% che questa tecnica offre prestazioni migliori rispetto a qualsiasi formula di conversione, soprattutto se il campo è utilizzato nella clausola where di una istruzione SQL.

Leggi l’intero articolo e molti altri sul sito https://www.markonetools.it

Related Posts
DB2 for i SQL – Stringhe – POSSTR-LOCATE-LOCATE_IN_STRING (IT)

Introduzione Spesso, nelle nostre applicazioni, abbiamo la necessità di lavorare con le stringhe di testo e l'SQL del DB2 può Read more

DB2 for i & SQL – FAQ & Howto (Part. 1) (IT)

Database DB2 e SQL ... forse lo strumento più potente e completo che abbiamo sulla piattaforma IBM i: ecco una Read more

Annuncio IBM i 7.4

Arriva direttamente con l'uovo di Pasqua questo annuncio IBM per le novità della versione IBM i 7.4, versione iNext secondo Read more

Generated Always Columns – Approfondimenti (IT)

Introduzione "Generated Always Column": sono colonne, campi, di una tabella il cui contenuto è controllato direttamente dal sistema ... e Read more

View Comments

  • Ciao Marco, una domanda riguardante un campo data.
    Ti è mai capitato di dover modificare il giorno o mese o anno di un campo data ?
    Ad esempio campo data '2020-03-31' modificarlo in '2020-03-07'.
    Esiste un comando ?

    Grazie
    Mimmo

  • Si potrebbe estrarre le porzioni di data e poi ricomporli insieme dopo aver modificato la porzione che interessa.
    Per estrarre porzioni di data puoi usare il codice operativo EXTRCT o la funzione %subdt.
    P.es.
    // estrazione anno
    AnnoOrdine = %subdt(DataOrdine : *y);
    // estrazione mese
    MeseOrdine = %subdt(DataOrdine : *m);

    Per riottenere la data completa poi concateni le 3 variabili (giorno, mese e anno) e con la funzione %date ottieni un campo data "vero".

    Sul mio sito c'è un articolo in cui ho riassunto molte informazioni tecniche sulla manipolazione delle date/ore: https://www.markonetools.it/tutto-o-quasi-su-date-e-ore/#estrazione_date_ore.

    Un'altra tecnica che si può usare è sfruttare i puntatori. Ti riporto un esempio

    dcl-s MiaData date(*iso) inz(d'2021-01-12');
    dcl-ds DataDS based(Dataptr);
    Anno zoned(4);
    Sep1 char(1);
    Mese zoned(2);
    Sep2 char(1);
    Giorno zoned(2);
    end-ds;
    DataPtr = %addr(MiaData);
    // a questo punto modificando uno dei campi Anno, Mese o Giorno ottieni una modifica immediata anche del campo MiaData.
    Giorno = 31;

    Ciao.

    Marco Riva
    http://www.markonetools.it

Recent Posts

Gestione dei file video bloccati su IBM i: una soluzione efficace

Riceviamo e pubblichiamo ben volentieri questo "tip & trick" di Patrick Rizzi che presenta una tecnica che permette di intervenire…

2 mesi ago

Monitoraggio Messaggi QSYSOPR: SQL per Ottenere Messaggi e Reply

Prendo spunto da una risposta di Michael Mayer sulle mailing list di Midrange.com a chi chiedeva come monitorare i messaggi…

2 mesi ago

Perché l’ERP è la Chiave del Successo per le Imprese Moderne

Le imprese sono sempre più alla ricerca di strumenti che possano migliorare l'efficienza, la collaborazione e la gestione delle risorse.…

4 mesi ago

ACS Access Client Solution 1.1.9.5

I primi di Aprile è uscita la "Spring Version" di ACS Access Client Solution, versione 1.1.9.5 Interessanti novità soprattutto in…

8 mesi ago

Tim Rowe and Scott Forstie – Promo video for CEC 2024 – Milan

Se non vi bastava la ricca agenda delle sessioni del Common Europe Congress 2024, 3-6 Giugno Milano, ecco un altro…

8 mesi ago

Code for IBM i 2.10.0 – Debug IBM i con Visual Studio Code

Le funzioni di debug con Visual Studio Code sono disponibili da qualche tempo ma questa nuova versione 2.10.0 semplifica la…

8 mesi ago