1日の開始時刻と終了時刻を取得する方法は?


121

1日の開始時刻と終了時刻を取得する方法は?

このようなコードは正確ではありません:

 private Date getStartOfDay(Date date) {
    Calendar calendar = Calendar.getInstance();
    int year = calendar.get(Calendar.YEAR);
    int month = calendar.get(Calendar.MONTH);
    int day = calendar.get(Calendar.DATE);
    calendar.set(year, month, day, 0, 0, 0);
    return calendar.getTime();
}

private Date getEndOfDay(Date date) {
    Calendar calendar = Calendar.getInstance();
    int year = calendar.get(Calendar.YEAR);
    int month = calendar.get(Calendar.MONTH);
    int day = calendar.get(Calendar.DATE);
    calendar.set(year, month, day, 23, 59, 59);
    return calendar.getTime();
}

ミリ秒単位では正確ではありません。


8
「正確でない」とはどういう意味ですか?
カークウォル

コードの何が問題になっていますか?期待していないことは何ですか?一日の始まりと終わりのタイムゾーンを教えてください。
マークリード

4
上記のコードでは、メソッドに渡された日付も使用されていません。カレンダーが作成された後、calendar.setTime(date)を実行する必要があります。
Jerry Destremps 2013年

2
この質問では、日時値の分解能がミリ秒であると想定しています。java.util.DateおよびJoda-Timeの場合はTrue。ただし、Java 8のjava.timeパッケージ(ナノ秒)、Postgres(マイクロ秒)、UNIXライブラリ(全秒)などの一部のデータベースではFalse 。Half-Openを使用したより安全なアプローチについては、私の回答を参照しください。
バジルブルク2014

2
実際には、次の日の始めを取得する必要があり、その日にアイテムを繰り返す場合(そうしたいと仮定した場合)、<=の代わりに<を使用して上限を指定します。それは翌日を除外しますが、ナノ秒までの前日のすべてを含みます。
ダニエルF

回答:


115

tl; dr

LocalDate                       // Represents an entire day, without time-of-day and without time zone.
.now(                           // Capture the current date.
    ZoneId.of( "Asia/Tokyo" )   // Returns a `ZoneId` object.
)                               // Returns a `LocalDate` object.
.atStartOfDay(                  // Determines the first moment of the day as seen on that date in that time zone. Not all days start at 00:00!
    ZoneId.of( "Asia/Tokyo" ) 
)                               // Returns a `ZonedDateTime` object.

一日の始まり

時間帯で見た今日の全長を取得します。

開始が包括的で、終了が排他的であるハーフオープンアプローチの使用。このアプローチは、1日の最後の2分の1を占めないコードの欠陥を解決します。

ZoneId zoneId = ZoneId.of( "Africa/Tunis" ) ;
LocalDate today = LocalDate.now( zoneId  ) ;

ZonedDateTime zdtStart = today.atStartOfDay( zoneId ) ;
ZonedDateTime zdtStop = today.plusDays( 1 ).atStartOfDay( zoneId ) ;

zdtStart.toString()= 2020-01-30T00:00 + 01:00 [アフリカ/チュニス]

zdtStop.toString()= 2020-01-31T00:00 + 01:00 [アフリカ/チュニス]

UTCで同じ瞬間をご覧ください。

Instant start = zdtStart.toInstant() ;
Instant stop = zdtStop.toInstant() ;

start.toString()= 2020-01-29T23:00:00Z

stop.toString()= 2020-01-30T23:00:00Z

タイムゾーンではなくUTCで表示される日付の1日全体が必要な場合は、を使用しますOffsetDateTime

LocalDate today = LocalDate.now( ZoneOffset.UTC  ) ;

OffsetDateTime odtStart = today.atTime( OffsetTime.MIN ) ;
OffsetDateTime odtStop = today.plusDays( 1 ).atTime( OffsetTime.MIN ) ;

