Formating dates and times

Problem:

You want to format a date correctly for a locale.

Solution:

Formatting dates and times correctly for a locale can prove challenging, especially considering the variety of possibilities.  Thankfully, Java provides a number of date formatting methods and classes.  icu4j adds even more tools to the programmers toolbox.

 

To format a date you need to get an instance of the DateFormat class passing the locale in:

//Get a locale object.  In this case we could also use the static constant Locale.FRENCH

Locale french = new Locale("fr");

//Get a DateFormat instance using the locale.  We also specify a length.

//Possible length values are DateFormat.SHORT, DateFormat.MEDIUM, DateFormat.LONG, DateFormat.FULL

//To demonstrate we will get an example of each

int[] lengths = {DateFormat.SHORT, DateFormat.MEDIUM, DateFormat.LONG, DateFormat.FULL};

//Loop through the lengths.  Get a DateFormat instance for each length and format a date

for(int x = 0; x < lengths.length; x++){

    DateFormat df = DateFormat.getDateInstance(lengths[x], french);

    System.out.println(df.format(new Date()));

}

 

 

The output:

 

29/11/08

29 nov. 2008

29 novembre 2008

samedi 29 novembre 2008

 

Format a time amount

Problem:

You want to format a time amount like "5 days" for a locale.

Solution:

TimeUnitFormat from icu4j provides some excellent formatting options for time units.  To use you specify a TimeUnitAmount which contains an amount and a unit.  Then specify a locale on the TimeUnitFormat.

To format 1 day in French:

//Get a TimeUnit
TimeUnit unit = TimeUnit.DAY;
//Specify the unit amount
TimeUnitAmount amount = new TimeUnitAmount(1,unit);
TimeUnitAmount amount2= new TimeUnitAmount(3,unit);
//Get a unit format
TimeUnitFormat format = new TimeUnitFormat();
//Get and set a ULocale
ULocale uloc = new ULocale("fr");
format.setLocale(uloc);
//Format and output
System.out.println(format.format(amount));
System.out.println(format.format(amount2));


The output:

1 jour
3 jours
 

Format a time interval

Problem:

You want to format a span of time.

Solution:

As of version 4, icu4j contains a DateIntervalFormat class.  This class makes it relatively easy to format an interval value.  The format will automatically format as compactly as possible. For example: if the difference between the two dates is only a few hours and both dates occur on the same day, the year, month, and day parts of the date will be omitted.

 

In order to try this example out you will need to download a copy of icu4j from http://icu-project.org/download/4.0.html#ICU4J .

 

Import the correct classes

import java.text.FieldPosition;
import com.ibm.icu.text.DateFormat;
import com.ibm.icu.text.DateIntervalFormat;
import com.ibm.icu.util.Calendar;
import com.ibm.icu.util.ULocale;

Then simply get an instance of the DateIntervalFormat class and format:

//Get two calendar instances
Calendar cal1 = Calendar.getInstance();
Calendar cal2 = (Calendar)cal1.clone();
//increment one calendar by one week.
cal2.add(Calendar.HOUR, 4);
cal2.add(Calendar.MINUTE, 34);
//Get an instance of DateIntervalFormat.  Specify the date format skeleton to use.
//The skeleton is an example format that will be used as a base for the interval format.
//You can specify your own skeleton format, or use one of the defaults in the DateFormat class
DateIntervalFormat dtIntervalFmt = DateIntervalFormat.getInstance(DateFormat.HOUR_MINUTE, new ULocale("ja"));
//Create a StringBuffer to hold the formatted value.
StringBuffer str = new StringBuffer("");
//Create a FieldPosition to specify the position for the StringBuffer
FieldPosition pos = new FieldPosition(0);
//format the interval.  The formatted value is placed in the StringBuffer.
dtIntervalFmt.format(cal1, cal2, str, pos);
System.out.println(str);

 

 Running this we get an output of "19時20分~23時54分".

Format and cast a date to a timezone

Problem:

You want to format a date and cast it to a timezone.

Solution:

The Java DateFormat class can cast a date to a new time zone at the same time as formatting it.  This can be accomplished simply by specifying a TimeZone on the DateFormat object.

 

To format and cast a date to PST:

//Get a Locale.  In this case we are going to use Afrikans in South Africa
Locale afrikans = new Locale("af","ZA");
//Get a DateFormat instance.
//We specify length for date and time and a locale to format for.
DateFormat df = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL, afrikans);
//Get a TimeZone instance for a specified id.
//The ids are Olsen TimeZone ids
TimeZone tz = TimeZone.getTimeZone("America/Los_Angeles");
//Specify the time zone
df.setTimeZone(tz);
//output the format
System.out.println(df.format(new Date()));

 

Output Example:

viernes 5 de diciembre de 2007 06:49:46 PM PST

Get all time zone ids

Problem:

You want to know what time zones are available on your system.

Solution:

Java uses the TimeZone class to handle date/time casting to and from UTC.  A TimeZone is retrieved by specifying a string id.  Most of these ids are Olsen time zone ids.  You can retrieve an array of time zone ids by calling the static getAvailableIDs() method.

 

To get all available IDs:

//Get all Timezone ids
String[] ids = TimeZone.getAvailableIDs();
//Loop through ids and output
for(int x = 0; x < ids.length; x++){
    System.out.println(ids[x]);
}

 

