web-dev-qa-db-ja.com

Javaカレンダー:2つの日付/時刻の差を取得-1つずつオフ

私はこのトピックに関して多くの質問と回答を見てきましたが、私の特定の問題に対処したものはありませんでした。 Javaカレンダークラス(標準-サードパーティライブラリなし))を拡張し、2つの任意の日付間のの違いを見つける必要がありました。

方法:

  1. 両方の日付の時刻を深夜に変更します。
  2. 日付をミリ秒に変換します。
  3. 2つの日付の違いを見つけます。
  4. 結果を1日のミリ秒数(24 * 60 * 60 * 1000)で割ります。
  5. 結果は日数の違いになるはずです。

そして、そうであることもあれば、そうでないこともあります。同じ日のテストでさえ、1つずれることがあります。どうしたの?

7
Scott Biggs

他のユーザーが提案しているように、日付のみを比較するには、カレンダーのミリ秒値もゼロに設定する必要があります。これは、次のコードスニペットで実現できます。

someCalendar.set(Calendar.MILLISECOND, 0)

また、タイムゾーンの変更(冬時間から夏時間への移動など)は、1日あたり86,400,000ミリ秒より多いまたは少ないことを意味する場合があることに注意してください。

5
Duncan Jones

Joda Time ライブラリはこのような問題を非常によくサポートしています。

LocalDate d1 = new LocalDate(calendar1.getTimeInMillis());
LocalDate d2 = new LocalDate(calendar2.getTimeInMillis());
int days = Days.daysBetween(d1, d2).getDays();

更新(@ basil-bourqueからのフィードバック):

Java 8の時点で新しい時間ライブラリJava.timeが導入され、外部依存関係のない同様のオプションが利用可能になりました。

int days = Duration.between(calendar1.toInstant(), calendar2.toInstant()).toDays();
7
Arne Burmeister

tl; dr

ChronoUnit.DAYS.between( then , now )

ナイーブな計算

コードでは、毎日が正確に24時間であると想定しています。違います。 夏時間(DST) などの異常は、23時間、25時間、またはその他の数値など、日が異なる可能性があることを意味します。タイムゾーンのまさに意味は、これらの異常の履歴を追跡することです。

また、1日が深夜に始まると想定します。違います。異常のため、01:00などの他の時刻から始まる日もあります。

従来の日時クラスを避ける

Java.util.DateJava.util.CalendarJava.text.SimpleTextFormatなどの厄介な古い日時クラスを使用しています レガシーJava.time クラスに置き換えられました。

タイムゾーン

適切なタイムゾーン名continent/regionの形式で指定します。たとえば、 America/MontrealAfrica/Casablanca 、またはPacific/AucklandESTISTなどの3〜4文字の省略形は、そのままでは使用しないでくださいnot真のタイムゾーンであり、標準化されておらず、一意でもありません(!)。

Java.timeを拡張しないでください

Java.timeクラスを拡張(サブクラス化)しないでください(これらはfinalとマークされています)。

また、ビジネスロジックのインターフェイスに一般化しないでください。このフレームワークの具体的なクラスに固執します。一般化は Javaコレクション などの他のフレームワークでは意味がありますが、Java.timeではそうではありません。

Java.timeの使用

この作業は、Java.timeクラスを使用するとはるかに簡単になります。

ZonedDateTime は、タイムライン上でタイミングゾーン(ZoneId)が割り当てられた瞬間を表します。

ZoneId z = ZoneId.of( "America/Montreal" );

// of(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond, ZoneId zone)
ZonedDateTime then = ZonedDateTime.of( 2017 , 1 , 23 , 12 , 34 , 56 , 123456789 , z );

ZonedDateTime now = ZonedDateTime.now( z );

ChronoUnit 列挙型は、モーメントのペア間の経過時間を計算できます。

long days = ChronoUnit.DAYS.between( then , now );

このコードはIdeOne.comでライブで実行されます を参照してください。

then.toString():2017-01-23T12:34:56.123456789-05:00 [南北アメリカ/モントリオール]

now.toString():2017-03-01T21:26:04.884-05:00 [南北アメリカ/モントリオール]

日:37

レガシーインスタンスのJava.timeへの変換

GregorianCalendar オブジェクトが与えられた場合は、古いクラスに追加された新しいメソッドを使用してJava.timeに変換します。

