Información de fecha límite de Java

121

Tengo un objeto Fecha de Java que contiene información de fecha y hora. Quiero escribir un método que corte la información de tiempo, trunca las horas-minutos-segundos, por lo que solo me queda la fecha.

Entrada de ejemplo:

2008-01-01 13:15:00

Rendimiento esperado:

2008-01-01 00:00:00

¿Tienes un consejo? Intenté hacer algo como esto:

(timestamp / (24 * 60 * 60 * 1000)) * (24 * 60 * 60 * 1000)

pero tuve problemas con la zona horaria.

Marco
fuente

Respuestas:

178

La forma recomendada de manipular la fecha / hora es usar un Calendarobjeto:

Calendar cal = Calendar.getInstance(); // locale-specific
cal.setTime(dateObject);
cal.set(Calendar.HOUR_OF_DAY, 0);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
long time = cal.getTimeInMillis();
cletus
fuente
2
@cletus, ¿es posible eliminar la hora del objeto Date? Quiero una fecha pura como 2008-01-01 sin porción de tiempo.
Prem
2
No diría que se recomienda java.util.Calendar. Sun / Oracle suplantó esa clase en Java 8 con el nuevo paquete java.time. Usa eso o Joda-Time.
Basil Bourque
3
Ya no se recomienda Calendar. Consulte stackoverflow.com/a/28868089/40064 si está utilizando Java 8.
Wim Deblauwe
2
Para su información, las problemáticas clases de fecha y hora antiguas, como java.util.Calendarahora son heredadas , suplantadas por las clases java.time . Consulte el tutorial de Oracle .
Basil Bourque
148

¿Ha mirado el método de truncar DateUtils en Apache Commons Lang?

Date truncatedDate = DateUtils.truncate(new Date(), Calendar.DATE);

eliminará el elemento de tiempo.

A.M
fuente
9
Enlace actualizado de nuevo (¡vamos Apache! ¡Deja de mover tus documentos!)
Ogre Psalm33
6
Menos de este enfoque, la zona horaria en la que se realiza la operación de truncado es siempre la zona horaria local de la PC. Entonces, si trabaja con el calendario y las fechas en la zona horaria UTC o en cualquier zona horaria que no sea la zona horaria local, podría truncar la fecha no según lo previsto.
Cloud
como señaló @Cloud, el hecho de que no vea la instancia de Calendar aquí (no tengo control sobre ella) me pone un poco nervioso :)
Jaroslav Záruba
Ese método es sorprendentemente largo y tiene al menos dos correcciones de errores.
Pino
42

¿Has mirado a Joda ? Es una forma mucho más fácil e intuitiva de trabajar con fechas y horas. Por ejemplo, puede convertir trivialmente entre (digamos) objetos LocalDateTime y LocalDate .

p. ej. (para ilustrar la API)

LocalDate date = new LocalDateTime(milliseconds).toLocalDate()

Además, resuelve algunos problemas de seguridad de subprocesos con formateadores de fecha / hora y se recomienda encarecidamente para trabajar con cualquier problema de fecha / hora en Java.

Brian Agnew
fuente
12
Creo que sí. Es una recomendación para usar una biblioteca mejor en lugar de luchar con las clases java.util rotas (como lo demuestra la pregunta)
Brian Agnew
10
Por supuesto que se relaciona, Joda es LA biblioteca para la manipulación de fecha, hora y calendario en Java. Sería un incumplimiento del deber no recomendarlo.
Joel
1
El tema de seguridad del hilo es muy interesante, encontré esta publicación investigando un error de Oracle / Hibernate "IllegalArgumentException, sun.util.calendar.ZoneInfo.getOffset (ZoneInfo), oracle.jdbc.driver.DateCommonBinder.zoneOffset (OraclePreparedStatement)". Esto habla de otros problemas de Oracle que solo aparecen en foros de carga pesada.oracle.com/forums/thread.jspa?threadID=325576 Es realmente difícil incluso crear un objeto con milisegundos no válidos en código normal. Para mí, creo que me abatiré en mi hilo usando Joda, en lugar de dejar que Oracle use las cosas posiblemente sospechosas que no son de Joda.
Mark Bennett
4
Ojalá pudiera votar en contra de todos los que votaron a favor de esto. ¿Quiénes son todas estas personas que hacen +1 en una respuesta que ni siquiera intenta responder la pregunta? Una respuesta usando Joda sería más que bienvenida, pero esto no es así ...
Ryan
1
@Ryan ¿Cómo es que esta respuesta no es relevante? La Pregunta solicitó la fecha sin una hora del día. Esta respuesta presenta una clase cuyo único propósito es representar una fecha sin una hora del día.
Basil Bourque
35

