Internazionalizzazione: il supporto alle culture locali Università degli Studi di Bologna Facoltà di Ingegneria • In molte situazioni, le stampe (e l'I/O in genere) devono adeguarsi alla cultura locale – utilizzando i separatori per la parte decimale e le migliaia tipici di un certa cultura – utilizzando il formato delle date e delle ore tipici di un certa cultura – utilizzando i nomi dei giorni, dei mesi, i simboli di valuta tipici di un certa cultura – … e tanti altri ancora Fondamenti di Informatica T2 Modulo 2 Corso di Laurea in Ingegneria Informatica Anno accademico 2008/2009 • Spesso le stampe devono inoltre seguire un formato in termini di numero di cifre decimali, etc. 2 Internazionalizzazione: il supporto alle culture locali Formattazione di numeri (1/3) 1) procurarsi un oggetto NumberFormat opportunamente configurato rivolgendosi alla FABBRICA (FACTORY) In Java è possibile ottenere tutto ciò attraverso : 2) utilizzare il suo metodo format per formattare il numero – tale metodo restituisce la stringa formattata • il concetto di cultura locale: java.util.Locale • il concetto di data: java.util.Date • il concetto di calendario: java.util.Calendar import java.text.NumberFormat; import java.util.Locale; (in partic. GregorianCalendar) • una serie di formattatori, tra cui: public class TestNumberFormat { public static void main(String args[]) { – java.text.DateFormat double x = 43.12345678, y = 0.7, z = 13456.78; – java.text.NumberFormat ... SEGUE MAIN ... } } 3 Formattazione di numeri (2/3) // il main 4 Formattazione di numeri (3/3) L’oggetto non viene creato tramite una new: se ne richiede la costruzione alla FABBRICA! PATTERN FACTORY Per ottenere separatori diversi occorre specificare l'uso di una diversa cultura locale (classe Locale) // segue main NumberFormat formattatore = NumberFormat.getNumberInstance(); formattatore = NumberFormat.getNumberInstance(Locale.CANADA); System.out.println( formattatore.format(z) ); // 13,456.78 formattatore.setMaximumFractionDigits(2); System.out.println( formattatore.format(x) ); // 43,12 formattatore = NumberFormat.getNumberInstance( Locale.CANADA_FRENCH); System.out.println(formattatore.format(z) ); // 13 456.78 System.out.println( formattatore.format(y) ); // 0,7 System.out.println( formattatore.format(z) ); // 3.456,8 Nota lo spazio al posto della virgola! usa il punto per le migliaia e la virgola per la parte decimale: sono i separatori della cultura predefinita (qui, Italia) 5 Università di Bologna – A.A. 2008/2009 6 Formattazione di valute Formattazione di Date (1/5) Per stampare valori di valuta nel formato di un certo Paese si usa un altro tipo di formattatore numerico, specifico per valute: anch'esso va richiesto alla FABBRICA. // segue main x = 1243.5678; Usa la cultura predefinita (qui, Italia) NumberFormat formattatoreEUR = NumberFormat.getCurrencyInstance(); System.out.println(formattatoreEUR.format(x)); // € 1.243,57 1) procurarsi un oggetto DateFormat opportunamente configurato rivolgendosi alla FABBRICA (FACTORY) 2) utilizzare il suo metodo format per formattare la data – tale metodo restituisce la stringa formattata import java.text.DateFormat; import java.util.Locale; import java.util.Date; public class TestDateFormat { Usa la cultura inglese public static void main(String args[]) { NumberFormat formattatoreGBP = = NumberFormat.getCurrencyInstance(Locale.UK); Date d = new Date(); // data odierna System.out.println(formattatoreGBP.format(x)); // £ 1,243.57 Nota il simbolo di valuta e i diversi separatori ... SEGUE MAIN ... } } 7 Formattazione di Date (2/5) Formattazione di Date (3/5) La cultura locale predefinita sulla macchina in uso // il main 8 // il main System.out.println("Cultura locale predefinita: " + Locale.getDefault()); System.out.println(formatterShort .format(d)); System.out.println(formatterMedium.format(d)); Fabbrica (Factory) System.out.println(formatterLong.format(d)); DateFormat formatterShort = DateFormat.getDateInstance(DateFormat.SHORT); DateFormat formatterMedium = // il default DateFormat.getDateInstance(DateFormat.MEDIUM); DateFormat formatterLong = DateFormat.getDateInstance(DateFormat.LONG); DateFormat formatterFull = DateFormat.getDateInstance(DateFormat.FULL); System.out.println(formatterFull.format(d)); Output: Cultura locale predefinita: it_IT 05/03/09 Short Medium 5-mar-2009 5 marzo 2009 Long giovedì 5 marzo 2009 Quattro diversi formati: short, medium (default), long, full sigla cultura locale in uso Full 9 Formattazione di Date (4/5) 10 Formattazione di Date (5/5) Una diversa cultura locale // il main // il main System.out.println("Nuova cultura locale: " + Locale.US); System.out.println(formatterShort.format(d)); System.out.println(formatterMedium.format(d)); DateFormat formatterShort = DateFormat.getDateInstance(DateFormat.SHORT, Locale.US); DateFormat formatterMedium = // il default DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.US); DateFormat formatterLong = DateFormat.getDateInstance(DateFormat.LONG, Locale.US); DateFormat formatterFull = DateFormat.getDateInstance(DateFormat.FULL, Locale.US); System.out.println(formatterLong.format(d)); System.out.println(formatterFull.format(d)); Output: 3/5/09 Short Medium Mar 5, 2009 March 5, 2009 Soliti quattro formati, ma localizzati sigla cultura locale in uso Nuova cultura locale: en_US Thursday, March 5, 2009 11 Università di Bologna – A.A. 2008/2009 Long Full 12 Data e Ora Data e Ora • La classe Date è la fotografia di un istante, quindi un punto sull’asse dei tempi • Per interpretare correttamente una data occorre utilizzare un calendario specifico – – – – • Per retrocompatibilità sono stati mantenuti metodi e costruttori originali ma sono stati dichiarati deprecati (obsoleti), quindi ne è sconsigliato l’utilizzo • Di fatto i costruttori non deprecati sono: – Date(): crea un oggetto Date che rappresenta la data corrente – Date(long millis): crea un oggetto Date che rappresenta la data ottenuta aggiungendo i millisecondi passati come parametro, all’istante corrispondente a 00:00:00 del 1 Gennaio 1970 Gregoriano Islamico Ebraico … • Prima del JDK 1.1 Date era tutto ciò che serviva per manipolare una data • I metodi disponibili (quelli non deprecati) non permettono di recuperare giorno, mese e anno singolarmente occorre utilizzare un calendario… – un solo tipo di calendario – complicate le operazioni di internazionalizzazione 13 14 Calendario Gregoriano Calendario Gregoriano • La classe GregorianCalendar consente di interpretare un oggetto Date nel calendario gregoriano • Prima del 15 Ottobre 1582 (data di istituzione), GregorianCalendar si comporta come calendario giuliano – Riconosce gli anni bisestili (isLeapYear()) • Un anno è bisestile se è divisibile per 4, con l'eccezione degli anni secolari (divisibili per 100) che sono bisestili solo se divisibili per 400 – Un anno è bisestile se divisibile per 4… e basta! – Attenzione: la data che precede il 15/10/1582 è il • boolean isLeapYear() { return ((anno % 4 == 0 && anno % 100 != 0) || anno % 400 == 0); } 4/10/1582 necessità di aggiustamenti dovuti alle imprecisioni del calendario giuliano 15 16 Date & Calendar Riassumendo • Date rappresenta un istante di tempo • GregorianCalendar è l’interprete della data ne fornisce una chiave di lettura • Per usare usare un GregorianCalendar occorre “caricarlo” con una data • …evitiamo di usare GregorianCalendar per “portare in giro” delle date… • NON è l’oggetto giusto! • Costruire una data/ora GregorianCalendar cal = new GregorianCalendar(); cal.set(GregorianCalendar.DAY_OF_MONTH, 10); cal.set(GregorianCalendar.MONTH, GregorianCalendar.OCTOBER); cal.set(GregorianCalendar.YEAR, 2010); cal.set(GregorianCalendar.HOUR_OF_DAY, 17); cal.set(GregorianCalendar.MINUTE, 41); cal.set(GregorianCalendar.SECOND, 23); • Ottenere un oggetto Data Date myDate = cal.getTime(); Differenza fra HOUR_OF_DAY e HOUR? …continua… 17 Università di Bologna – A.A. 2008/2009 18 Date & Calendar Date & Calendar Analisi di oggetti Date • Sempre tramite calendario (GregorianCalendar) Formato data • Ottenere un formattatore per data/ora DateFormat dateFormatter = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM); • Solo dopo aver caricato il calendario con una data • Con metodi get del tutto simili ai metodi set già visti • Stampare con formato GregorianCalendar cal = new GregorianCalendar(); cal.setTime(new Date()); int month = cal.get(GregorianCalendar.MONTH); switch (month) { Mese o qualsiasi altra case GregorianCalendar.NOVEMBER: caratteristica… case GregorianCalendar.DECEMBER: break; } Formato ora System.out.println(dateFormatter.format(myDate)); • Metodi “shortcut” per costruzione di date: cal.set(2015, GregorianCalendar.NOVEMBER, 30, 10, 17, 25); anno mese giorno ore min. sec. …esistono altri metodi “shortcut” con meno parametri 19 Date & Calendar 20 Esercizio: differenza fra date • Siano dati due oggetti Date che rappresentano due data/ora • Calcolare la differenza in termini di giorni, ore, minuti e secondi • Suggerimenti: • Per lavorare con le date è importante usare sempre le costanti definite in GregorianCalendar… – Trasformare tutto in millisecondi metodo Date.getTime() – Effettuare la differenza – Calcolare giorni, ore, minuti, secondi tramite divisioni e moduli successivi – Come influisce il cambio di ora solare/legale e viceversa nel calcolo? • …e mai fare supposizioni sul valore di queste • Quanto vale GregorianCalendar.JANUARY? Non certo il valore che ci si aspetta: sarebbe troppo facile! 21 GregorianCalendar: Problemi?! • I giorni, i mesi e tutti i valori costanti del GregorianCalendar sono costanti intere public static final int LA_MIA_COSTANTE = 7 • Il problema è che il codice che segue si compila in totale tranquillità: 22 Problemi – Sintesi • Problemi che possono emergere dall’utilizzo di costanti così fatte: – Nessuna type safety (v. slide precedente) – Nessuna possibilità di raggruppare in un namespace le costanti GregorianCalendar cal = new GregorianCalendar(); //cal.set(GregorianCalendar.DAY_OF_MONTH, 10); cal.set(513, 10); • In GregorianCalendar la costante SUNDAY è allo stesso livello della costante JANUARY • Eventualmente, il namespace avrebbe potuto essere incluso nel nome stesso: DAY_SUNDAY o MONTH_JANUARY Oppure //cal.set(GregorianCalendar.DAY_OF_WEEK, GregorianCalendar.Monday); cal.set(GregorianCalendar.DAY_OF_WEEK, 17); • …ma poi non funziona assolutamente generando errore a run time! – Fragilità – in generale, inserire una nuova costante o cambiare il valore delle costanti è un problema: le costanti vengono “incorporate” nel codice compile time occorre ricompilare e sperare che funzioni tutto (v. legge di Murphy)!!! 23 Università di Bologna – A.A. 2008/2009 24 Domande?! • C’è un modo per rendere le costanti strongly typed? • Certamente sì: basta costruire un opportuno ADT… AH-HA! • Allora perché non l’hanno fatto i progettisti di Java? – In assenza di supporto agli enumerativi a livello di linguaggio, le costanti intere sono indubbiamente più semplici di qualsiasi altra realizzazione – GregorianCalendar esiste da una delle prime versioni di Java… 25 Università di Bologna – A.A. 2008/2009