23:59-0:00から時計が刻まれた場合、日の差があるはずのような日付の変更がある場合、日数で2つのCalendar
オブジェクトの違いを見つけたいです。
私はこれを書きました
public static int daysBetween(Calendar startDate, Calendar endDate) {
return Math.abs(startDate.get(Calendar.DAY_OF_MONTH)-endDate.get(Calendar.DAY_OF_MONTH));
}
しかし、月の違いが無価値である場合にのみ日の差を与えるため、機能しません。
Java 8以降では、単純に Java.time クラスを使用できます。
hoursBetween = ChronoUnit.HOURS.between(calendarObj.toInstant(), calendarObj.toInstant());
daysBetween = ChronoUnit.DAYS.between(calendarObj.toInstant(), calendarObj.toInstant());
次のアプローチを試してください。
public static long daysBetween(Calendar startDate, Calendar endDate) {
long end = endDate.getTimeInMillis();
long start = startDate.getTimeInMillis();
return TimeUnit.MILLISECONDS.toDays(Math.abs(end - start));
}
この関数は、2つのカレンダー間の日数を、その間にある月のカレンダー日数として計算します。これは、OPが求めていたものです。計算は、カレンダーがそれぞれの日の真夜中に設定された後、カレンダー間の86,400,000ミリ秒の倍数をカウントすることによって実行されます。
たとえば、私の関数は、1月1日11:59 PMと1月2日12:01 AMのカレンダー間の1日の差を計算します。
import Java.util.concurrent.TimeUnit;
/**
* Compute the number of calendar days between two Calendar objects.
* The desired value is the number of days of the month between the
* two Calendars, not the number of milliseconds' worth of days.
* @param startCal The earlier calendar
* @param endCal The later calendar
* @return the number of calendar days of the month between startCal and endCal
*/
public static long calendarDaysBetween(Calendar startCal, Calendar endCal) {
// Create copies so we don't update the original calendars.
Calendar start = Calendar.getInstance();
start.setTimeZone(startCal.getTimeZone());
start.setTimeInMillis(startCal.getTimeInMillis());
Calendar end = Calendar.getInstance();
end.setTimeZone(endCal.getTimeZone());
end.setTimeInMillis(endCal.getTimeInMillis());
// Set the copies to be at midnight, but keep the day information.
start.set(Calendar.HOUR_OF_DAY, 0);
start.set(Calendar.MINUTE, 0);
start.set(Calendar.SECOND, 0);
start.set(Calendar.MILLISECOND, 0);
end.set(Calendar.HOUR_OF_DAY, 0);
end.set(Calendar.MINUTE, 0);
end.set(Calendar.SECOND, 0);
end.set(Calendar.MILLISECOND, 0);
// At this point, each calendar is set to midnight on
// their respective days. Now use TimeUnit.MILLISECONDS to
// compute the number of full days between the two of them.
return TimeUnit.MILLISECONDS.toDays(
Math.abs(end.getTimeInMillis() - start.getTimeInMillis()));
}
[〜#〜] update [〜#〜]Joda-Time プロジェクト、現在は メンテナンスモード 、 Java.time クラスへの移行を推奨します。経過時間の計算についてはAnees Aによる The Answer を参照してください。また、Java.timeを使用するための私の new Answer を参照してください。カレンダーに関する経過日数を計算します。
古いJava.util.Date/.Calendarクラスは面倒なことで有名なので、避けるべきです。
代わりに Joda-Time ライブラリを使用してください。 Java 8テクノロジーを使用している場合を除き、その後継である組み込みのJava.timeフレームワークを使用します(2015年時点でAndroidではありません)。
日付として定義される(24時間ではなく)「日」のみが重要なので、日付に注目しましょう。 Joda-Timeは、時刻もタイムゾーンもない日付のみの値を表すクラスLocalDate
を提供します。
タイムゾーンがありませんが、「今日」などの日付を決定する際にタイムゾーンはcrucialであることに注意してください。新しい日は、西よりも東の方が早く始まります。そのため、日付は世界中で同じではありません。日付はタイムゾーンによって異なります。
DateTimeZone zone = DateTimeZone.forID ( "America/Montreal" );
LocalDate today = LocalDate.now ( zone );
来週までの日数を数えましょう。もちろん7日間です。
LocalDate weekLater = today.plusWeeks ( 1 );
int elapsed = Days.daysBetween ( today , weekLater ).getDays ();
最後のgetDays
は、int
によって返された Days
オブジェクトから単純なdaysBetween
番号を抽出します。
コンソールにダンプします。
System.out.println ( "today: " + today + " to weekLater: " + weekLater + " is days: " + days );
今日:2015-12-22から週後:2015-12-29は日:7
Calendarオブジェクトがあります。それらをJoda-Timeオブジェクトに変換する必要があります。内部的には、Calendarオブジェクトには、1970年の最初の瞬間のエポック以降のミリ秒数を追跡するlong
整数があります [〜#〜] utc [〜#〜] 。その番号を抽出して、Joda-Timeに渡すことができます。また、日付を決定する予定のタイムゾーンを割り当てる必要があります。
long startMillis = myStartCalendar.getTimeInMillis();
DateTime startDateTime = new DateTime( startMillis , zone );
long stopMillis = myStopCalendar.getTimeInMillis();
DateTime stopDateTime = new DateTime( stopMillis , zone );
DateTimeオブジェクトからLocalDateに変換します。
LocalDate start = startDateTime.toLocalDate();
LocalDate stop = stopDateTime.toLocalDate();
ここで、先ほど見たのと同じ経過計算を行います。
int elapsed = Days.daysBetween ( start , stop ).getDays ();
@ JK1の素晴らしい答えへの拡張:
public static long daysBetween(Calendar startDate, Calendar endDate) {
//Make sure we don't change the parameter passed
Calendar newStart = Calendar.getInstance();
newStart.setTimeInMillis(startDate.getTimeInMillis());
newStart.set(Calendar.HOUR_OF_DAY, 0)
newStart.set(Calendar.MINUTE, 0)
newStart.set(Calendar.SECOND, 0)
newStart.set(Calendar.MILLISECOND, 0)
Calendar newEnd = Calendar.getInstance();
newEnd.setTimeInMillis(endDate.getTimeInMillis());
newEnd.set(Calendar.HOUR_OF_DAY, 0)
newEnd.set(Calendar.MINUTE, 0)
newEnd.set(Calendar.SECOND, 0)
newEnd.set(Calendar.MILLISECOND, 0)
long end = newEnd.getTimeInMillis();
long start = newStart.getTimeInMillis();
return TimeUnit.MILLISECONDS.toDays(Math.abs(end - start));
}
古き良きCalendarオブジェクトを使用した私のソリューションは次のとおりです。
public static int daysApart(Calendar d0,Calendar d1)
{
int days=d0.get(Calendar.DAY_OF_YEAR)-d1.get(Calendar.DAY_OF_YEAR);
Calendar d1p=Calendar.getInstance();
d1p.setTime(d1.getTime());
for (;d1p.get(Calendar.YEAR)<d0.get(Calendar.YEAR);d1p.add(Calendar.YEAR,1))
{
days+=d1p.getActualMaximum(Calendar.DAY_OF_YEAR);
}
return days;
}
これは、d0がd1より遅いことを前提としています。それが保証されない場合は、いつでもテストして交換できます。
基本的な原則は、それぞれの年の日の差を取ることです。彼らが同じ年にいるなら、それはそれでしょう。
しかし、それらは異なる年かもしれません。そこで、それらの間のすべての年をループし、1年の日数を追加します。 getActualMaximumは、うるう年で366、うるう年以外で365を返すことに注意してください。それがループを必要とする理由です。うるう年が存在する可能性があるため、年の差に365を掛けることはできません。 (私の最初のドラフトはgetMaximumを使用しましたが、年に関係なく366を返すため、機能しません。getMaximumは、この特定の年ではなく、任意の年の最大値です。)
このコードは1日の時間数を想定していないため、夏時間にだまされません。
プロジェクトが新しいJava 8クラス(選択された答えとして))をサポートしていない場合、このメソッドを追加して、タイムゾーンやその他の事実に影響されることなく日数を計算できます。
他の方法ほど高速(時間の複雑さ)ではありませんが、信頼性が高く、とにかく日付の比較が数百年または数千年を超えることはめったにありません。
(Kotlin)
/**
* Returns the number of DAYS between two dates. Days are counted as calendar days
* so that tomorrow (from today date reference) will be 1 , the day after 2 and so on
* independent on the hour of the day.
*
* @param date - reference date, normally Today
* @param selectedDate - date on the future
*/
fun getDaysBetween(date: Date, selectedDate: Date): Int {
val d = initCalendar(date)
val s = initCalendar(selectedDate)
val yd = d.get(Calendar.YEAR)
val ys = s.get(Calendar.YEAR)
if (ys == yd) {
return s.get(Calendar.DAY_OF_YEAR) - d.get(Calendar.DAY_OF_YEAR)
}
//greater year
if (ys > yd) {
val endOfYear = Calendar.getInstance()
endOfYear.set(yd, Calendar.DECEMBER, 31)
var daysToFinish = endOfYear.get(Calendar.DAY_OF_YEAR) - d.get(Calendar.DAY_OF_YEAR)
while (endOfYear.get(Calendar.YEAR) < s.get(Calendar.YEAR)-1) {
endOfYear.add(Calendar.YEAR, 1)
daysToFinish += endOfYear.get(Calendar.DAY_OF_YEAR)
}
return daysToFinish + s.get(Calendar.DAY_OF_YEAR)
}
//past year
else {
val endOfYear = Calendar.getInstance()
endOfYear.set(ys, Calendar.DECEMBER, 31)
var daysToFinish = endOfYear.get(Calendar.DAY_OF_YEAR) - s.get(Calendar.DAY_OF_YEAR)
while (endOfYear.get(Calendar.YEAR) < d.get(Calendar.YEAR)-1) {
endOfYear.add(Calendar.YEAR, 1)
daysToFinish += endOfYear.get(Calendar.DAY_OF_YEAR)
}
return daysToFinish + d.get(Calendar.DAY_OF_YEAR)
}
}
ユニットテスト、あなたはそれらを改善することができます、私は負の日を必要としなかったので、私はそれをそれほどテストしませんでした:
@Test
fun `Test days between on today and following days`() {
val future = Calendar.getInstance()
calendar.set(2019, Calendar.AUGUST, 26)
future.set(2019, Calendar.AUGUST, 26)
Assert.assertEquals(0, manager.getDaysBetween(calendar.time, future.time))
future.set(2019, Calendar.AUGUST, 27)
Assert.assertEquals(1, manager.getDaysBetween(calendar.time, future.time))
future.set(2019, Calendar.SEPTEMBER, 1)
Assert.assertEquals(6, manager.getDaysBetween(calendar.time, future.time))
future.set(2020, Calendar.AUGUST, 26)
Assert.assertEquals(366, manager.getDaysBetween(calendar.time, future.time)) //leap year
future.set(2022, Calendar.AUGUST, 26)
Assert.assertEquals(1096, manager.getDaysBetween(calendar.time, future.time))
calendar.set(2019, Calendar.DECEMBER, 31)
future.set(2020, Calendar.JANUARY, 1)
Assert.assertEquals(1, manager.getDaysBetween(calendar.time, future.time))
}
@Test
fun `Test days between on previous days`() {
val future = Calendar.getInstance()
calendar.set(2019, Calendar.AUGUST, 26)
future.set(2019,Calendar.AUGUST,25)
Assert.assertEquals(-1, manager.getDaysBetween(calendar.time, future.time))
}
@Test
fun `Test days between hour doesn't matter`() {
val future = Calendar.getInstance()
calendar.set(2019, Calendar.AUGUST, 26,9,31,15)
future.set(2019,Calendar.AUGUST,28, 7,0,0)
Assert.assertEquals(2, manager.getDaysBetween(calendar.time, future.time))
future.set(2019,Calendar.AUGUST,28, 9,31,15)
Assert.assertEquals(2, manager.getDaysBetween(calendar.time, future.time))
future.set(2019,Calendar.AUGUST,28, 23,59,59)
Assert.assertEquals(2, manager.getDaysBetween(calendar.time, future.time))
}
@Test
fun `Test days between with time saving change`() {
val future = Calendar.getInstance()
calendar.set(2019, Calendar.OCTOBER, 28)
future.set(2019, Calendar.OCTOBER,29)
Assert.assertEquals(1, manager.getDaysBetween(calendar.time, future.time))
future.set(2019, Calendar.OCTOBER,30)
Assert.assertEquals(2, manager.getDaysBetween(calendar.time, future.time))
}
Kotlinソリューションは、純粋にカレンダーに依存しています。最後に正確な日数の差を与えます。 @ Jk1に触発
private fun daysBetween(startDate: Calendar, endDate: Calendar): Long {
val start = Calendar.getInstance().apply {
timeInMillis = 0
set(Calendar.DAY_OF_YEAR, startDate.get(Calendar.DAY_OF_YEAR))
set(Calendar.YEAR, startDate.get(Calendar.YEAR))
}.timeInMillis
val end = Calendar.getInstance().apply {
timeInMillis = 0
set(Calendar.DAY_OF_YEAR, endDate.get(Calendar.DAY_OF_YEAR))
set(Calendar.YEAR, endDate.get(Calendar.YEAR))
}.timeInMillis
val differenceMillis = end - start
return TimeUnit.MILLISECONDS.toDays(differenceMillis)
}
https://stackoverflow.com/a/31800947/3845798 によって上で与えられた同様の(まったく同じではない)アプローチを持っています。
そして、APIの周りにテストケースを作成しました。私にとっては、開始日として2017年3月8日、終了日として2017年4月8日をパスすると失敗しました。
1日ごとに違いがわかる日付はほとんどありません。したがって、私はAPIにいくつかの小さな変更を加えましたが、現在のAPIは次のようになります
public long getDays(long currentTime, long endDateTime) {
Calendar endDateCalendar;
Calendar currentDayCalendar;
//expiration day
endDateCalendar = Calendar.getInstance(TimeZone.getTimeZone("EST"));
endDateCalendar.setTimeInMillis(endDateTime);
endDateCalendar.set(Calendar.MILLISECOND, 0);
endDateCalendar.set(Calendar.MINUTE, 0);
endDateCalendar.set(Calendar.HOUR, 0);
endDateCalendar.set(Calendar.HOUR_OF_DAY, 0);
//current day
currentDayCalendar = Calendar.getInstance(TimeZone.getTimeZone("EST"));
currentDayCalendar.setTimeInMillis(currentTime);
currentDayCalendar.set(Calendar.MILLISECOND, 0);
currentDayCalendar.set(Calendar.MINUTE, 0);
currentDayCalendar.set(Calendar.HOUR,0);
currentDayCalendar.set(Calendar.HOUR_OF_DAY, 0);
long remainingDays = (long)Math.ceil((float) (endDateCalendar.getTimeInMillis() - currentDayCalendar.getTimeInMillis()) / (24 * 60 * 60 * 1000));
return remainingDays;}
いくつかの問題を引き起こしていたTimeUnit.MILLISECONDS.toDaysを使用していません。