Solo una actualización rápida a la luz de las clases java.time ahora integradas en Java 8 y posteriores.

LocalDateTimetiene un truncatedTométodo que aborda de manera efectiva lo que está hablando aquí:

LocalDateTime.now().truncatedTo(ChronoUnit.MINUTES)

Esto expresará la hora actual en minutos solamente:

2015-03-05T11:47

Puede usar un ChronoUnit(o TemporalUnit) menor que DAYS para ejecutar el truncamiento (ya que el truncamiento se aplica solo a la parte de tiempo de LocalDateTime, no a la parte de fecha).

Adam Berry
fuente
3
Solo puede usar ChronoUnits hasta DÍAS; las unidades más altas arrojarán una excepción.
assylias
26
Date date = new 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);
date = cal.getTime();
estar nervioso
fuente
22

Con Joda puedes conseguir fácilmente la fecha esperada.

A partir de la versión 2.7 (tal vez desde alguna versión anterior superior a 2.2), como señala un comentarista, toDateMidnightse ha desaprobado a favor o con el nombre apropiado withTimeAtStartOfDay(), lo que hace que sea conveniente

DateTime.now().withTimeAtStartOfDay()

posible.

Beneficio agregado de una API mucho más agradable.

Con versiones anteriores, puede hacer

new DateTime(new Date()).toDateMidnight().toDate()
h7r
fuente
6
Los métodos y clases relacionados con la medianoche en Joda-Time ahora están en desuso. Los desarrolladores recomiendan no usarlos. En su lugar, utilice el método withTimeAtStartOfDay.
Basil Bourque
Gracias por señalar. Se actualizó la respuesta para considerar esto.
h7r
También puede usar toLocalDatepara obtener un objeto que ni siquiera tiene un componente de tiempo.
Charles Wood
Para su información, el proyecto Joda-Time está ahora en modo de mantenimiento , y el equipo aconseja la migración a las clases java.time . Consulte el tutorial de Oracle .
Basil Bourque
8

Hice el truncamiento con la nueva API java8. Me enfrenté a una cosa extraña pero en general es truncada ...

Instant instant = date.toInstant();
instant = instant.truncatedTo(ChronoUnit.DAYS);
date = Date.from(instant);
Sergio Kosik
fuente
palabras clave: java.time, Java 8, LocalDateTime.truncatedTo ()
Alexander Taylor
1
Esta respuesta solo produce fechas para el contexto de la zona horaria UTC.
Basil Bourque
6

Use DateUtils de Apache, con truncar, así:

DateUtils.truncate(Calendar.getInstance().getTime(), Calendar.DATE);
Byron Marambio Troncoso
fuente
Esto se truncará en la zona horaria local, no en UTC que representa la fecha (getTime).
epox
6

Para marcas de tiempo:

