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.
Inicio del día
Obtenga la longitud completa de hoy como se ve en una zona horaria.
Utilizando el enfoque Half-Open, donde el principio es inclusivo mientras que el final es exclusivo . Este enfoque resuelve la falla en su código que no tiene en cuenta el último segundo del día.
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 [África / Túnez]
zdtStop.toString () = 2020-01-31T00: 00 + 01: 00 [África / Túnez]
Ver los mismos momentos en UTC.
Instant start = zdtStart.toInstant() ;
Instant stop = zdtStop.toInstant() ;
start.toString () = 2020-01-29T23: 00: 00Z
stop.toString () = 2020-01-30T23: 00: 00Z
Si desea el día completo de una fecha como se ve en UTC en lugar de en una zona horaria, 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
Estos OffsetDateTime objetos ya estarán en UTC, pero puede llamar toInstantsi los necesita, que siempre están en UTC por definición.
Instant start = odtStart.toInstant() ;
Instant stop = odtStop.toInstant() ;
start.toString () = 2020-01-29T06: 00: 00Z
stop.toString () = 2020-01-30T06: 00: 00Z
Sugerencia: Puede que le interese agregar la biblioteca ThreeTen-Extra a su proyecto para usar su Intervalclase para representar este par de Instantobjetos. Esto ofrece la clase métodos útiles para la comparación como abuts, overlaps, contains, y mucho más.
Interval interval = Interval.of( start , stop ) ;
interval.toString () = 2020-01-29T06: 00: 00Z / 2020-01-30T06: 00: 00Z
Medio abierto
La respuesta de mprivat es correcta. Su punto es no tratar de obtener el final del día, sino compararlo con "antes del comienzo del día siguiente". Su idea se conoce como el enfoque "Half-Open" donde un lapso de tiempo tiene un comienzo que es inclusivo mientras que el final es exclusivo .
- Los marcos actuales de fecha y hora de Java (java.util.Date/Calendar y Joda-Time ) usan milisegundos de la época . Pero en Java 8, las nuevas clases java.time. * JSR 310 utilizan una resolución de nanosegundos. Cualquier código que haya escrito basado en forzar la cuenta de milisegundos del último momento del día sería incorrecto si se cambiara a las nuevas clases.
- La comparación de datos de otras fuentes se vuelve defectuosa si emplean otras resoluciones. Por ejemplo, las bibliotecas de Unix suelen emplear segundos enteros y las bases de datos como Postgres resuelven la fecha y la hora en microsegundos.
- Algunos cambios en el horario de verano ocurren durante la medianoche, lo que podría confundir aún más las cosas.

Joda-Time 2.3 ofrece un método para este mismo propósito, para obtener un primer momento del día: withTimeAtStartOfDay(). Del mismo modo, en java.time, LocalDate::atStartOfDay.
Busque en StackOverflow "joda medio abierto" para ver más debates y ejemplos.
Vea esta publicación, Los intervalos de tiempo y otros rangos deben estar medio abiertos , por Bill Schneider.
Evite las clases de fecha y hora heredadas
Las clases java.util.Date y .Calendar son notoriamente problemáticas. Evítales.
Utilice clases de java.time . El marco java.time es el sucesor oficial de la exitosa biblioteca Joda-Time .
java.time
El marco java.time está integrado en Java 8 y versiones posteriores. Adaptado a Java 6 y 7 en el proyecto ThreeTen-Backport , más adaptado a Android en el proyecto ThreeTenABP .
Un Instantes un momento en la línea de tiempo en UTC con una resolución de nanosegundos .
Instant instant = Instant.now();
Aplique una zona horaria para obtener la hora del reloj de pared de alguna localidad.
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
Para obtener el primer momento del día, repase la LocalDateclase y su atStartOfDaymétodo.
ZonedDateTime zdtStart = zdt.toLocalDate().atStartOfDay( zoneId );
Usando el enfoque Half-Open, obtenga el primer momento del día siguiente.
ZonedDateTime zdtTomorrowStart = zdtStart.plusDays( 1 );

Actualmente, el marco java.time carece de una Intervalclase como se describe a continuación para Joda-Time. Sin embargo, el proyecto ThreeTen-Extra extiende java.time con clases adicionales. Este proyecto es el campo de pruebas para posibles adiciones futuras a java.time. Entre sus clases se encuentra Interval. Construye un Intervalpasando un par de Instantobjetos. Podemos extraer un Instantde nuestros ZonedDateTimeobjetos.
Interval today = Interval.of( zdtStart.toInstant() , zdtTomorrowStart.toInstant() );
Acerca de java.time
El marco java.time está integrado en Java 8 y versiones posteriores. Estas clases suplantar la vieja problemáticos heredados clases de fecha y hora como java.util.Date, Calendar, y SimpleDateFormat.
Para obtener más información, consulte el tutorial de Oracle . Y busque Stack Overflow para obtener muchos ejemplos y explicaciones. La especificación es JSR 310 .
El proyecto Joda-Time , ahora en modo de mantenimiento , aconseja la migración a las clases java.time .
Puede intercambiar objetos java.time directamente con su base de datos. Utilice un controlador JDBC compatible con JDBC 4.2 o posterior. No se necesitan cuerdas, no se necesitan java.sql.*clases. Hibernate 5 y JPA 2.2 admiten java.time .
¿Dónde obtener las clases java.time?
Joda-Time
ACTUALIZACIÓN: El proyecto Joda-Time ahora está en modo de mantenimiento y recomienda la migración a las clases java.time . Dejo esta sección intacta para la historia.
Joda-Time tiene tres clases para representar un espacio de tiempo de varias maneras: Interval, Period, y Duration. An Intervaltiene un comienzo y un final específicos en la línea de tiempo del Universo. Esto se ajusta a nuestra necesidad de representar "un día".
Llamamos al método en withTimeAtStartOfDaylugar de establecer la hora del día en ceros. Debido al horario de verano y otras anomalías, el primer momento del día puede no serlo 00:00:00.
Código de ejemplo usando 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 );
Si es necesario, puede convertirlo a java.util.Date.
java.util.Date date = todayStart.toDate();