鉴于我的默认TimeZone是欧洲/巴黎:
System.out.println("Current Timezone: " + TimeZone.getDefault());
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
String dateStrIn = "2016-08-01T00:00:00Z";
Date date = dateFormat.parse(dateStrIn);
String dateStrOut = dateFormat.format(date);
System.out.println("Input date String: "+dateStrIn);
System.out.println("Date.toString() "+date);
System.out.println("Output date String: "+dateStrOut);
输出结果为:
Current Timezone: sun.util.calendar.ZoneInfo[id="Europe/Paris",offset=3600000,dstSavings=3600000,useDaylight=true,transitions=184,lastRule=java.util.SimpleTimeZone[id=Europe/Paris,offset=3600000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]]
Input date String: 2016-08-01T00:00:00Z
Date.toString() Mon Aug 01 02:00:00 CEST 2016
Output date String: 2016-08-01T02:00:00+02
现在,我重复执行但设置了不同的默认TimeZone(UTC):
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
System.out.println("Current Timezone: " + TimeZone.getDefault());
...
这是输出:
Current Timezone: sun.util.calendar.ZoneInfo[id="UTC",offset=0,dstSavings=0,useDaylight=false,transitions=0,lastRule=null]
Input date String: 2016-08-01T00:00:00Z
Date.toString() Mon Aug 01 00:00:00 UTC 2016
Output date String: 2016-08-01T02:00:00+02
Date.toString()
已正确考虑TimeZone更改。但是,从dateFormat.format(date);
得到的字符串仍显示+02而不是Z或+00,为什么?
使用标准Java API,有没有办法根据选定的TimeZone强制格式化?
更新
Jesper的解决方案几乎适用于所有情况,但我遇到过这个问题(使用加那利岛的夏季时区WEST),其中并没有:
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss z");
dateFormat.setTimeZone(TimeZone.getTimeZone("Europe/Paris"));
TimeZone.setDefault(TimeZone.getTimeZone("Europe/Paris"));
System.out.println("Current Timezone: " + TimeZone.getDefault());
String dateStrIn = "2016-08-01T08:00:00 WEST";
Date date = dateFormat.parse(dateStrIn);
String dateStrOut = dateFormat.format(date);
System.out.println("Input date String: "+dateStrIn);
System.out.println("Date.toString() "+date);
System.out.println("Output date String: "+dateStrOut);
输出:
Current Timezone: sun.util.calendar.ZoneInfo[id="Europe/Paris",offset=3600000,dstSavings=3600000,useDaylight=true,transitions=184,lastRule=java.util.SimpleTimeZone[id=Europe/Paris,offset=3600000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]]
Input date String: 2016-08-01T08:00:00 WEST
Date.toString() Mon Aug 01 09:00:00 CEST 2016
Output date String: 2016-08-01T08:00:00 WEST
您可以看到输出日期字符串仍然以WEST表示。
例如,如果我将初始dateStrIn更改为:String dateStrIn = "2016-08-01T08:00:00 GMT";
,那么输出日期字符串将在CEST中表示为:
Output date String: 2016-08-01T10:00:00 CEST
这可能是个错误吗?
更新2:
另一个例子
Default TimeZone for both Date and SimpleDateFormat: "Europe/Paris"
Input String: "2016-08-01T08:00:00 WET"
输出:
Date.toString() Mon Aug 01 10:00:00 CEST 2016
Output date String: 2016-08-01T09:00:00 WEST
请注意dateFormat.format(date);
已生成WEST日期字符串。来自WET - > WEST应该是WET - > CEST。
答案 0 :(得分:2)
不是设置默认时区,而是在SimpleDateFormat
对象上设置时区:
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
这会使SimpleDateFormat
对象格式化为Date
时区中的UTC
对象。
如果您使用的是Java 8,请考虑在包java.time
中使用新的日期和时间API,而不是旧的java.util.Date
和java.text.SimpleDateFormat
。
答案 1 :(得分:0)
否,这不是错误。
您必须先了解Date类的工作原理。它只是自epoch
以来的毫秒数的包装器,用 long 表示。因此,无论时区如何,日期对象的基础值都保持不变。你永远不能真正改变Date
类的时区。您只能使用String
类表示日期实例的SimpleDateFormat
格式。此表示可能具有不同的时区,基于您在创建SimpleDateFormat
对象时使用的时区。
同样,您需要检查Date类的toString方法。它始终使用默认时区打印日期。
修改强>
您还应该查看SimpleDateFormat.parse()定义。 JDK说,
TimeZone值可能会被覆盖,具体取决于给定的模式和文本中的时区值。之前通过调用setTimeZone设置的任何TimeZone值可能需要恢复以进行进一步操作。
答案 2 :(得分:0)
这个谜的答案在于Javadoc:
DateFormat.parse(String source, ParsePosition pos)
此解析操作使用日历生成日期。作为一个 结果,日历的日期时间字段和TimeZone值可以 已被覆盖,具体取决于子类实现。任何 之前通过调用setTimeZone设置的TimeZone值 可能需要恢复以进行进一步的操作。
解析后设置TimeZone可以解决问题:
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss z");
TimeZone.setDefault(TimeZone.getTimeZone("Europe/Paris"));
System.out.println("Current Timezone: " + TimeZone.getDefault());
String dateStrIn = "2016-08-01T08:00:00 WET";
Date date = dateFormat.parse(dateStrIn);
dateFormat.setTimeZone(TimeZone.getTimeZone("Europe/Paris"));
String dateStrOut = dateFormat.format(date);
System.out.println("Input date String: "+dateStrIn);
System.out.println("Date.toString() "+date);
System.out.println("Output date String: "+dateStrOut);
右输出:
Current Timezone: sun.util.calendar.ZoneInfo[id="Europe/Paris",offset=3600000,dstSavings=3600000,useDaylight=true,transitions=184,lastRule=java.util.SimpleTimeZone[id=Europe/Paris,offset=3600000,dstSavings=3600000,useDaylight=true,startYear=0,startMode=2,startMonth=2,startDay=-1,startDayOfWeek=1,startTime=3600000,startTimeMode=2,endMode=2,endMonth=9,endDay=-1,endDayOfWeek=1,endTime=3600000,endTimeMode=2]]
Input date String: 2016-08-01T08:00:00 WET
Date.toString() Mon Aug 01 10:00:00 CEST 2016
Output date String: 2016-08-01T10:00:00 CEST
答案 3 :(得分:0)
你通过使用现在已经过时的java.time框架的旧的遗留日期时间类来折磨自己。
ZonedDateTime.ofInstant( Instant.parse( "2016-08-01T00:00:00Z" ) , ZoneId.of( "Europe/Paris" ) )
java.time框架内置于Java 8及更高版本中。这些类取代了旧的麻烦日期时间类,例如java.util.Date
,.Calendar
和& java.text.SimpleDateFormat
。 Joda-Time团队还建议迁移到java.time。
要了解详情,请参阅Oracle Tutorial。并搜索Stack Overflow以获取许多示例和解释。
大部分java.time功能都被反向移植到Java 6& ThreeTen-Backport中的7,并在ThreeTenABP中进一步适应Android。
Instant
输入字符串Z
末尾的2016-08-01T00:00:00Z
是Zulu
的缩写,表示UTC。由Instant
类在java.time中表示,分辨率高达纳秒。
Instant instant = Instant.parse ( "2016-08-01T00:00:00Z" );
ZonedDateTime
通过ZoneId
指定时区以获得ZonedDateTime
。
在夏天,Paris time比UTC早两个小时。所以在巴黎墙上的钟表上看到的同一时刻是8月1日凌晨2点而不是午夜。
ZoneId zoneId_Paris = ZoneId.of ( "Europe/Paris" );
ZonedDateTime zdt_Paris = ZonedDateTime.ofInstant ( instant , zoneId_Paris );
您可以调整到另一个时区,例如Atlantic/Canary
。对于夏季的这个日期,时间比UTC早一个小时。结果是凌晨1点而不是午夜。
ZoneId zoneId_Canary = ZoneId.of ( "Atlantic/Canary" );
ZonedDateTime zdt_Canary = zdt_Paris.withZoneSameInstant ( zoneId_Canary );
转储到控制台。
System.out.println ( "instant: " + instant + " | zdt_Paris: " + zdt_Paris + " | zdt_Canary: " + zdt_Canary );
即时:2016-08-01T00:00:00Z | zdt_Paris:2016-08-01T02:00 + 02:00 [欧洲/巴黎] | zdt_Canary:2016-08-01T01:00 + 01:00 [大西洋/金丝雀]
所有这三个对象(UTC,Paris,Canary)代表历史上相同的同步时刻,即时间轴上的同一个点。每个都通过不同wall-clock time的镜头观看。
避免使用3-4个字母区域缩写,例如WET
和WEST
。这些不是实时区域,不是标准化的,甚至不是唯一的(!)。
Europe/Paris
和Atlantic/Canary
为proper time zone names,格式为continent/region
。