timestamp -= timestamp % (24 * 60 * 60 * 1000)
Gan Quan
fuente
3
Hay un pequeño error en este código - el corchete izquierdo no está colocado correctamente, debe ser marca de tiempo - = marca de tiempo% (24 * 60 * 60 * 1000). Esto cortará la parte del tiempo. Y estoy seguro de que de esta manera es más suficiente que crear un objeto Caledar y jugar con sus métodos o incluso usar cualquier otra biblioteca de terceros
Stan
2
No, esta NO es la solución correcta, TENGA CUIDADO. Esto es lo mismo que redondear el valor de la fecha al piso, es decir, puede obtener el 28 de marzo para la marca de tiempo del 29 de marzo (en su localidad). Entonces, las soluciones basadas en calendario (DateUtils de Apache también usa Calendar) es la única forma
Drew
Esto tampoco funcionará cuando la hora cambie debido al horario de verano.
Alexandru Severin
5

Desde java.util.Date JavaDocs:

La clase Date representa un instante específico en el tiempo, con precisión de milisegundos.

y desde java.sql.Date JavaDocs:

Para cumplir con la definición de SQL DATE, los valores de milisegundos envueltos por una instancia java.sql.Date deben 'normalizarse' estableciendo las horas, minutos, segundos y milisegundos en cero en la zona horaria particular con la que está asociada la instancia. .

Entonces, el mejor enfoque es usar java.sql.Date si no necesita la parte de tiempo

java.util.Date utilDate = new java.util.Date();
java.sql.Date sqlDate = new java.sql.Date(System.currentTimeMillis());

y la salida es:

java.util.Date : Thu Apr 26 16:22:53 PST 2012
java.sql.Date  : 2012-04-26
Sunil Manheri
fuente
4

tl; dr

LocalDateTime.parse(                            // Lacking an offset or time zone, parse as a `LocalDateTime`. *Not* a specific moment in time.
    "2008-01-01 13:15:00".replace( " " , "T" )  // Alter input string to comply with ISO 8601 standard format.
)
.toLocalDate()                                  // Extract a date-only value.
.atStartOfDay(                                  // Do not assume the day starts at 00:00:00. Let class determine start-of-day.
    ZoneId.of( "Europe/Paris" )                 // Determining a specific start-of-day requires a time zone.
)                                               // Result is a `ZonedDateTime` object. At this point we have a specific moment in time, a point on the timeline.
.toString()                                     // Generate a String in standard ISO 8601 format, wisely extended to append the name of the time zone in square brackets.

2008-01-01T00: 00 + 01: 00 [Europa / París]

Para generar una cadena en su formato deseado, pase un DateTimeFormatter.

LocalDateTime.parse(                            // Lacking an offset or time zone, parse as a `LocalDateTime`. *Not* a specific moment in time.
    "2008-01-01 13:15:00".replace( " " , "T" )  // Alter input string to comply with ISO 8601 standard format.
)
.toLocalDate()                                  // Extract a date-only value.
.atStartOfDay(                                  // Do not assume the day starts at 00:00:00. Let class determine start-of-day.
    ZoneId.of( "Europe/Paris" )                 // Determining a specific start-of-day requires a time zone.
)                                               // Result is a `ZonedDateTime` object. At this point we have a specific moment in time, a point on the timeline.
.format(                                        // Generate a String representing the object’s value.
    DateTimeFormatter.ISO_LOCAL_DATE_TIME       // Built-in predefined formatter close to what you want. 
)
.replace( "T" , " " )                           // Replace the standard’s use of a 'T' in the middle with your desired SPACE character.

2008-01-01 00:00:00

Detalles

Otras respuestas son correctas, pero usan clases de fecha y hora antiguas ahora obsoletas por el marco java.time.

java.time

El marco java.time está integrado en Java 8 y versiones posteriores. Gran parte de la funcionalidad de java.time se ha actualizado a Java 6 y 7 ( ThreeTen-Backport ) y se ha adaptado aún más a Android ( ThreeTenABP ).

Primero, modifique la cadena de entrada para cumplir con la versión canónica del formato ISO 8601. Los formatos estándar ISO 8601 se utilizan de forma predeterminada en las clases java.time para analizar / generar cadenas que representan valores de fecha y hora. Necesitamos reemplazar ese ESPACIO en el medio con un T.