odtStart.toString()= 2020-01-30T00:00 + 18:00

odtStop.toString()= 2020-01-31T00:00 + 18:00

これらのOffsetDateTime オブジェクトはすでにUTCにありますがtoInstant、定義により常にUTCにあるオブジェクトが必要な場合は呼び出すことができます。

Instant start = odtStart.toInstant() ;
Instant stop = odtStop.toInstant() ;

start.toString()= 2020-01-29T06:00:00Z

stop.toString()= 2020-01-30T06:00:00Z

ヒント:このオブジェクトのペアを表すためにそのクラスを使用するために、プロジェクトにThreeTen-Extraライブラリを追加することに興味があるかもしれません。これは、クラスの提供など、比較のために有用な方法を、、、そしてより多くの。IntervalInstantabutsoverlapscontains

Interval interval = Interval.of( start , stop ) ;

interval.toString()= 2020-01-29T06:00:00Z / 2020-01-30T06:00:00Z

半開き

mprivatによって答えが正解です。彼のポイントは、1日の終わりを取得しようとするのではなく、「翌日の開始前」と比較することです。彼のアイデアは「ハーフオープン」アプローチとして知られています。このアプローチでは、ある期間の始まりは包括的で、終わりは排他的です。

  • Javaの現在の日時フレームワーク(java.util.Date/CalendarおよびJoda-Time)は、どちらもエポックからのミリ秒を使用します。しかし、Java 8では、新しいJSR 310 java.time。*クラスはナノ秒の解像度を使用します。新しいクラスに切り替えた場合、最後の瞬間のミリ秒カウントを強制することに基づいて記述したコードは正しくありません。
  • 他のソースからのデータの比較は、他の解像度を採用している場合に失敗します。たとえば、Unixライブラリは通常、秒単位を使用し、Postgresなどのデータベースは日時をマイクロ秒に解決します。
  • 夏時間の変更の中には真夜中に発生するものもあり、さらに混乱を招く可能性があります。

ここに画像の説明を入力してください

Joda-Time 2.3は、まさにこの目的のために、その日の最初の瞬間を取得する方法を提供しますwithTimeAtStartOfDay()。同様にjava.timeで、LocalDate::atStartOfDay

StackOverflowで「joda half-open」検索して、詳細な説明と例を確認してください。

この投稿を参照してください。時間間隔とその他の範囲は、Bill Schneiderによりハーフオープンにする必要があります

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

java.util.Dateクラスと.Calendarクラスは、非常に面倒です。それらを避けてください。

java.timeクラスを使用します。java.timeのフレームワークは非常に成功したの公式の後継者であるジョダタイムライブラリ。

java.time

java.timeフレームワークは、Java 8以降に組み込まれています。でJava 6&7にバックポートThreeTen-バックポートのプロジェクト、さらに中のAndroidに適応ThreeTenABPのプロジェクト。

アンは、Instantタイムライン上の瞬間であるUTCの解像度を持つナノ秒

Instant instant = Instant.now();

タイムゾーンを適用して、特定の地域の実時間を取得します。

ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );

その日の最初の瞬間を取得するには、LocalDateクラスとそのatStartOfDayメソッドを通過します。

ZonedDateTime zdtStart = zdt.toLocalDate().atStartOfDay( zoneId );

ハーフオープンアプローチを使用して、翌日の最初の瞬間を取得します。

ZonedDateTime zdtTomorrowStart = zdtStart.plusDays( 1 );

モダンとレガシーの両方のJavaのすべての日時タイプの表

現在、java.timeフレームワークには、Interval以下に説明するJoda -Timeのクラスがありません。ただし、ThreeTen-Extraプロジェクトは、java.timeを追加のクラスで拡張します。このプロジェクトは、java.timeに将来追加される可能性があることを証明する場です。そのクラスの中にありますInterval。オブジェクトのIntervalペアを渡してを構築しInstantます。オブジェクトInstantからを抽出できZonedDateTimeます。

