Reputation: 2661
I am trying to get output for a date in the format (String
):
20170801 123030 America/Los_Angeles
But using this code:
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd hhmmss Z", Locale.US);
sdf.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));
System.out.println(sdf.format(new java.util.Date()));
I am getting the output as (notice the zone part):
20174904 024908 -0700
Any idea how to fix it? I need to print "America/Los_Angeles"
instead of "-0700"
.
Upvotes: 3
Views: 3319
Reputation:
As pointed by @Alexandr's answer, there's no built-in pattern in SimpleDateFormat
to print the timezone ID. But there's a way to overwrite this.
First you create a formatter with the z
pattern, that corresponds to the timezone short name. I'm not sure if the locale matters for this (I know it affects the long names, not sure about the short names, but anyway I'm keeping it).
Then I get the java.text.DateFormatSymbols
from the formatter and overwrite the strings that correspond to the short names:
// use "z" (short timezone name)
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd hhmmss z", Locale.US);
sdf.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles"));
// get the java.text.DateFormatSymbols
DateFormatSymbols symbols = sdf.getDateFormatSymbols();
// get the zones names
String[][] zones = symbols.getZoneStrings();
// overwrite zone short names
for (int i = 0; i < zones.length; i++) {
String zoneId = zones[i][0];
if ("America/Los_Angeles".equals(zoneId)) {
zones[i][2] = zoneId; // short name for standard time
zones[i][4] = zoneId; // short name for Daylight Saving Time
}
}
// update the symbols in the formatter
symbols.setZoneStrings(zones);
sdf.setDateFormatSymbols(symbols);
System.out.println(sdf.format(new java.util.Date()));
This will print:
20171005 045317 America/Los_Angeles
Note that I changed the zone name only for America/Los_Angeles
timezone. You can modify the if
to change whatever zones you want - or just remove the if
to change it for all zones.
Another detail is that you're using hh
for the hours. According to javadoc, this is the Hour in am/pm field (values from 1 to 12). Without the AM/PM designator (pattern a
), the output might be ambiguous. You can change this to HH
(Hour in day field, values from 0 to 23) or to kk
(values from 1 to 24) if you want.
The old classes (Date
, Calendar
and SimpleDateFormat
) have lots of problems and design issues, and they're being replaced by the new APIs.
If you're using Java 8, consider using the new java.time API. It's easier, less bugged and less error-prone than the old APIs.
If you're using Java 6 or 7, you can use the ThreeTen Backport, a great backport for Java 8's new date/time classes. And for Android, you'll also need the ThreeTenABP (more on how to use it here).
The code below works for both.
The only difference is the package names (in Java 8 is java.time
and in ThreeTen Backport (or Android's ThreeTenABP) is org.threeten.bp
), but the classes and methods names are the same.
Actually, this new API is so straighforward that the code will be exactly the same as the other answers posted by @Juan and @Jens. But there's a subtle difference between those.
Let's suppose I have 2 different ZonedDateTime
objects: one represents the current date/time in America/Los_Angeles
timezone, and another one represents the same current date/time in Asia/Tokyo
timezone:
// current date/time
Instant now = Instant.now();
// get the same instant in different timezones
ZonedDateTime nowLA = now.atZone(ZoneId.of("America/Los_Angeles"));
ZonedDateTime nowTokyo = now.atZone(ZoneId.of("Asia/Tokyo"));
System.out.println(nowLA); // 2017-10-05T05:04:31.253-07:00[America/Los_Angeles]
System.out.println(nowTokyo); // 2017-10-05T21:04:31.253+09:00[Asia/Tokyo]
These dates are:
2017-10-05T05:04:31.253-07:00[America/Los_Angeles]
2017-10-05T21:04:31.253+09:00[Asia/Tokyo]
Now let's see the difference. @Jens's answer sets the timezone in the formatter. This means that all dates will be converted to that specific timezone when formatting (I've just modified the code a little bit, to set the locale directly - instead of using withLocale
- but the resulting formatter is equivalent):
// set the timezone in the formatter
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyyMMdd hhmmss VV", Locale.US)
// use Los Angeles timezone
.withZone(ZoneId.of("America/Los_Angeles"));
// it converts all dates to Los Angeles timezone
System.out.println(nowLA.format(fmt)); // 20171005 050431 America/Los_Angeles
System.out.println(nowTokyo.format(fmt)); // 20171005 050431 America/Los_Angeles
As the timezone is set in the formatter, both dates are converted to this timezone (including the date and time values):
20171005 050431 America/Los_Angeles
20171005 050431 America/Los_Angeles
While in @Juan's answer, the formatter doesn't have a timezone set, which means it'll preserve the timezone used in the ZonedDateTime
objects:
// formatter without a timezone set
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyyMMdd hhmmss VV", Locale.US);
// it keeps the timezone set in the date
System.out.println(nowLA.format(fmt)); // 20171005 050431 America/Los_Angeles
System.out.println(nowTokyo.format(fmt)); // 20171005 090431 Asia/Tokyo
Now the timezone is preserved (and also the date and time values):
20171005 050431 America/Los_Angeles
20171005 090431 Asia/Tokyo
It's a subtle difference, and you must choose the approach that works best for your case. Note that the same issue about hh
versus HH
also applies here: the variable nowTokyo
holds the value equivalent to 21:04:31 (9:04:31 PM in Tokyo), but it's formatted as 090431
- without the AM/PM designator, this time is ambiguous, so IMO the pattern should use HH
(so the output would be 210431
). But it's up to you to decide.
In the javadoc you can see details about all available patterns.
Upvotes: 4
Reputation: 6801
If you can use Java 8, you can use DateTimeFormatter
and its symbol V
to display the Zone ID:
Instant now = Instant.now();
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyyMMdd hhmmss VV")
.withLocale(Locale.US)
.withZone(ZoneId.of("America/Los_Angeles"));
System.out.println(fmt.format(now));
Prints:
20171004 032552 America/Los_Angeles
Note that SimpleDateFormat
doesn't support this flag as mentioned in Alexandr's answer.
If you have to start from java.util.Date
but can use Java 8 still, you can convert it to an Instant
first:
Instant now = new java.util.Date().toInstant();
...
Upvotes: 4
Reputation: 1388
Looks like the SimpleDateFormat doesn't support what you want.
Here are possible formats:
z Time zone General time zone Pacific Standard Time; PST; GMT-08:00
Z Time zone RFC 822 time zone -0800
X Time zone ISO 8601 time zone -08; -0800; -08:00
You can just add "America/Los_Angeles" after a formatted date:
TimeZone timeZone = TimeZone.getTimeZone("America/Los_Angeles");
DateFormat sdf = new SimpleDateFormat("yyyyMMdd hhmmss", Locale.US);
sdf.setTimeZone(timeZone);
System.out.println(sdf.format(new Date()) + " " + timeZone.getID());
DateFormat sdfWithTimeZone = new SimpleDateFormat("yyyyMMdd hhmmss zzzz", Locale.US);
sdfWithTimeZone.setTimeZone(timeZone);
System.out.println(sdfWithTimeZone.format(new Date()));
Output:
20171004 031611 America/Los_Angeles
20171004 031611 Pacific Daylight Time
Upvotes: 5