1

When I search online about "how to convert a Calendar to a String", all the results I find suggest to first convert to a Date and then convert the Date to a String.

The problem is that a Date is only a representation of the number of milliseconds since the epoch - it does not respect timezone. Calendar is more advanced in this way.

Of course, I could call the individual Calendar.get methods to create my own formatted string, but surely there must be an easier way?

To illustrate, I wrote this code:

long currentTime = Calendar.getInstance().getTimeInMillis();
Calendar calendar = new GregorianCalendar();
calendar.setTimeZone(TimeZone.getTimeZone("Europe/Madrid"));
calendar.setTimeInMillis(currentTime);
System.out.println(calendar.getTime().toString());
System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(calendar.getTime()));
System.out.println(calendar.get(Calendar.HOUR_OF_DAY));

While running this code from a machine based in London (UTC+0) at 8:02pm, I got the following results:

Wed Nov 18 20:02:26 UTC 2015
2015-11-18 20:02:26
21

The last line shows the real hour according to the calendar's timezone (Madrid which is UTC+1). It is 9:02pm in Madrid, but obviously both the native Date.toString as well as the DateFormat.format methods ignore the timezone because the timezone information is erased when calling Calendar.getTime (similarly Calendar.getTimeInMillis).

Given this, what is the best way to get a formatted string from a Calendar which respects timezone?

Adam Burley
  • 5,551
  • 4
  • 51
  • 72

4 Answers4

3

Set the timezone on the SimpleDateFormat object and then use z ..

sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z");
sdf.setTimeZone(TimeZone.getTimeZone("Europe/Madrid"));
System.out.println(sdf.format(calendar.getTime());
Kal
  • 24,724
  • 7
  • 65
  • 65
1

See here for details on how to handle timezones in Java.

SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("Europe/Madrid"));

Calendar cal = Calendar.getInstance();
System.out.println(simpleDateFormat.format(cal.getTime()));
Community
  • 1
  • 1
Raf
  • 7,505
  • 1
  • 42
  • 59
1

java.time

While the other Answers appear to be correct, a better approach is to avoid using java.util.Date/.Calendar entirely.

Those old date-time classes have been superseded by the java.time framework built into Java 8 and later. The new classes are inspired by the highly successful Joda-Time framework, intended as its successor, similar in concept but re-architected. Defined by JSR 310. Extended by the ThreeTen-Extra project. See the Tutorial.

Instant

An Instant represents a moment on the timeline in UTC.

Instant instant = Instant.now ( ); // Current moment in UTC.

For a given Calendar object, convert to an Instant using the method toInstant added in Java 8.

Instant instant = myCalendar.toInstant();

ZonedDateTime

You can assign a time zone (ZoneId) to an Instant to get a ZonedDateTime.

ZoneId zoneId = ZoneId.of ( "Europe/Madrid" );
ZonedDateTime zdt = ZonedDateTime.ofInstant ( instant, zoneId );

String Representation of Date-Time Value

Dump to console.

System.out.println ( "instant: " + instant + " adjusted into zone: " + zoneId + " is zdt: " + zdt );

The java.time classes use ISO 8601 standard formatting by default when parsing/generating String representations of date-time values. By default the ISO 8601 style is extended by appending the name of the time zone in addition to the usual offset-from-UTC.

instant: 2015-11-18T22:23:46.764Z adjusted into zone: Europe/Madrid is zdt: 2015-11-18T23:23:46.764+01:00[Europe/Madrid]

If you want the ISO 8601 style but without the T, either call .replace( "T" , "" ) on the resulting String object or define your own formatter.

The java.time.format package can do the work of determining a localized format appropriate to a particular Locale.

Locale locale = Locale.forLanguageTag ( "es-ES" );
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime ( FormatStyle.FULL );
String output = zdt.format ( formatter.withLocale ( locale ) );

miércoles 18 de noviembre de 2015 23H38' CET

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • thanks - really interesting! However we are unfortuately constrained to use Java 7 for the moment (JBOSS EAP 6.3) – Adam Burley Nov 19 '15 at 12:00
  • @Kidburla Then use Joda-Time until you can move to Java 8 or later. The old .Date/.Calendar classes really are that bad. – Basil Bourque Nov 19 '15 at 18:21
1

You can use String.format() to avoid timezone problems

http://docs.oracle.com/javase/7/docs/api/java/util/Formatter.html

This example gives a result in the format: "yyyy-MM-dd HH:mm:ss"

Calendar c = Calendar.getInstance();
String s = String.format("%1$tY-%1$tm-%1$td:%1$tM:%1$tS", c);
System.out.println(s);

Output:

2015-11-20:44:55
korsgaard
  • 84
  • 8
  • Welcome to the site! This is an interesting approach, which gives food for thought for other similar scenarios. Thanks for suggesting it! However, I don't think it makes for code that is quite as readable as Kal's answer. – Adam Burley Nov 20 '15 at 20:13