Interval today = Interval.of( zdtStart.toInstant() , zdtTomorrowStart.toInstant() );

java.timeについて

java.timeのフレームワークは、Java 8に組み込まれており、後にされています。これらのクラスは面倒古い取って代わるレガシーのような日付時刻クラスをjava.util.DateCalendar、& SimpleDateFormat

詳細については、Oracleチュートリアルを参照してください。スタックオーバーフローで多くの例と説明を検索してください。仕様はJSR 310です。

ジョダタイムプロジェクトは、今でメンテナンスモードへの移行をアドバイスjava.timeのクラス。

java.timeオブジェクトをデータベースと直接交換することができます。JDBC 4.2以降に準拠したJDBCドライバーを使用します。文字列もクラスも必要ありません。Hibernate 5とJPA 2.2はjava.timeをサポートしていますjava.sql.*

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


ジョーダタイム

更新:Joda -Timeプロジェクトはメンテナンスモードになり、java.timeクラスへの移行を推奨します。このセクションはそのまま残しておきます。

ジョダタイムは、さまざまな方法で時間のスパンを表現するための3つのクラスがありますIntervalPeriodDuration。アンはInterval、特定の開始と宇宙のタイムライン上で終了しています。これは「1日」を表すという私たちのニーズに適合します。

withTimeAtStartOfDay時刻をゼロに設定するのではなく、メソッドを呼び出します。夏時間およびその他の異常のため、その日の最初の瞬間はそうではない場合があります00:00:00

Joda-Time 2.3を使用したサンプルコード。

DateTimeZone timeZone = DateTimeZone.forID( "America/Montreal" );
DateTime now = DateTime.now( timeZone );
DateTime todayStart = now.withTimeAtStartOfDay();
DateTime tomorrowStart = now.plusDays( 1 ).withTimeAtStartOfDay();
Interval today = new Interval( todayStart, tomorrowStart );

必要な場合は、java.util.Dateに変換できます。

java.util.Date date = todayStart.toDate();

使用する必要がありますLocalDateTime
user924

170

Java 8


public static Date atStartOfDay(Date date) {
    LocalDateTime localDateTime = dateToLocalDateTime(date);
    LocalDateTime startOfDay = localDateTime.with(LocalTime.MIN);
    return localDateTimeToDate(startOfDay);
}

public static Date atEndOfDay(Date date) {
    LocalDateTime localDateTime = dateToLocalDateTime(date);
    LocalDateTime endOfDay = localDateTime.with(LocalTime.MAX);
    return localDateTimeToDate(endOfDay);
}

private static LocalDateTime dateToLocalDateTime(Date date) {
    return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
}

private static Date localDateTimeToDate(LocalDateTime localDateTime) {
    return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
}

更新:これらの2つのメソッドをJavaユーティリティクラスに追加しまし

次の場所にあるMaven Central Repositoryにあります。

<dependency>
  <groupId>com.github.rkumsher</groupId>
  <artifactId>utils</artifactId>
  <version>1.3</version>
</dependency>

Java 7以前


Apache Commons

public static Date atEndOfDay(Date date) {
    return DateUtils.addMilliseconds(DateUtils.ceiling(date, Calendar.DATE), -1);
}

public static Date atStartOfDay(Date date) {
    return DateUtils.truncate(date, Calendar.DATE);
}

Apache Commonsなし

public Date atEndOfDay(Date date) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date);
    calendar.set(Calendar.HOUR_OF_DAY, 23);
    calendar.set(Calendar.MINUTE, 59);
    calendar.set(Calendar.SECOND, 59);
    calendar.set(Calendar.MILLISECOND, 999);
    return calendar.getTime();
}

public Date atStartOfDay(Date date) {
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(date);
    calendar.set(Calendar.HOUR_OF_DAY, 0);
    calendar.set(Calendar.MINUTE, 0);
    calendar.set(Calendar.SECOND, 0);
    calendar.set(Calendar.MILLISECOND, 0);
    return calendar.getTime();
}

