2017-10-04 92 views
2

我试图让输出格式的日期(String):时区的Java时间格式

20170801 123030美洲/洛杉矶

但是使用这个代码:

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())); 

我得到的输出(注意该区域的部分):

20174904 024908 -0700

不知道如何解决它?我需要打印"​America/Los_Angeles"而不是"-0700"

+0

[您必须在几个月内使用大写的'M'](https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html)。小写'm'表示_minutes_ –

+0

为什么不移除'Z'并手动连接名称? – shmosel

+2

您正在使用现在遗留的麻烦的旧日期时间类,由java.time类代替。 –

回答

4

如果你可以使用Java 8,您可以使用DateTimeFormatter及其代码V来显示区域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)); 

打印:

20171004 032552美洲/洛杉矶

注意,在Alexandr's answer提到SimpleDateFormat不支持此标志。

如果java.util.Date开始,但可以使用Java 8是,你可以将其转换为Instant第一:

Instant now = new java.util.Date().toInstant(); 
... 
+2

我怀疑那些'hh'应该是大写'HH'。由于没有上午/下午指标,我认为我们可以假设24小时制。 –

+2

确实@Patty,请注意上面提到的'hh'和'HH'问题,以及在格式化程序中设置时区与不在[@ Hugo的详细答案]中提到的时区之间的细微差别(https:/ /stackoverflow.com/a/46585345/396255)。这些差异取决于您的要求。 –

4

看起来像SimpleDateFormat不支持你想要的。

以下是可能的格式:

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 

您可以格式化的日期后,只需加上 “美国/洛杉矶”:

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())); 

输出:

20171004 031611 America/Los_Angeles 
20171004 031611 Pacific Daylight Time 
+1

您正在使用现在已经存在的麻烦的旧日期时间类,由java.time类代替。 –

4

正如指出的@Alexandr's answer,有一个在SimpleDateFormat没有内置模式打印时区ID。但是有一种方法可以覆盖这个。

首先您创建一个格式化程序,其中z模式对应于时区短名称。 我不确定这个语言环境是否重要(我知道它影响长名称,不确定短名称,但无论如何我保留它)。

然后我从格式化的java.text.DateFormatSymbols并覆盖对应于短名的字符串:

// 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())); 

这将打印:

20171005 045317美洲/洛杉矶

请注意,我仅更改了America/Los_Angeles时区的区域名称。您可以修改if以更改所需的任何区域,或者仅删除if以更改所有区域的区域。

另一个细节是,你使用hh几个小时。 According to javadoc,这是小时am/pm字段(值从1到12)。没有AM/PM指示符(模式a),输出可能不明确。如果需要,您可以将其更改为HH小时的日期字段,值从0到23)或kk(值从1到24)。


新的Java日期/时间API

老班(DateCalendarSimpleDateFormat)有lots of problemsdesign issues,他们正在被新的API取代。

如果您使用Java 8,请考虑使用new java.time API。这很容易,less bugged and less error-prone than the old APIs

如果您使用的是Java 6或7,则可以使用ThreeTen Backport,这是用于Java 8的新日期/时间类的一个很好的后端。而对于Android,您还需要ThreeTenABP(更多关于如何使用它here)。

下面的代码适用于两者。 唯一的区别是软件包名称(在Java 8中为java.time,在ThreeTen Backport(或Android的ThreeTenABP)中为org.threeten.bp),但类别和方法名称是相同的。

实际上,这个新API非常直接,代码与@Juan和@Jens发布的其他答案完全相同。但是这些差异很微妙。

让我们假设我有2个不同ZonedDateTime对象:一个代表America/Los_Angeles时区当前的日期/时间,和另一个代表相同的当前日期/时间Asia/Tokyo时区:

// 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] 

这些日期是:

2017-10-05T05:04:31.253-07:00 [America/Los_Angeles]
2017-10-05T21:04:31。253 + 09:00 [亚洲/东京]

现在让我们来看看区别。 @Jens's answer在格式化程序中设置时区。这意味着所有的日期将在格式化时被转换为特定的时区(我刚刚修改了代码一点点,直接设置的语言环境 - 而不是使用withLocale - 但由此产生的格式等效):

// 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 

随着时区被格式化设置,这两个日期被转换为这个时区(包括日期和时间值):

20171005 050431美国/洛杉矶
20171005 050431美国/洛杉矶

虽然@Juan's answer,格式化没有设置时区,这意味着它会保存在ZonedDateTime对象所使用的时区:

// 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 

现在的时区被保留(以及日期和时间值):

20171005 050431美洲/洛杉矶
20171005 090431通社亚洲/东京

这是一个微妙的差异,你必须选择最适合你的情况的方法。请注意,关于hhHH相同的问题在这里也适用:变量nowTokyo的值等于21:04:31(东京时间9:04:31 PM),但它的格式为090431 - 没有AM/PM标识符,这个时候是不明确的,所以IMO的模式应该使用HH(所以输出将是210431)。但是由你来决定。

In the javadoc你可以看到所有可用模式的细节。