Soluzioni informatiche per l'impresa
Principale Novità Chi siamo Soluzioni/Servizi Supporto/Download Opportunità Contattaci

Funzioni per operare sulle date

L'autore dell'articolo è Ivan Prenosil. L'originale, in inglese, è reperibile all'indirizzo: Ivan Prenosil's Firebird/InterBase site.

Giorno della settimana

A partire da IB6 è disponibile la funzione predefinita EXTRACT(). La chiamata:
      EXTRACT(WEEKDAY FROM D)
	
restituisce 1=Lunedì, 2=Martedì… 7=Domenica.

Esempio

      SELECT
        D,
        EXTRACT( WEEKDAY FROM D)       AS AMERICAN,
        EXTRACT( WEEKDAY FROM D-1) + 1 AS ISO8601
      FROM T;


                D AMERICAN     ISO8601
      =========== ======== ===========
      29-NOV-2001        4           4
      30-NOV-2001        5           5
       1-DIC-2001        6           6
       2-DIC-2001        0           7  <<<< Domenica
       3-DIC-2001        1           1
       4-DIC-2001        2           2
       5-DIC-2001        3           3
    

Primo/ultimo giorno del mese

Il primo giorno del mese:
      D - EXTRACT(DAY FROM D) + 1;
	

L'ultimo giorno del mese:
      LDM = D   - EXTRACT(DAY FROM D) + 32;
      LDM = LDM - EXTRACT(DAY FROM LDM);
	
ovvero, in un'unica espressione:
      D - EXTRACT(DAY FROM D) + 32 - EXTRACT(DAY FROM D - EXTRACT(DAY FROM D) + 32)
	

Primo giorno del prossimo mese:
      FDNM = D    - EXTRACT(DAY FROM D) + 32;
      FDNM = FDNM - EXTRACT(DAY FROM FDNM) + 1;
	
ovvero, in un'unica espressione:
 
      D - EXTRACT(DAY FROM D) + 33 - EXTRACT(DAY FROM D - EXTRACT(DAY FROM D) + 32) 
	

Esempio

Sia D='2002-3-22', risulta:

Numero di giorni in un mese

Questa procedura calcola il numero di giorni in un mese:
      CREATE PROCEDURE MonthLength (D DATE) RETURNS (ML INTEGER) AS
      DECLARE VARIABLE TMP DATE;
      BEGIN
          TMP = D - EXTRACT(DAY FROM D) + 32;
          ML  = EXTRACT(DAY FROM (TMP - EXTRACT(DAY FROM TMP)));
      END 
    
ovvero, in un'unica espressione:
      EXTRACT(DAY FROM (D - EXTRACT(DAY FROM D) + 32 - EXTRACT(DAY FROM D - EXTRACT(DAY FROM D) + 32))) 
	

Settimana dell'anno

Lo standard ISO 8601, riguardante le notazioni utilizzate per date e tempi, specifica che il primo giorno della settimana è lunedì; afferma inoltre che la prima settimana dell'anno è quella che include il primo giovedì (in altri termini la settimana appartiene all'anno che ne possiede la frazione maggiore). La formula base è:
      (EXTRACT(YEARDAY FROM D) - EXTRACT(WEEKDAY FROM D-1) + 7) / 7
	
che restituisce un valore fra 0 e 53. Se vogliamo la conformità allo standard ISO, dobbiamo apportare alcuni cambiamenti nel caso dei valori 0 e 53.
Il risultato 0 indica una settimana appartenente "all'anno precedente". L'ultima settimana del precedente anno potrebbe essere la 52ª o la 53ª; per scoprire quale, applichiamo la formula stessa (con argomento l'ultimo giorno dell'anno precedente).
Se il risultato fosse 53, dovremmo controllare se la settimana sia proprio la 53ª o, invece, la prima del prossimo anno. La 53ª settimana è valida solo se contiene giovedì (vale a dire il 31 dicembre è giovedì o venerdì).
Si possono facilmente evitare questi aggiustamenti se calcoliamo la formula per il giovedì della settimana cui siamo interessati:
      CREATE PROCEDURE YearWeek (D DATE)
        RETURNS (WEEK_NO VARCHAR(8)) AS
      DECLARE VARIABLE W INTEGER;  /* numero della settimana */
      DECLARE VARIABLE Y INTEGER;  /* anno a cui appartiene la settimana */
      BEGIN
        D = D - EXTRACT(WEEKDAY FROM D-1) + 3;  /* sposta a giovedì */

        W = (EXTRACT(YEARDAY FROM D) - EXTRACT(WEEKDAY FROM D-1) + 7) / 7e0;
        Y = EXTRACT(YEAR FROM D);

        IF (W<10) THEN
          WEEK_NO = '0';
        ELSE
          WEEK_NO = '';
        WEEK_NO = Y || '/' || WEEK_NO || W;

        SUSPEND;
      END
    