5
Jodaのようなタイムライブラリが処理するタイムゾーンや夏時間などで発生するであろうエッジケースの問題のため、このアプローチはお勧めしません。
エドワードアンダーソン

7
私はで考えるgetStartOfDayべきである:LocalDateTime startOfDay = localDateTime.with(LocalTime.MIN);すなわちにLocalTime、ないのLocalDateTime。
Konstantin Kulagin 2016年

それらを考えないでくださいgetEndOfDay(Date date)getStartOfDay(Date date)Java 8セクションのメソッドは正確です。つまり、LocalDateTime.MAXと.MIN は、1日の始まりと終わりではなく、過去と未来の最も早い可能な日付に移動します。代わりに、開始には.atStartOfDay()を、終了には.plusDays(1).atStartOfDay()。minusNanos(1)をお勧めします。
Glen Mazza 2016年

メソッドlocalDateTimeToDate(LocalDateTime startOfDay)が例外をスローする
-java.lang.IllegalArgumentException

1
@GlenMazza Javaの8ソリューションを使用するにLocalTime .MAX、ないLocalDateTime.MAXを。解決策は正しいです。
Frederico Pantuzza

28

getEndOfDayでは、以下を追加できます。

calendar.set(Calendar.MILLISECOND, 999);

数学的にはですが、「翌日の始まりの前」と言う以外に、一日の終わりを指定することはできません。

したがって、と言う代わりに、と言うif(date >= getStartOfDay(today) && date <= getEndOfDay(today))必要がありますif(date >= getStartOfDay(today) && date < getStartOfDay(tomorrow))。それははるかに確かな定義です(そして、ミリ秒の精度について心配する必要はありません)。


1
この答えの後半が最善の方法です。このアプローチは「ハーフオープン」として知られています。私の答えでさらに説明します
バジルブルク2014

14

java.time

java.timeJava 8に組み込まれたフレームワークを使用します。

import java.time.LocalTime;
import java.time.LocalDateTime;

LocalDateTime now = LocalDateTime.now(); // 2015-11-19T19:42:19.224
// start of a day
now.with(LocalTime.MIN); // 2015-11-19T00:00
now.with(LocalTime.MIDNIGHT); // 2015-11-19T00:00
// end of a day
now.with(LocalTime.MAX); // 2015-11-19T23:59:59.999999999

3
この回答では、夏時間(DST)などの異常は無視されます。Local…クラスは、タイムゾーンの概念はありませんし、異常を調整します。たとえば、一部のタイムゾーンでは午前0時にDST切り替えが行われるため、1日の最初の時刻は、このコードによって生成され01:00:00.000:00:00.0値ではなく、時刻(午前1時)になります。ZonedDateTime代わりに使用してください。
バジルブルク

4

通過する代わりにjava8 java.time.ZonedDateTimeを使用して1日の始まりを見つける追加の方法はLocalDateTime、入力ZonedDateTimeをDAYSに切り捨てるだけです。

zonedDateTimeInstance.truncatedTo( ChronoUnit.DAYS );

2

Java 8では、次の1行のステートメントが機能しています。この例では、UTCタイムゾーンを使用しています。現在使用しているTimeZoneの変更を検討してください。

System.out.println(new Date());

final LocalDateTime endOfDay       = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);
final Date          endOfDayAsDate = Date.from(endOfDay.toInstant(ZoneOffset.UTC));

System.out.println(endOfDayAsDate);

final LocalDateTime startOfDay       = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
final Date          startOfDayAsDate = Date.from(startOfDay.toInstant(ZoneOffset.UTC));

System.out.println(startOfDayAsDate);

出力との時差がない場合。試してください:ZoneOffset.ofHours(0)


