SimpleDateFormat without the Timezone Offset in Java (GMT+00:00) for Custom Timezone
This Question and the Answers are now outmoded. They use old date-time classes outmoded by the java.time framework built into Java 8 and later. The old classes are poorly designed, confusing, and troublesome; Avoid them.
Avoid 3-4 Letter Zone Codes
Avoid the 3-4 letter codes such as BST
. They are neither standardized nor unique. They do not actually represent time zones. And they add even more confusion to the problem of Daylight Saving Time (DST).
Instead, use proper time zones. Most are continent/region
format such as Europe/London
.
Avoid setting default time zone
Calling java.util.TimeZone.setDefault
should be done only in the most extreme cases. This call affects all code running in all threads of all apps within the JVM immediately during runtime.
Instead, in all your date-time code, specify the desired/expected time zone. If omitted, Java falls back by implicitly relying on the JVM’s current default time zone. As noted above this default can change at any moment during runtime! Instead, specify explicitly. If you specify your desired/expected time zone as a passed argument routinely then the current default time zone is moot, irrelevant.
java.time
The java.time framework is built into Java 8 and later. See Tutorial. Defined by JSR 310. Inspired by the highly successful Joda-Time library.
Instant
An Instant
is a moment on the timeline in UTC.
The following example shows how the java.time classes can parse/generate strings by default if in standard ISO 8601 format, with no need to specify a parsing pattern. Use DateTimeFormatter
class to specify other non-standard patterns.
Instant instant = Instant.parse( "2011-12-27T09:00:00Z" );
ZonedDateTime
Apply a time zone as needed, producing a ZonedDateTime
.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = instant.atZone( zoneId );
Generating Strings
You can produce textual representations of the ZonedDateTime object using a DateTimeFormatter
. You can specify custom patterns. Or, as I recommend, let java.time localize for you.
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.MEDIUM );
Best to specify the desired/expected Locale
for the same reason as time zone… the JVM’s current default can be changed at any moment by any code in any thread of any app running within the JVM. The Locale
determines (a) the human language used for names of day & month, and (b) the cultural norms such as commas versus periods and the order of the parts such as month or day or year coming first.
formatter = formatter.withLocale( Locale.CANADA_FRENCH );
String output = zdt.format( formatter );
About java.time
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date
, Calendar
, & SimpleDateFormat
.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.*
classes.
Where to obtain the java.time classes?
- Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
- Java 9 adds some minor features and fixes.
- Java SE 6 and Java SE 7
- Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
- Android
- Later versions of Android bundle implementations of the java.time classes.
- For earlier Android (<26), the ThreeTenABP project adapts ThreeTen-Backport (mentioned above). See How to use ThreeTenABP….
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval
, YearWeek
, YearQuarter
, and more.
I think that you are using the correct pattern for your requirements, however the JDK doesn't know the name of your timezone, so it switches over to using a GMT offset value instead.
When I format a date using your pattern, I get "GMT" for the timezone part.
What does TimeZone.getDefault().getDisplayName()
give you? For me, I get "Greenwich Mean Time".
Not an elegant solution at all but it works for us. I had to create a custom implementation for DateFormat/SimpleDateFormat. This looks like something as follows:
static {
// this would be initialized like something as follows when the application starts
// which causes the headaches of SimpleDateFormat not to work...
SimpleTimeZone tz = new SimpleTimeZone(0, "Out Timezone");
TimeZone.setDefault(tz);
}
// therefore this class will workaround the issue,
public class OurOwnCustomDateFormat
extends SimpleDateFormat {
/** The pattern to use as the format string. */
protected String pattern;
public OurOwnCustomDateFormat(String pattern) {
super(pattern);
// store the pattern
this.pattern = pattern;
}
@Override
public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition pos) {
// custom implementation to format the date and time based on our TimeZone
toAppendTo.insert(pos.getBeginIndex(), "the date with our custom format calculated here");
return toAppendTo;
}