Fondamenti di Informatica T2 Modulo 2

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