1
これは、既存の回答の価値をどのように高めますか?また、タイムゾーンの重要な問題を無視します。また、UTC定数を渡すことtoInstantは違法な構文であり、概念的には冗長です。
バジルブルク2017

@BasilBourque「これは既存の回答の価値を超えてどのように付加価値をもたらしますか?」これは、stackoverflowが機能する方法です。ところでこれは単なるテンプレートです。人々は自分が望む方法を変えることができます。UTCをtoInstantに渡すことは完全に合法であり、出力を調べることができます。
FıratKÜÇÜK

2

Java 8またはThreeTenABP

ZonedDateTime

ZonedDateTime curDate = ZonedDateTime.now();

public ZonedDateTime startOfDay() {
    return curDate
    .toLocalDate()
    .atStartOfDay()
    .atZone(curDate.getZone())
    .withEarlierOffsetAtOverlap();
}

public ZonedDateTime endOfDay() {

    ZonedDateTime startOfTomorrow =
        curDate
        .toLocalDate()
        .plusDays(1)
        .atStartOfDay()
        .atZone(curDate.getZone())
        .withEarlierOffsetAtOverlap();

    return startOfTomorrow.minusSeconds(1);
}

// based on https://stackoverflow.com/a/29145886/1658268

LocalDateTime

LocalDateTime curDate = LocalDateTime.now();

public LocalDateTime startOfDay() {
    return curDate.atStartOfDay();
}

public LocalDateTime endOfDay() {
    return startOfTomorrow.atTime(LocalTime.MAX);  //23:59:59.999999999;
}

// based on https://stackoverflow.com/a/36408726/1658268

誰かのお役に立てば幸いです。


「atTime」メソッドは、1日の終わりに定数「LocalTime.MAX」を使用する非常に優れたアプローチです。いいね!
アンダーソン氏

2

私はこのコードを試してみましたが、うまくいきます!

final ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
final ZonedDateTime startofDay =
    now.toLocalDate().atStartOfDay(ZoneOffset.UTC);
final ZonedDateTime endOfDay =
    now.toLocalDate().atTime(LocalTime.MAX).atZone(ZoneOffset.UTC);

あなたが持っていZonedDateTimeInstant、あなたの処分でこれまで使用する必要はありませんjava.sql.Timestamp。そのクラスは、現在java.timeクラスに取って代わられた恐ろしい古いレガシークラスの1つです。JDBC 4.2以降では、java.timeオブジェクトをデータベースと直接交換できます。レガシークラスとモダンクラスの混在は、私には意味がありません。
バジルブルク2018

1

フレームワークに依存しないもう1つのソリューションは次のとおりです。

static public Date getStartOfADay(Date day) {
    final long oneDayInMillis = 24 * 60 * 60 * 1000;
    return new Date(day.getTime() / oneDayInMillis * oneDayInMillis);
}

static public Date getEndOfADay(Date day) {
    final long oneDayInMillis = 24 * 60 * 60 * 1000;
    return new Date((day.getTime() / oneDayInMillis + 1) * oneDayInMillis - 1);
}

UTCベースの時間を返すことに注意してください


1
private Date getStartOfDay(Date date) {
    Calendar calendar = Calendar.getInstance();
    int year = calendar.get(Calendar.YEAR);
    int month = calendar.get(Calendar.MONTH);
    int day = calendar.get(Calendar.DATE);
    calendar.setTimeInMillis(0);
    calendar.set(year, month, day, 0, 0, 0);
    return calendar.getTime();
    }

private Date getEndOfDay(Date date) {
    Calendar calendar = Calendar.getInstance();
    int year = calendar.get(Calendar.YEAR);
    int month = calendar.get(Calendar.MONTH);
    int day = calendar.get(Calendar.DATE);
    calendar.setTimeInMillis(0);
    calendar.set(year, month, day, 23, 59, 59);
    return calendar.getTime();
    }

calendar.setTimeInMillis(0); ミリ秒までの精度を提供します