ZonedDateTime zdt = myGregCal.toZonedDateTime() ;

Calendarインスタンスが実際にはGregorianCalendarであることがわかっている場合は、それをキャストします。

GregorianCalendar myGregCal = (GregorianCalendar) myCal ;

半開き

Java.timeクラスは、開始がinclusiveで、終了がexclusiveである、Half-Openアプローチによって期間を定義します。


Java.timeについて

Java.time フレームワークはJava 8以降に組み込まれています。これらのクラスは、厄介な古い legacy などの日時クラスに取って代わります。 Java.util.DateCalendar 、& SimpleDateFormat

Joda-Time プロジェクトは、現在 メンテナンスモード であり、 Java.time クラスへの移行をアドバイスします。

詳細については、 Oracleチュートリアル を参照してください。そして、StackOverflowで多くの例と説明を検索してください。仕様は JSR 31 です。

Java.timeクラスはどこで入手できますか?

ThreeTen-Extra プロジェクトは、Java.timeを追加のクラスで拡張します。このプロジェクトは、Java.timeに将来追加される可能性のある試験場です。 IntervalYearWeekYearQuarter などの便利なクラスがここにあります。 、および more

5
Basil Bourque

私はこの問題に取り組むためのはるかに簡単な方法を書きましたが、いくつかの日付に1日余分にかかるという同じ問題に直面しました。

これが現在の私のアプローチです、

    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);

    //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);

    long remainingDays = Math.round((float) (endDateCalendar.getTimeInMillis() - currentDayCalendar.getTimeInMillis()) / (24 * 60 * 60 * 1000));

    return remainingDays;
}

以前はこの線を使用していましたが、残りは同じです:-

long remainingDays = TimeUnit.MILLISECONDS.toDays(
            Math.abs(expiryCalendar.getTimeInMillis() - currentDayCalendar.getTimeInMillis()));

しかし、私にはうまくいかないようです。

3
Arpit

手順1で、時刻を深夜に設定することで問題を認識しました。これにより、すべての時間、分、秒がゼロになっていることが確認されました。しかし、あなたは十分に行きませんでした!また、millisecondsもゼロになっていることを確認する必要があります。

これを正確に行うためのコードを次に示します。

protected long truncate_to_seconds (long date_in_millis) {
    long l = date_in_millis / 1000L;
    l *= 1000L;
    return l;
} 
2
Scott Biggs

時間部分を切り捨てる必要があると思います以下のように差を計算する前の日付から:

   DateFormat formatter= new SimpleDateFormat("MM/dd/yyyy");
   String truncatedDateString1 = formatter.format(date1);
   Date truncatedDate1 = formatter.parse(truncatedDateString1);

   String truncatedDateString2 = formatter.format(date2);
   Date truncatedDate2 = formatter.parse(truncatedDateString2);

   long timeDifference = truncatedDate2.getTime()- truncatedDate1.getTime();

   int daysInBetween = timeDifference / (24*60*60*1000);

これがうまくいくことを願っています。

1
Yogendra Singh

私はあなたの要求に応じて目的として私の機能を持っています。カレンダーを使用していたので、比較する前にすべての時間部分を0に設定しました。

    int countDays(Date dateBegin, Date dateEnd) {
    if (dateBegin == null || dateEnd == null)
        return 0;
    Calendar from = asCalendar(dateBegin); // asCalendar() Initialise a Calendar from a Date
    Calendar to = asCalendar(dateEnd);
    // Set the time part to 0
    from.set(Calendar.MILLISECOND, 0);
    from.set(Calendar.SECOND, 0);
    from.set(Calendar.MINUTE, 0);
    from.set(Calendar.HOUR_OF_DAY, 0);
    to.set(Calendar.MILLISECOND, 0);
    to.set(Calendar.SECOND, 0);
    to.set(Calendar.MINUTE, 0);
    to.set(Calendar.HOUR_OF_DAY, 0);
    int nbJours = 0;
    for (Calendar c = from ; c.before(to) ; c.add(Calendar.DATE, +1))
    {
        nbJours++;
    }
    for (Calendar c = from ; c.after(to) ; c.add(Calendar.DATE, -1))
    {
        nbJours--;
    }
    return nbJours;
    }
0
Fredouille