[英]Why SimpleDateFormat.format() and SimpleDateFormat.parse() are giving different time though setting only one TimeZone?
[英]Why does SimpleDateFormat.format(Date) ignore the configured 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);
獲得的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。
代替設置默認時區,在您的SimpleDateFormat
對象上設置時區:
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
這將使SimpleDateFormat
對象在UTC
時區中設置Date
對象的格式。
如果您使用的是Java 8,請考慮在包java.time
使用新的日期和時間API,而不是舊的java.util.Date
和java.text.SimpleDateFormat
。
不 ,這不是錯誤。
您必須首先了解Date類的工作原理。 自epoch
以來的毫秒數只是一個包裝,以long表示。 因此,無論是哪個時區,日期對象的基礎值都保持不變。 您永遠無法真正更改Date
類的時區。 您只能使用SimpleDateFormat
類表示日期實例的String
格式。 根據您在創建SimpleDateFormat
對象時所使用的時區,此表示形式可能具有不同的時區。
同樣,您需要檢查Date類的toString方法。 它始終以默認時區打印日期。
編輯
您還應該查看SimpleDateFormat.parse()定義。 JDK說,
根據給定的模式和文本中的時區值,TimeZone值可能會被覆蓋。 可能需要恢復以前通過調用setTimeZone設置的任何TimeZone值,以進行進一步的操作。
這個謎的答案在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
您通過使用麻煩的舊的舊式日期時間類來折磨自己,這些類現在已被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教程 。 並在Stack Overflow中搜索許多示例和說明。
多的java.time功能后移植到Java 6和7在ThreeTen-反向移植並且還適於在到Android ThreeTenABP 。
Instant
輸入字符串2016-08-01T00:00:00Z
末尾的Z
是Zulu
縮寫,表示UTC 。 由Instant
類在java.time中表示,分辨率最高為ns。
Instant instant = Instant.parse ( "2016-08-01T00:00:00Z" );
ZonedDateTime
通過ZoneId
指定一個時區以獲取ZonedDateTime
。
在夏天, 巴黎時間比UTC提前兩個小時。 因此,在8月1日的同一天,與巴黎牆上的時鍾所看到的同一時刻是凌晨2點,而不是午夜。
ZoneId zoneId_Paris = ZoneId.of ( "Europe/Paris" );
ZonedDateTime zdt_Paris = ZonedDateTime.ofInstant ( instant , zoneId_Paris );
您可以調整到另一個時區,例如Atlantic/Canary
。 對於夏季的這個日期,時間比UTC提前一小時。 結果是1 AM,而不是午夜。
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,巴黎,加那利)的代表在歷史上是相同的同時時刻,在時間軸上同一個點。 每個鏡頭都是通過不同的時鍾時間觀看的。
避免使用3-4個字母的區域縮寫,例如WET
和WEST
。 這些不是實時區域,不是標准化的,甚至不是唯一的(!)。
Europe/Paris
和Atlantic/Canary
是正確的時區名稱 ,格式為continent/region
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.