String input = "2008-01-01 13:15:00".replace( " " , "T" );  // → 2008-01-01T13:15:00

Ahora podemos analizarlo como un LocalDateTime, donde "Local" significa que no hay una localidad específica. La entrada carece de información de compensación de UTC o de zona horaria.

LocalDateTime ldt = LocalDateTime.parse( input );

ldt.toString ()… 2008-01-01T13: 15: 00

Si no le importa la hora del día ni la zona horaria, conviértalo a LocalDate.

LocalDate ld = ldt.toLocalDate();

ld.toString ()… 2008-01-01

Primer momento del día

Si, en cambio, desea que la hora del día se establezca en el primer momento del día, use una ZonedDateTimeclase y luego conviértala en un LocalDateobjeto para llamar a su atStartOfDaymétodo. Tenga en cuenta que el primer momento puede no ser el momento00:00:00 debido al horario de verano o quizás a otras anomalías.

La zona horaria es crucial porque en un momento dado la fecha varía en todo el mundo por zona. Por ejemplo, unos momentos después de la medianoche en París es un nuevo día para los parisinos, pero sigue siendo “ayer” en Montreal para los canadienses.

ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ldt.atZone( zoneId );
LocalDate ldFromZdt = zdt.toLocalDate();
ZonedDateTime zdtStartOfDay = ldFromZdt.atStartOfDay( zoneId );

zdtStartOfDay.toString ()… 2008-01-01T00: 00: 00-05: 00 [América / Montreal]

UTC

Para ver ese momento a través de la lente de la zona horaria UTC , extraiga un Instantobjeto. Tanto el ZonedDateTimecomo Instantrepresentarán el mismo momento en la línea de tiempo, pero aparecerán como dos tiempos de reloj de pared diferentes .

An Instantes la clase básica de bloques de construcción en java.time, siempre en UTC por definición. Use esta clase con frecuencia, ya que generalmente debería realizar su lógica comercial, almacenamiento de datos e intercambio de datos en UTC.

Instant instant = zdtStartOfDay.toInstant();

instant.toString ()… 2008-01-01T05: 00: 00Z

Vemos las 5 de la mañana en lugar de la medianoche. En formato estándar, al Zfinal es la abreviatura Zuluy significa "UTC".


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.

El proyecto Joda-Time , ahora en modo de mantenimiento , aconseja la migración a las clases java.time .

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 .

Puede intercambiar objetos java.time directamente con su base de datos. Utilice un controlador JDBC compatible con JDBC 4.2 o posterior. Sin necesidad de cuerdas, sin necesidad dejava.sql.* clases.

¿Dónde obtener las clases java.time?

  • Java SE 8 , Java SE 9 y posterior
    • Incorporado.
    • Parte de la API estándar de Java con una implementación integrada.
    • Java 9 agrega algunas características y correcciones menores.
  • Java SE 6 y Java SE 7
    • Gran parte de la funcionalidad de java.time está adaptada a Java 6 y 7 en ThreeTen-Backport .
  • Androide
    • Versiones posteriores de implementaciones de paquetes de Android de las clases java.time.
    • Para anteriormente Android (<26), el ThreeTenABP proyecto adapta ThreeTen-Backport (mencionado anteriormente). Consulte Cómo utilizar ThreeTenABP… .

El proyecto ThreeTen-Extra extiende java.time con clases adicionales. Este proyecto es un campo de pruebas para posibles adiciones futuras a java.time. Usted puede encontrar algunas clases útiles aquí, como Interval, YearWeek, YearQuarter, y más .

Albahaca Bourque
fuente
2

Utilice el método de la clase Calendarset() para establecer los campos HOUR_OF_DAY, MINUTE, SECONDy MILLISECONDen cero.

Michael Borgwardt
fuente
2

La pregunta es contradictoria. Solicita una fecha sin una hora del día, pero muestra un ejemplo con una hora de 00:00:00.