The output is very long, so I will omit it here.

Get an array of day names

 Problem:

You want to retrieve all of the day of the week names for a locale.

Solution:

The DateFormatSymbols class contains a wealth of information useful to the localization programmer.  You can easily retrieve day names, month names, era names, etc...

 

To get the days of the week in Korean and English:

//Get a DateFormatSymbols object using a locale as an argument

DateFormatSymbols dfsKorean = new DateFormatSymbols(Locale.KOREAN);

DateFormatSymbols dfsEnglish = new DateFormatSymbols(Locale.ENGLISH);

//Get an array of weekday names

String[] kWeekdays = dfsKorean.getWeekdays();

String[] eWeekdays = dfsEnglish.getWeekdays();

//Loop through the array and output the names

for(int x = 1; x < 8; x++){

    System.out.println(eWeekdays[x] + " = " + kWeekdays[x]);

}

 

 


The output:

Sunday = 일요일

Monday = 월요일

Tuesday = 화요일

Wednesday = 수요일

Thursday = 목요일

Friday = 금요일

Saturday = 토요일

 

Get an array of timezone ids for offset

Problem:

You know what your offset from UTC is and you want to know what timezones are available for that offset

Solution:

The Java TimeZone class uses string ids to retrieve an instance.  You can get a list of all TimeZone ids, but the list is quite large.  Luckily you can narrow the results by specifying an offset from UTC and retrieve only the results with that raw offset.

 

The offset is specified as milliseconds from UTC.  Go figure.

 

To get all TimeZone ids for an offset of -7:00 from UTC:

//Get all Timezone ids for an offset.
//For some reason the offset is in milliseconds. 
//To my knowledge there is no offset increment less than half an hour
//Here we get all ids for an offset of -7 hours from UTC
String[] ids = TimeZone.getAvailableIDs(-1000*60*60*7);
//Loop through ids and output
for(int x = 0; x < ids.length; x++){
    System.out.println(ids[x]);
}


 

Output:

America/Boise
America/Cambridge_Bay
America/Chihuahua
America/Dawson_Creek
America/Denver
America/Edmonton
America/Hermosillo
America/Inuvik
America/Mazatlan
America/Phoenix
America/Shiprock
America/Yellowknife
Canada/Mountain
Etc/GMT+7
MST
MST7MDT
Mexico/BajaSur
Navajo
PNT
SystemV/MST7
SystemV/MST7MDT
US/Arizona
US/Mountain

Get the best date format pattern

 Problem:

You know what parts of a date you want to display but you don't know the exact pattern to use.

Solution:

Often a Java programmer will know what type of pattern he wants, but not exactly what the pattern would be.  With icu4j's DateTimePatternGenerator you can specify a pattern skeleton, and it will return the most appropriate localized match.

 

To get the best pattern for the skeleton, "MMMddYYYYH" in Japanese:

//Get a Locale object

ULocale locale = new ULocale("ja_JP");

//Get an instance of the DatePatternGenerator for the locale

DateTimePatternGenerator dp = DateTimePatternGenerator.getInstance(locale);

//Get the best pattern match

String best = dp.getBestPattern("MMMddYYYYHH");

//output the best pattern match

System.out.println(best);

//Get a DateFormat for the pattern

DateFormat df = new SimpleDateFormat(best);

//output a formatted date

System.out.println(df.format(new Date()));

 


The output:

 

YYYY年MM月dd日 HH

2008年12月06日 15

 

As you can see the closest match uses a 4 digit year followed by a year symbol, a 2 digit month followed by a month symbol, a 2 digit day followed by a day symbol, and a 0~24 hour.

 

 

Get the display name for a Timezone

Problem:

You want to get a localized display name for a time zone.

Solution:

You can get a display name for a TimeZone in Java very easily by specifying a locale in the getDisplayName method.

 

To get a display name for Canada/Mountain in Japanese:

//Get a locale for japanese in Japan
Locale jp = new Locale("ja","JP");
//Get a TimeZone for the id Canada/Mountain
TimeZone tz = TimeZone.getTimeZone("Canada/Mountain");
//output the display name
System.out.println(tz.getDisplayName(jp));

 


The output:

山地標準時

Parse a formatted date string

Problem:

You want to parse a localized date string to a Java Date.

Solution:

As important as date formatting is, you have to be able to parse the formatted dates.  Parsing a date is very similar to formatting the date.  You just call the parse method on a DateFormat instance.

 

One important point is you need to specify the length of the DateFormat.  This can be difficult to know in advance sometimes.  You can wrap the parse attempt in a try catch to trap the ParseException and change the length of the DateFormat until you return a success.

 

To parse a date:

//Get a Locale
Locale chinese = new Locale("zh","CN");
//Get an instance of DateFormat.
DateFormat df = DateFormat.getDateInstance(DateFormat.FULL, chinese);
System.out.println(df.format(new Date()));
//Parse the date
//We need to handle the ParseException the parse method can throw
//The length specified in the DateFormat.getDateInstance method has to be the
//same as the string you are trying to parse, or you will get a ParseException in many cases.
try {
    //parse(String str) returns a Date
    Date date = df.parse("2008年11月30日 星期日");
    System.out.println(date.toString());
} catch (ParseException e) {
    e.printStackTrace();
}


 

The output:

2008年11月30日 星期日 Sun Nov 30 00:00:00 EST 2008