Il codice originale:
      CREATE PROCEDURE YearWeek (D DATE)
        RETURNS (WEEK_NO VARCHAR(8)) AS
      DECLARE VARIABLE W INTEGER;  /* numero della settimana */
      DECLARE VARIABLE Y INTEGER;  /* anno a cui appartiene la settimana */
      BEGIN
        W = (EXTRACT(YEARDAY FROM D) - EXTRACT(WEEKDAY FROM D-1) + 7) / 7e0;
        Y = EXTRACT(YEAR FROM D);

        IF (W=0) THEN BEGIN
          Y = Y - 1;
          D = D - EXTRACT(YEARDAY FROM D) - 1;  /* ultimo giorno dell'anno precedente; D è qui utilizzata come variabile temporanea */
          W = (EXTRACT(YEARDAY FROM D) - EXTRACT(WEEKDAY FROM D-1) + 7) / 7e0;
          END
        ELSE
        IF (W=53 AND 4>EXTRACT(WEEKDAY FROM (D - EXTRACT(DAY FROM D) + 31))) THEN BEGIN
          Y = Y + 1;
          W = 1;
          END

        /* Quanto segue è sola formattazione; si potrebbero anche restituire direttamente W ed Y. */
        IF (W<10) THEN
          WEEK_NO = '0';
        ELSE
          WEEK_NO = '';
        WEEK_NO = Y || '/' || WEEK_NO || W;

        SUSPEND;
      END	
	

È un anno bisestile?

Per scoprire se una data ricade in un anno bisestile, possiamo, per esempio, controllare il 59º giorno dell'anno: se è il 29 febbraio si tratta di un anno bisestile (altrimenti sarebbe il 1 marzo di un anno normale). Ecco una procedura di esempio:
      CREATE PROCEDURE Is_LeapYear (D DATE) RETURNS (LY INTEGER) AS
      BEGIN
        IF ( 2 = EXTRACT(MONTH FROM (D - EXTRACT(YEARDAY FROM D) + 59)) ) THEN
          LY = 1;  /* anno bisestile */
        ELSE
          LY = 0;  /* anno normale */
      END 
	
Una procedura simile (con l'anno come argomento):
      CREATE PROCEDURE Is_LeapYear (Y INTEGER) RETURNS (LY INTEGER) AS
      BEGIN
        IF ( 60 = EXTRACT(YEARDAY FROM CAST(Y || '-3-1' AS TIMESTAMP)) ) THEN
          LY = 1;  /* anno bisestile */
        ELSE
          LY = 0;  /* anno normale */
      END 
	
Dato che IB5 non supporta la funzione EXTRACT, possiamo semplicemente limitarci a verificare se esiste il 29 febbraio:
      CREATE PROCEDURE Is_LeapYear (Y INTEGER) RETURNS (LY INTEGER) AS
      DECLARE VARIABLE D DATE;
      BEGIN
        LY = 1;
        D  = CAST('29-FEB-' || Y AS DATE);
        WHEN ANY DO LY = 0;
      END 
	
Un'espressione assai nota per individuare un anno bisestile è questa:
      Is_LeapYear :=
         ( ((Year MOD 4) = 0) AND ((Year MOD 100) <> 0) )
         OR ((Year DIV 400) = 0); 	
	
ma IB/FB non supportano direttamente l'operatore MOD. Questo problema può essere superato ricorrendo ad una funzione UDF o mediante l'espressione:
      x MOD y = x - (x DIV y) * y	
	
da codificarsi correttamente (cosa non semplice quanto sembra per via delle differenti regole fra Dialect-1 e Dialect-3).
Mappa del sito Collegamenti Riservatezza Note legali Accessibilità W3C
Tutti i marchi ed i copyright in questa pagina sono di proprietà dei rispettivi proprietari. Per il resto © EOS (Pisa). Ultimo aggiornamento: 2010-4-9.