java - 為什麼減去兩個時間( 以1927 ) 會給出一個奇怪的結果?

  显示原文与译文双语对照的内容
0 0

如果我運行下面的程序,它將分析兩個日期字元串的引用時間間隔一秒,並比較它們:


public static void main(String[] args) throws ParseException {
 SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 
 String str3 ="1927-12-31 23:54:07"; 
 String str4 ="1927-12-31 23:54:08"; 
 Date sDt3 = sf.parse(str3); 
 Date sDt4 = sf.parse(str4); 
 long ld3 = sDt3.getTime()/1000; 
 long ld4 = sDt4.getTime()/1000; 
 System.out.println(ld3); 
 System.out.println(ld4); 
 System.out.println(ld4-ld3);
}

輸出結果為:


-1325491905
-1325491552
353

為什麼 ld4-ld3 不是 1 ( 就像我在one-second中的差異所期望的那樣),而是 353

如果我將日期更改為一秒鐘:


String str3 ="1927-12-31 23:54:08"; 
String str4 ="1927-12-31 23:54:09"; 

那麼 ld4-ld3 將是 1


更新

Java版本:


java version"1.6.0_22"
Java(TM) SE Runtime Environment (build 1.6.0_22-b04)
Dynamic Code Evolution Client VM (build 0.2-b02-internal, 19.0-b04-internal, mixed mode)

Timezone( TimeZone.getDefault() ):


sun.util.calendar.ZoneInfo[id="Asia/Shanghai",
offset=28800000,dstSavings=0,
useDaylight=false,
transitions=19,
lastRule=null]

Locale(Locale.getDefault()): zh_CN

时间: 原作者:

0 0

這是在上海 31年十二月的時區變化。

有關詳細信息,請參閱本頁 1927的上海。 基本上在 1927的午夜時分,時鐘返回 5分鐘和 52秒。 "1927-12-31 23:54:08"na actually_________________________________________________________________前,parsing looks likeit爪哇islater華南地區的地方,可能瞬間fortime-日期/因而區別。

只是在時間區域的奇妙奇妙世界中又一個插曲。

收費: 停止媒體 ! 歷史更改。。

原始問題將不再顯示同樣的行為,如果用 2013版本的TZDB 重新生成。 在 2013,結果將是 358秒,過渡時間的23: 54: 03代替 23: 54: 08.

我只注意到這是因為我在Noda時間內收集這樣的問題,以單元測試的形式 。 測試現在已經改變了,但它只是為了顯示- 即使歷史數據是安全的。

編輯: 歷史再次更改。。

在 TZDB 2014中,更改時間已經移動到 1900-12-31,現在是一個 343秒的更改( 所以 tt+1 之間的時間是 344秒,如果你明白我的意思) 。

編輯:回答肯問題的親戚過渡 1900. 看起來java時區實現對待所有時區就好比是在標準時間任何即時 1900utc開始前:


import java.util.TimeZone;

public class Test {
 public static void main(String[] args) throws Exception {
 long startOf1900Utc = -2208988800000L;
 for (String id : TimeZone.getAvailableIDs()) {
 TimeZone zone = TimeZone.getTimeZone(id);
 if (zone.getRawOffset()!= zone.getOffset(startOf1900Utc - 1)) {
 System.out.println(id);
 }
 }
 }
}

上面的代碼在我的Windows 機器上沒有輸出。 所以在 1900開始時,任何有偏移量的時區都將算作一個過渡。 TZDB本身有一些早於它的數據,並且不依賴於"固定"標準時間( 這就是 getRawOffset 認為是一個有效的概念)的任何概念,所以其他庫不需要引入這種人工轉換。

原作者:
0 0

我運行了你的代碼,輸出是:


-1325466353
-1325466352
1

還有:


-1325466352
-1325466351
1

打開:


$ java -version
java version"1.6.0_20"
OpenJDK Runtime Environment (IcedTea6 1.9.7) (fedora-52.1.9.7.fc14-i386)
OpenJDK Client VM (build 19.0-b09, mixed mode)

原作者:
0 0