Joda-Time

ACTUALIZACIÓN: El proyecto Joda-Time está ahora en modo de mantenimiento , y el equipo aconseja la migración a las clases java.time . Vea mi otra respuesta para la solución java.time.

Si, en cambio, desea que la hora del día se establezca en el primer momento del día, use un DateTimeobjeto en la biblioteca Joda-Time y llame a su withTimeAtStartOfDaymétodo. Tenga en cuenta que el primer momento puede no ser el momento 00:00:00debido al horario de verano o quizás a otras anomalías.

Albahaca Bourque
fuente
Esta es la respuesta definitiva (asumiendo que uno puede y quiere usar Joda Time)
Clint Eastwood
2

Solo clear()campos redundantes.

Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.clear(Calendar.MINUTE);
calendar.clear(Calendar.SECOND);
calendar.clear(Calendar.MILLISECOND);
Date truncatedDate = calendar.getTime();

Desde Java 8, una mejor opción es usar el truncatedTométodo de LocalDateTime, por ejemplo:

LocalDateTime.now().truncatedTo(ChronoUnit.DAYS)
m190
fuente
Esto no funciona porque calendar.clear(Calendar.HOUR_OF_DAY);no despeja las horas. La solución de @cletus funciona bien.
Arcao
Lo siento, tienes razón, lo corregí, durante horas debes establecer el valor. En general, es casi de la misma manera cómo truncar la fecha ...
m190
1

Realmente me molestó que el nuevo constructor de calendario "mejorado" no tome un int durante milisegundos como el "horrible" antiguo Date one. Entonces me enojé mucho y escribí esto:

long stuffYou = startTime.getTimeInMillis() % 1000;
startTime.setTimeInMillis(startTime.getTimeInMillis() - stuffYou);

No usé la palabra "cosas" en ese momento, pero luego descubrí la felicidad de esto:

startTime.set(Calendar.MILLISECOND, 0);

Pero todavía estoy bastante enojado por eso.

Dave
fuente
1

Solucioné el problema de esta manera (en la zona ocho del este (hora de Beijing)):

private Date getTruncatedDate(Date d) {
    if (d == null) {
        return null;
    }
    long h = 60 * 60 * 1000L;
    long dateStamp = d.getTime() - (d.getTime() + 8 * h) % (24 * h);
    return new Date(dateStamp);
}

En primer lugar, debe tener claro qué es la marca de tiempo. La marca de tiempo es el total de milisegundos desde el 01 de enero a las 00:00:00 1970 de GMT (igual que UTC) o el jueves 01 de enero a las 08:00:00 CST de 1970 hasta ahora.

Recuerde: la marca de tiempo es independiente de la zona horaria.

Entonces obtienes el mismo resultado con la siguiente declaración en diferentes zonas horarias:

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

Y

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

imprime información horaria diferente en diferentes zonas horarias: si configura la zona horaria de su PC como UTC, obtendrá

Thu Jan 01 00:00:00 UTC 1970

Pero si configura la zona horaria como (UTC +8: 00) Beijing, Chongqing, HongKong, Urumq, obtiene:

Thu Jan 01 08:00:00 CST 1970

Java obtiene la marca de tiempo, luego muestra la información de fecha y hora de acuerdo con la zona horaria.

Para la introducción de cómo Java muestra la información de fecha y hora en diferentes zonas horarias, la forma de transferir la información de tiempo es fácil. Debe obtener la marca de tiempo y tener en cuenta la zona horaria cuando corte la información de tiempo. Luego, puede crear un nuevo objeto de fecha con el sello de tiempo de corte (podemos llamarlo sello de fecha), Java compensará la zona horaria cuando muestre la información de la fecha.

Al igual que en la zona este de ocho (hora de Pekín), la hora de Pekín es 8 horas antes que GMT, por lo que debe restar más 8 horas cuando realice la operación de módulo. Es decir, primero debe obtener la hora GMT, luego Java agregará 8 horas cuando se muestre el tiempo según la configuración de la zona horaria de su PC.