1
これらの恐ろしい日時のクラスは数年前、現代に取って代わられたjava.timeの JSR 310で定義されたクラス
バジルボーク

パッケージ@BasilBourque java.time唯一のAndroid N(API 26)以降で使用可能であるだけのJava 8に登場
ivan8m8

@ ivan8m8 java.time機能のほとんどは、ThreeTen-BackportライブラリのJava 6および7にバックポートされています。ThreeTenABPライブラリで初期のAndroid(<26)にさらに適合。
バジルブルク

@BasilBourqueですが、ThreeTenはAndroidで非常に非効率的なメカニズムを使用しています
ivan8m8

@あなたはの前身の考え方かもしれivan8m8 java.timeジョダタイム。ここでも、ThreeTenABPプロジェクトを参照してください。そのライブラリを使用しているAndroidでは、このような非効率性について聞いたことがありません。
バジルブルク

0

私はそれが少し遅いことを知っていますが、Java 8の場合、OffsetDateTime(TimeZone、Nanosecondsなどの多くの利点を提供します)を使用している場合は、次のコードを使用できます。

OffsetDateTime reallyEndOfDay = someDay.withHour(23).withMinute(59).withSecond(59).withNano(999999999);
// output: 2019-01-10T23:59:59.999999999Z

0

インスタント変数のタイプが必要で、タイムゾーンが常にすべての変更を妨げるため、すべてのソリューションにいくつかの不便がありました。ソリューションを組み合わせると、これが良いオプションであることがわかりました。

        LocalDate today = LocalDate.now();
        Instant startDate = Instant.parse(today.toString()+"T00:00:00Z");
        Instant endDate = Instant.parse(today.toString()+"T23:59:59Z");

結果として

        startDate = 2020-01-30T00:00:00Z
        endDate = 2020-01-30T23:59:59Z

お役に立てば幸いです


その日は実際には翌日の最初の瞬間の到着で終了します。コードは、1秒間の最後の1秒、つまり報告されない10億ナノ秒をスキップします。期間を定義するためのハーフオープンアプローチについては、私の回答を参照しください。
バジルブルク

日付時刻値を処理する方法として文字列を操作することは、一般的に不適切なアプローチです。この作業には、ダム文字列ではなくスマートオブジェクトを使用してください。java.timeのクラスを使用すると、文字列操作に頼ることなく、この問題のために必要なすべての機能を与えます。
バジルブルク

オブジェクトのペアとして1日の範囲を取得する方法を示すtl; drセクションをAnswerに追加したところInstantです。ヒント:クラスを使用するためにプロジェクトにThreeTen-Extraライブラリーを追加することに興味があるかもしれませんInterval
バジルブルク

0

私は最も簡単なものだと思います:

// Joda Time

DateTime dateTime=new DateTime(); 

StartOfDayMillis = dateTime.withMillis(System.currentTimeMillis()).withTimeAtStartOfDay().getMillis();
EndOfDayMillis = dateTime.withMillis(StartOfDayMillis).plusDays(1).minusSeconds(1).getMillis();

これらのミリは、Joda Timeの要件に従って、Calendar、Instant、またはLocalDateに変換できます。


-2
public static Date beginOfDay(Date date) {
    Calendar cal = Calendar.getInstance();
    cal.setTime(date);
    cal.set(Calendar.HOUR_OF_DAY, 0);
    cal.set(Calendar.MINUTE, 0);
    cal.set(Calendar.SECOND, 0);
    cal.set(Calendar.MILLISECOND, 0);

    return cal.getTime();
}

public static Date endOfDay(Date date) {
    Calendar cal = Calendar.getInstance();
    cal.setTime(date);
    cal.set(Calendar.HOUR_OF_DAY, 23);
    cal.set(Calendar.MINUTE, 59);
    cal.set(Calendar.SECOND, 59);
    cal.set(Calendar.MILLISECOND, 999);

    return cal.getTime();
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.