Il contenuto di questo articolo è focalizzato su una questione che può generare incongruenze quando si lavora con le date in Google Apps Script ovvero, la loro formattazione. Il motivo per il quale ho deciso di parlarne in questo blog, più centrato su temi che interessano la Digital Analytics, è proprio perché la manipolazione delle date è un aspetto con il quale è quasi inevitabile imbattersi quando si tenta di accedere ai dati di Google Analytics tramite le Analytics API da un Google Sheets con Google Apps Script.
Lo scenario
Una situazione tipica si ha quando, da un Foglio di Google, si recupera una data presente all’interno di una cella (Fig. 1) tramite una funzione di Apps Script, per utilizzarla al fine di interrogare un report di Google Analytics o effettuare una query in BigQuery.
Considerando che la data così recuperata dallo script viene convertita in un formato non utilizzabile per la maggior parte delle operazioni di uso comune (nel caso specifico Sun Sep 01 00:00:00 GMT+02:00 2019, Fig. 2), è richiesta la conversione in un formato dedicato, ovvero con una struttura di tipo Anno-Mese-Giorno: YYYY-MM-DD.
Da notare che la data all’interno del Foglio di Google non contiene l’ora, in quanto non necessariamente utile per gli scopi in oggetto. Per questo motivo il suo valore convertito all’interno del Log in Apps Script viene considerato come 00:00:00. Questo dettaglio è il fulcro della questione.
Per convertire una data nel formato di interesse, con Google Apps Script, è possibile utilizzare il metodo formatDate della Classe Utilities. Tale metodo accetta come parametri la data da convertire, il timeZone e il formato di conversione. Il timeZone indica il fuso orario con il quale sarà gestito il risultato.
Alcuni esempi sono i seguenti:
- Utilities.formatDate(date, ‘UTC‘, ‘yyyy-MM-dd’);
- Utilities.formatDate(date, ‘GMT+1‘, ‘yyyy-MM-dd’);
- Utilities.formatDate(date, ss.getSpreadsheetTimeZone(), ‘yyyy-MM-dd’);
È molto importante prestare attenzione al fuso orario utilizzato nella funzione in quanto il risultato della conversione del formato della data può essere imprevisto e inaspettato.
Quello mostrato in Fig. 3 è quanto ottenuto utilizzando in un caso la stringa UTF come valore del timeZone, nell’altro il metodo getSpreadsheetTimeZone() associato all’istanza del Foglio di Google dal quale viene recuperata la data dalla cella.
Nel primo caso la data convertita non corrisponde a quella di origine!
Diversi valori in timeZone danno diverse risposte
L’utilizzo dei valori proposti nell’esempio, per la conversione della data nel formato desiderato, può tradursi in un risultato non previsto. Questo significa che, se nella cella dello Spreadsheet recupero il valore della data 2019/09/01 per utilizzarlo in una funzione dal file di script (ad esempio per interrogare Google Analytics), il rischio dopo la sua conversione è quello di andare ad interrogare nella piattaforma di analisi il giorno sbagliato, nel caso dell’esempio 2019/08/31, che è appunto il giorno precedente a quello selezionato.
Vediamo il motivo di questa incongruenza.
timeZone: UTC, GTM o qualsiasi stringa
Normalmente la rappresentazione delle date è relativa all’ora di Greenwich (Greenwich Mean Time o GMT) conosciuta anche come Universal Time Coordinate (UTC). Utilizzando il valore UTC per definire il timeZone nel metodo di formattazione della data, viene considerata l’ora rispetto a dove si trova il server di Google Apps Script nel quale risiede lo script. Dall’Italia, trovandosi in un fuso orario diverso, sottrarrà alla data una o due ore (in base all’ora legale) scalando al giorno precedente e formattando quest’ultima data.
Lo stesso si ottiene se come valore di timeZone utilizziamo una stringa fittizia come ‘UTF‘ oppure ‘pippo‘. Questo è importante saperlo perché, anziché generare un errore, il metodo ignorerà tale valore considerando quello di default.
Forzando manualmente il valore di timeZone, ovvero inserendo GMT+1, noteremo che la data verrà convertita correttamente, ma questo solo fino al 31 marzo dopodiché, a causa dell’ora legale, necessiterebbe di essere modificato in GMT+2.
Questo inconveniente può essere evitato con un metodo dedicato dello Spreadsheet.
timeZone: getSpreadsheetTimeZone()
Applicando il metodo in questione all’istanza del Foglio di Google interrogato per recuperare la data, verrà recuperato il timeZone effettivo dello Spreadsheet.
var ss = SpreadsheetApp.openById('
SPREADSHEET_ID
');
Utilities.formatDate(date
, ss.
getSpreadsheetTimeZone()
, 'yyyy-MM-dd');
La formattazione della data elaborerà e restituirà la data corretta.
Soluzione
Quando si lavora con le date in Google Sheets, e relative elaborazioni con Google Apps Script, è consigliato definire sempre lo stesso timeZone dello strumento dal quale si recuperano i valori, appunto lo Spreadsheet. Per farlo sarà sufficiente applicare il metodo getSpreadsheetTimeZone() all’istanza del Foglio di Google all’interno del file di script.
Non mi funziona, non trovo nel mio progetto la funzione ss.getSpreadsheetTimeZone()
Ciao pierluigi,
getSpreadsheetTimeZone() è un metodo della classe Spreadsheet, per cui l’unica cosa che mi viene in mente è che il riferimento allo Spreadsheet che hai definito non si chiami ‘ss’.
Prova a verificare se è quello il problema, in caso contrario riporta qui l’errore completo che ricevi e la parte di codice incriminata.
Fammi sapere!
di programmazione sono a zero io sto solo cercando di automatizzare il lavoro di un collega. Nel listato c’è questa definizione
”
function scadenze() {
// RECUPERO I DATI DAL FOGLIO SUBBAPALTI
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(‘sommario subappalti’);”
di più non so
Ciao pierluigi,
il metodo in questione si applica al riferimento dello Spreadsheet, non a un suo foglio. Nel tuo caso la variabile ‘ss’ contiene il riferimento al foglio ‘sommario subappalti’.
Ipoteticamente il codice per fare in modo che il metodo possa funzionare con la variabile che hai chiamato ‘ss’ dovrebbe essere il seguente:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var foglio_subappalti = ss.getSheetByName(‘sommario subappalti’);
Ovviamente tutti i riferimenti che si chiamano ‘ss’ nel tuo caso dovranno essere modificati in ‘foglio_subappalti’ e a quel punto, laddove hai una data che vuoi convertire con il fuso orario dello Spreadsheet, potrà essere utilizzata l’istruzione:
Utilities.formatDate(date, ss.getSpreadsheetTimeZone(), ‘yyyy-MM-dd’);
Dove ‘date’ è la variabile che contiene la data da convertire.
Ti consiglio tuttavia, se sei a zero in programmazione, di non partire direttamente con questo concetti che richiedono oltre al JavaScript anche la conoscenza delle logiche di Apps Script.
Può esserti utile dare un’occhiata al mio video corso Gratuito sul JavaScript Semplificato per il mondo Google: https://www.youtube.com/watch?v=qI6_yBW_7jg sicuramente ti farà acquisire dimestichezza con i concetti minimi che ti garantiranno in poco tempo di ottenere il massimo risultato.
Dopodiché nel mio blog https://www.appsscript.it potrai trovare spunti interessanti per l’applicazione del linguaggio agli strumenti Google, come appunto i Fogli.
grazie per la celerità con cui mi rispondi, io ci metto un po di più, ho provato le tue indicazioni ma senza successo o meglio,
queste due righe
var ss = SpreadsheetApp.getActiveSpreadsheet();
var foglio_subappalti = ss.getSheetByName(‘sommario subappalti’);
funzionano tranqulliamente
ma questa non va proprio
Utilities.formatDate(date, ss.getSpreadsheetTimeZone(), ‘yyyy-MM-dd’);
vorrei fare una prova con un foglio completamente nuovo, un po come per gli esercizi che sto seguendo nei tuoi corsi e scrivere da capo il cambio fuso orario non è che potresti inviarmi l’esercizio grazie
Ciao pierluigi,
quale errore ti viene restituito? Senza questa indicazione non è possibile capire il perché quel comando non funzioni.
Con le informazioni a disposizione posso ipotizzare che non esista la variabile date, se così fosse dovresti provare una cosa del genere:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var foglio_subappalti = ss.getSheetByName(‘sommario subappalti’);
var date = new Date();
console.log(date);
var data_converted = Utilities.formatDate(date, ss.getSpreadsheetTimeZone(), “yyyy-MM-dd”);
console.log(data_converted);
In questo modo puoi vedere il valore della data così come ti viene restituito da new Date() e il relativo valore convertito nel formato desiderato (e con il fuso orario dello Spreadsheet).
questo è lo scipt, non è mio lo ha fatto un collega io sto solo cercando di rimuovere un errore del cambio di data con l’ora legale
function scadenze() {
// RECUPERO I DATI DAL FOGLIO SUBBAPALTI
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(‘sommario subappalti’);
var lastRow = ss.getLastRow();
var datarray = ss.getRange(“G2:H”+(lastRow) ).getValues();
var corpo_mail = ss.getRange(“B2:B”+(lastRow) ).getValues();
// RECUPERO LE MAIL DAL FOGLIO IMPOSTAZIONI
var impostazioni = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(‘impostazioni’);
var lastRowimpostazioni = impostazioni.getLastRow();
var indirizzi = impostazioni.getRange(“B4:B”+(lastRowimpostazioni) ).getValues();
var commessa = impostazioni.getRange(“C4”).getValues();
// CREO IL GRUPPO DELLE MAIL
var mail = indirizzi[0][0]
for (i=1; i <indirizzi.length; i++) {
mail = mail + ","+ indirizzi[i][0]
}
var oggi = new Date();
for (i=0; i = 30 && differenza <= 34):
var numGiorni = differenza – 34 + 4
var data_scadenza = Utilities.formatDate(datarray[i][0], "GMT+2", "dd-MM-yyyy"); originale che devo cambiare al cambio dell'ora, se metto al posto di "GMT+2" la tua istruzione ss.getSpreadsheetTimeZone(), non mi ricava la data. non mi da nessun errore, prprio non considera l'istruzione
GmailApp.sendEmail( mail , "Avviso Scadenza per la commessa" +" " + commessa , "Il contratto del sub appaltatore" +" "+
corpo_mail[i][0] +" " + "scade il " +" "+ data_scadenza +" " + "Mancano" +" "+ differenza +" "+
"giorni.Questa mail verra ripetuta per i prossimi" +" " + numGiorni +" "+ "giorni SI PREGA DI RISPONDERE AL MITTENTE PER INTERROPERE AVVISO" );
break;
Ciao Michele,
Articolo davvero interessante! Ho notato però che nei settings di Google Sheet il fuso orario di Roma risulta UTC + 1 attualmente, mentre dovrebbe essere UTC + 2 in questo preciso istante. Quindi anche tramite la funzione presentata il valore ottenuto sarebbe incorretto?
Ciao Nic, essendo noto il periodo in cui è presente l’ora solare/legale (senza contare che in futuro il suo utilizzo può essere a discrezione del paese) una valida soluzione può essere quella gestire lato codice il +1 da associare al timezone.