El problema de la zona horaria es oscuro y también me desconcierta durante mucho tiempo. Ahora lo dejo claro. La esperanza ayuda.


2018-01-04 El método siguiente también funciona.

private Date getTruncatedDate2(Date d) {
    Calendar cal = Calendar.getInstance(); // locale-specific
    cal.setTime(d);
    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();

}

Gelbert
fuente
Para su información, esta respuesta utiliza clases de fecha y hora antiguas problemáticas suplantadas hace años por las clases java.time .
Basil Bourque
1

Puede hacer esto para evitar problemas de zona horaria:

public static Date truncDate(Date date) {
    Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
    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();
}

Aunque el Dateobjeto Java es un valor de marca de tiempo, pero durante el truncado, se convertirá a la zona horaria local, por lo que obtendrá un valor sorprendente si espera un valor de la zona horaria UTC.

GMsoF
fuente
Las clases problemáticas Calendary Datese volvieron heredadas hace años con las clases java.time que implementan JSR 310 en Java 8 y versiones posteriores. Sugerir su uso en 2018 es un mal consejo.
Basil Bourque
0

Puede ser una respuesta tardía, pero aquí hay una forma de hacerlo en una línea sin usar ninguna biblioteca:

new SimpleDateFormat("yyyy-MM-dd").parse(new SimpleDateFormat("yyyy-MM-dd").format(YOUR_TIMESTAMP))
Alex
fuente
0

Con Joda-Time desde la versión 2.0 puede usar LocalDate.toDate().

Simplemente

// someDatetime is whatever java.util.Date you have.
Date someDay = new LocalDate(someDatetime).toDate();
lamusique
fuente
[A] Tu respuesta es redundante. Ya publicado por Brian Agnew . [B] Su código es incorrecto, ya que ignora la zona horaria, el mismo problema que se plantea en la pregunta.
Basil Bourque
Podría ser redundante, pero solo puse un ejemplo más simple aquí porque reconocí que una respuesta no debería estar en los comentarios. Sí, java.util.Date ignora TimeZone, pero la pregunta original fue con a Java Date objectlo que usé java.util.Date. Además, no explica cómo su zona horaria es problemática con java.util.Date (¿cuándo? ¿Dónde?) Aunque no puede evitar usar otro que no sea Date.
lamusique
0

Para todas las respuestas que usan Calendario, debe usarlo así en su lugar

public static Date truncateDate(Date date) {
    Calendar c = Calendar.getInstance();
    c.setTime(date);
    c.set(Calendar.HOUR_OF_DAY, c.getActualMinimum(Calendar.HOUR_OF_DAY));
    c.set(Calendar.MINUTE, c.getActualMinimum(Calendar.MINUTE));
    c.set(Calendar.SECOND, c.getActualMinimum(Calendar.SECOND));
    c.set(Calendar.MILLISECOND, c.getActualMinimum(Calendar.MILLISECOND));
    return c.getTime();
}

Pero yo prefiero esto:

public static Date truncateDate(Date date) {
    return new java.sql.Date(date.getTime());
}
Invitado
fuente
La java.sql.Dateclase pretende representar un valor de solo fecha, pero en realidad tiene una hora del día ajustada para la zona horaria UTC. Así que no es realmente una solución adecuada a la Pregunta.
Basil Bourque
0

Aquí hay una forma sencilla de obtener la fecha sin la hora si está utilizando Java 8+: Utilice el tipo java.time.LocalDate en lugar de Date.

LocalDate now = LocalDate.now();
 System.out.println(now.toString());

La salida:

2019-05-30

https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html

Amy Doxy
fuente
1
Usar LocalDatees una buena idea para la mayoría de los propósitos, pero no está claro cómo funcionará con la entrada de la pregunta 2008-01-01 13:15:00.
Ole VV