2

TimeUnit クラスの動作がおかしいことに気がついたので、再現するためにこの最小限の例を作成しました。

long differenceInDays;

Calendar c1 = Calendar.getInstance();
Calendar c2 = Calendar.getInstance();

c1.setTimeInMillis(1466062306000l); // Thu Jun 16 2016 09:31:46 GMT+0200
c2.setTimeInMillis(1466028000000l); // Thu Jun 16 2016 00:00:00 GMT+0200

differenceInDays = TimeUnit.DAYS.convert(c2.getTimeInMillis() - c1.getTimeInMillis(), TimeUnit.MILLISECONDS);
System.out.println(differenceInDays); // obviously zero

c2.add(Calendar.DATE, +1);
differenceInDays = TimeUnit.DAYS.convert(c2.getTimeInMillis() - c1.getTimeInMillis(), TimeUnit.MILLISECONDS);
System.out.println(differenceInDays); // why zero and not one?

c2.add(Calendar.DATE, +1);
differenceInDays = TimeUnit.DAYS.convert(c2.getTimeInMillis() - c1.getTimeInMillis(), TimeUnit.MILLISECONDS);
System.out.println(differenceInDays); // suddenly a 1, but not a 2 like expected

最初に差が計算されたときは明らかに 0 です。これは、2 つの日付の間に 1 日が存在するわけではないためです。

しかし、2 回目は丸 1 日を加算するのに、どうして差がまだ 0 になるのでしょうか?

出力:

0
0
1

この問題は夏時間やうるう年に関連しているとは思いません。同じ年、さらには月内でしか計算を行っていないからです。

これは、チェックするための日付からミリ秒への計算機です。

4

1 に答える 1

10

ここで何が起こっているかは、簡単な計算でよくわかります。

c1 = 1466062306000
c2 = 1466028000000

d = 86400000                // one day

c2 - c1 = -34306000         // negative, but less than one day in magnitude
c2 - c1 + d = 52094000      // less than one day
c2 - c1 + d + d = 138494000 // more than one day, less than two days

Java 8 を使用していると仮定すると、これを処理する正しい方法は次のとおりです。

// Decide what time zone you want to work in
ZoneId tz = ZoneId.of("Europe/Berlin");

// If you wanted the local time zone of the system,
// Use this instead:
// ZoneId tz = ZoneId.systemDefault();

// Get instants from the timestamps
Instant i1 = Instant.ofEpochMilli(1466062306000l);
Instant i2 = Instant.ofEpochMilli(1466028000000l);

// Get the calendar date in the specified time zone for each value
LocalDate d1 = i1.atZone(tz).toLocalDate();
LocalDate d2 = i2.atZone(tz).toLocalDate();

// Get the difference in days
long daysBetween = ChronoUnit.DAYS.between(d2, d1);

入力がタイムスタンプではなく本当にCalendarオブジェクトである場合は、レガシーの日時コードのガイダンスCalendar.toInstant()で説明されている方法をお勧めします。

Java 7 以前を使用している場合は、Joda Timeライブラリから同様の機能を見つけることができます。

これらのいずれも使用したくないが、それでも古い (難しい) 方法を使用する場合は、この例を参照してください。

于 2016-06-16T21:53:32.160 に答える