How to obtain the start time and end time of a day?

前端 未结 14 821
心在旅途
心在旅途 2020-11-27 10:23

How to obtain the start time and end time of a day?

code like this is not accurate:

 private Date getStartOfDay(Date date) {
    Calendar calendar =          


        
相关标签:
14条回答
  • 2020-11-27 10:35

    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.
    

    Start of day

    Get the full length of the today as seen in a time zone.

    Using Half-Open approach, where the beginning is inclusive while the ending is exclusive. This approach solves the flaw in your code that fails to account for the very last second of the day.

    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[Africa/Tunis]

    zdtStop.toString() = 2020-01-31T00:00+01:00[Africa/Tunis]

    See the same moments in UTC.

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

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

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

    If you want the entire day of a date as seen in UTC rather than in a time zone, use 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

    These OffsetDateTime objects will already be in UTC, but you can call toInstant if you need such objects which are always in UTC by definition.

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

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

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

    Tip: You may be interested in adding the ThreeTen-Extra library to your project to use its Interval class to represent this pair of Instant objects. This class offers useful methods for comparison such as abuts, overlaps, contains, and more.

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

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

    Half-Open

    The answer by mprivat is correct. His point is to not try to obtain end of a day, but rather compare to "before start of next day". His idea is known as the "Half-Open" approach where a span of time has a beginning that is inclusive while the ending is exclusive.

    • The current date-time frameworks of Java (java.util.Date/Calendar and Joda-Time) both use milliseconds from the epoch. But in Java 8, the new JSR 310 java.time.* classes use nanoseconds resolution. Any code you wrote based on forcing the milliseconds count of last moment of day would be incorrect if switched to the new classes.
    • Comparing data from other sources becomes faulty if they employ other resolutions. For example, Unix libraries typically employ whole seconds, and databases such as Postgres resolve date-time to microseconds.
    • Some Daylight Saving Time changes happen over midnight which might further confuse things.

    enter image description here

    Joda-Time 2.3 offers a method for this very purpose, to obtain first moment of the day: withTimeAtStartOfDay(). Similarly in java.time, LocalDate::atStartOfDay.

    Search StackOverflow for "joda half-open" to see more discussion and examples.

    See this post, Time intervals and other ranges should be half-open, by Bill Schneider.

    Avoid legacy date-time classes

    The java.util.Date and .Calendar classes are notoriously troublesome. Avoid them.

    Use java.time classes. The java.time framework is the official successor of the highly successful Joda-Time library.

    java.time

    The java.time framework is built into Java 8 and later. Back-ported to Java 6 & 7 in the ThreeTen-Backport project, further adapted to Android in the ThreeTenABP project.

    An Instant is a moment on the timeline in UTC with a resolution of nanoseconds.

    Instant instant = Instant.now();
    

    Apply a time zone to get the wall-clock time for some locality.

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

    To get the first moment of the day go through the LocalDate class and its atStartOfDay method.

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

    Using Half-Open approach, get first moment of following day.

    ZonedDateTime zdtTomorrowStart = zdtStart.plusDays( 1 );
    

    Currently the java.time framework lacks an Interval class as described below for Joda-Time. However, the ThreeTen-Extra project extends java.time with additional classes. This project is the proving ground for possible future additions to java.time. Among its classes is Interval. Construct an Interval by passing a pair of Instant objects. We can extract an Instant from our ZonedDateTime objects.

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

    About java.time

    The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date, Calendar, & SimpleDateFormat.

    To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.

    The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.

    You may exchange java.time objects directly with your database. Use a JDBC driver compliant with JDBC 4.2 or later. No need for strings, no need for java.sql.* classes. Hibernate 5 & JPA 2.2 support java.time.

    Where to obtain the java.time classes?

    • Java SE 8, Java SE 9, Java SE 10, Java SE 11, and later - Part of the standard Java API with a bundled implementation.
      • Java 9 brought some minor features and fixes.
    • Java SE 6 and Java SE 7
      • Most of the java.time functionality is back-ported to Java 6 & 7 in ThreeTen-Backport.
    • Android
      • Later versions of Android (26+) bundle implementations of the java.time classes.
      • For earlier Android (<26), a process known as API desugaring brings a subset of the java.time functionality not originally built into Android.
        • If the desugaring does not offer what you need, the ThreeTenABP project adapts ThreeTen-Backport (mentioned above) to Android. See How to use ThreeTenABP….

    Joda-Time

    UPDATE: The Joda-Time project is now in maintenance-mode, and advises migration to the java.time classes. I am leaving this section intact for history.

    Joda-Time has three classes to represent a span of time in various ways: Interval, Period, and Duration. An Interval has a specific beginning and ending on the timeline of the Universe. This fits our need to represent "a day".

    We call the method withTimeAtStartOfDay rather than set time of day to zeros. Because of Daylight Saving Time and other anomalies the first moment of the day may not be 00:00:00.

    Example code using 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 );
    

    If you must, you can convert to a java.util.Date.

    java.util.Date date = todayStart.toDate();
    
    0 讨论(0)
  • 2020-11-27 10:35
    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); gives you accuracy upto milliseconds

    0 讨论(0)
  • 2020-11-27 10:37

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

    Update: I've added these 2 methods to my Java Utility Classes here

    • DateUtils.atStartOfDay
    • DateUtils.atEndOfDay

    It is in the Maven Central Repository at:

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

    Java 7 and Earlier


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

    Without 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();
    }
    
    0 讨论(0)
  • 2020-11-27 10:40

    Additional way of finding start of day with java8 java.time.ZonedDateTime instead of going through LocalDateTime is simply truncating the input ZonedDateTime to DAYS:

    zonedDateTimeInstance.truncatedTo( ChronoUnit.DAYS );
    
    0 讨论(0)
  • 2020-11-27 10:41

    Java 8 or 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
    

    I hope that helps someone.

    0 讨论(0)
  • 2020-11-27 10:43
    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();
    }
    
    0 讨论(0)
提交回复
热议问题