當遞增時,你應該轉換回 UTC,然後添加或者減去。 僅使用本地時間顯示。

這樣你就可以在任何時間或者時間發生兩次。

如果轉換為 UTC,則添加每秒,並轉換為本地時間以顯示。 你會經過 11: 54: 08 p.m. 以前 - 11: 59: 59 p.m. 以前然後 11: 54: 08 p.m. 中科 - 11 0: 11 1: 11 2 p.m. 春秋國旅。

原作者:
0 0

使用以下代碼代替每個日期


long i = (sDt4.getTime() - sDt3.getTime())/1000;
System.out.println(i);

結果是:

 
1

 
原作者:
0 0

我很抱歉,但是時間不連續已經移動了一點

兩年前 JDK 6, JDK 7最近在更新 25

學習的教訓:避免non-UTC時間,除了可能的顯示。

原作者:
0 0

就像其他人所解釋的,有一個時間不連續。 1927-12-31 23:54:08Asia/Shanghai 處有兩個可能的時區偏移量,但 1927-12-31 23:54:07 只有一個偏移量。 所以,根據使用的偏移量,有一個第二個差或者 5分鐘和 53秒差。

這種偏移的輕微偏移,而不是通常的one-hour日光節約( 夏季時間),使問題有點模糊。

注意,2013更新時區資料庫在幾秒鐘之前移動了這個不連續,但效果仍然可以觀察到。

java.time 包java 8讓使用更清楚地看到這個,並提供工具來處理它。 給出:


DateTimeFormatterBuilder dtfb = new DateTimeFormatterBuilder();
dtfb.append(DateTimeFormatter.ISO_LOCAL_DATE);
dtfb.appendLiteral(' ');
dtfb.append(DateTimeFormatter.ISO_LOCAL_TIME);
DateTimeFormatter dtf = dtfb.toFormatter();
ZoneId shanghai = ZoneId.of("Asia/Shanghai");

String str3 ="1927-12-31 23:54:07"; 
String str4 ="1927-12-31 23:54:08"; 

ZonedDateTime zdt3 = LocalDateTime.parse(str3, dtf).atZone(shanghai);
ZonedDateTime zdt4 = LocalDateTime.parse(str4, dtf).atZone(shanghai);

Duration durationAtEarlierOffset = Duration.between(zdt3.withEarlierOffsetAtOverlap(), zdt4.withEarlierOffsetAtOverlap());

Duration durationAtLaterOffset = Duration.between(zdt3.withLaterOffsetAtOverlap(), zdt4.withLaterOffsetAtOverlap());

然後 durationAtEarlierOffset 將是一秒鐘,而 durationAtLaterOffset 將是五分鐘和 53秒。

同時,這兩個偏移量是相同的:


//Both have offsets +08:05:52
ZoneOffset zo3Earlier = zdt3.withEarlierOffsetAtOverlap().getOffset();
ZoneOffset zo3Later = zdt3.withLaterOffsetAtOverlap().getOffset();

但這兩個是不同的:


//+08:05:52
ZoneOffset zo4Earlier = zdt4.withEarlierOffsetAtOverlap().getOffset();

//+08:00
ZoneOffset zo4Later = zdt4.withLaterOffsetAtOverlap().getOffset();

你可以看到與 1927-12-31 23:59:59 相比,同樣的問題,但在這種情況下,它是產生較長散度的早期偏移量,它是有兩個可能偏移的早期日期。

另一種方法是檢查是否發生了轉換。 我們可以這樣做:


//Null
ZoneOffsetTransition zot3 = shanghai.getRules().getTransition(ld3.toLocalDateTime);

//An overlap transition
ZoneOffsetTransition zot4 = shanghai.getRules().getTransition(ld3.toLocalDateTime);

你可以檢查是否過渡是一個重疊——在這種情況下有多個有效抵消,日期/時間——或者一個缺口——在這種情況下,日期/時間無效區域id-通過使用 isOverlap()zot4isGap() 方法。

我希望這能幫助人們處理這種問題一旦java 8越來越廣泛,或使用java 7那些採用jsr 310 backport 。

原作者:
...