Change only hours, minutes, seconds of a date object

I need to change only the hours of an object of Type Date, preserving the day, the month and the year.

I tried with the setMinutes(), setSeconds(), setHours(), but "is deprecated" appears.

31/03/2019 13:23:14.958

31/03/2019 13:23:14.958

31/03/2019 23:59:59.999

Author: hkotsubo, 2019-03-31

2 answers

First we have to understand what the class really is java.util.Date it means. Despite the name, it does not represent a date. At least not in the sense of representing a single value of day, month, year, hour, minute and second.

Actually a Date represents a timestamp. The only value it has is a long: a number that represents the amount of milliseconds since the Unix Epoch (Unix Epoch being January 1, 1970, at midnight on UTC ). The timestamp value can be obtained by the method getTime().

What can be confusing when using Date is that when printing it, the JVM's timezone default is used to translate the timestamp to a date and time. A classic test is to print the Date multiple times by changing the timezone default :

Date d = new Date();
TimeZone.setDefault(TimeZone.getTimeZone("America/Sao_Paulo"));
System.out.println(d.getTime() + "=" + d);
TimeZone.setDefault(TimeZone.getTimeZone("America/Los_Angeles"));
System.out.println(d.getTime() + "=" + d);
TimeZone.setDefault(TimeZone.getTimeZone("Asia/Tokyo"));
System.out.println(d.getTime() + "=" + d);

The output is:

1554052464000=Sun Mar 31 14:14:24 BRT 2019
1554052464000 = Sun Mar 31 10:14: 24 PDT 2019
1554052464000 = Mon Apr 01 02: 14: 24 JST 2019

Notice that the timestamp value (returned by getTime()) does not change, i.e. Date is always the same. But when printing it, it uses timezone default to get the date and time values. See that in São Paulo the day is 31/03/2019 and the time is 14:14:24. But in Los Angeles the time is 10:14, and in Japan it is already 01/04 at 02: 14 in the morning.

This is the idea of timestamp: its value is the same all over the world (No matter where is the computer nor which timezone is configured, everyone would get 1554052464000 as the current timestamp if they ran this code at the same instant as me). What changes are the corresponding date and time values, as these vary from one time zone to another.

So what a java.util.Date represents is the timestamp value. Any value derived from it (day, month, year, hour, minute, second) depends on the timezone default of the JVM. But these values not they're part of Date. When you use the getters and setters, it checks the JVM's timezone default and timestamp, and checks what the respective field value is under these conditions.


That said, from the output you reported, it looks like you're actually using a java.sql.Timestamp. And since this is a subclass of java.util.Date, it has the same characteristics: all it has is the timestamp value, and if you print it, the date values and time also changes according to the timezone default of the JVM.

Either way, to manipulate the fields, you can use a java.util.Calendar:

Date d = ...
Timestamp t = ...

// criar o Calendar
Calendar cal = Calendar.getInstance();

// setar o timestamp
cal.setTimeInMillis(t.getTime());
// se for usar o timestamp de Date, faça
// cal.setTimeInMillis(d.getTime()); ou simplesmente cal.setTime(d);

// mudar o horário para 23:59:59.999
cal.set(Calendar.HOUR_OF_DAY, 23);
cal.set(Calendar.MINUTE, 59);
cal.set(Calendar.SECOND, 59);
cal.set(Calendar.MILLISECOND, 999);

// criar novas instâncias com o horário atualizado
t = new Timestamp(cal.getTimeInMillis());
d = cal.getTime();

Always remembering that Calendar will also work with the JVM's timezone default. If you want the date and time in a different timezone, you should pass it in method getInstance():

// usar timezone do Japão em vez do default da JVM
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("Asia/Tokyo"));

The timezone used will generate a different timestamp value, since 23: 59 occurs at a different instant in each part of the world. Most of the time people simply use the default without thinking because all servers are configured with the same timezone and everything "works". But it is important to keep in mind that the chosen timezone can influence the final values.


Another detail is that dates have no format (as already said here, here and here .)

The output you see (31/03/2019 13:23:14.958) is just one form that classes have chosen to represent their values. In the case of java.sql.Timestamp, by default it converts the timestamp to the JVM's timezone default, gets the respective date and time values, and shows in this format. But that's not to say that the date is in that format.


Java > = 8

From Java 8 there is the API java.time, much better than legacy classes(Date, Calendar, etc).

In this API, you can choose the most suitable type for working with your dates. In this case, you can for example map the java.sql.Timestamp to a java.time.LocalDateTime:

Timestamp t = ...
LocalDateTime ldt = t
    // converte para LocalDateTime (usa o timezone default da JVM)
    .toLocalDateTime()
    // seta o horário para 23:59:59.999999999
    .with(LocalTime.MAX);
// converte de volta para Timestamp
t = Timestamp.valueOf(ldt);

Door java.time.LocalTime to set the time, and the constant MAX which corresponds to 23: 59: 59.999999999.

Conversions to / from LocalDateTime use the JVM's timezone default to know the date and time values corresponding to the timestamp. But if you want to use a specific timezone, you can convert the classes to java.time.Instant (the class that represents the concept of timestamp), and then use a java.time.ZoneId (the class that represents a timezone).

Date d = ...
ZoneId zone = ZoneId.of("America/Sao_Paulo"); // timezone que quero usar na conversão
ZonedDateTime zdt = d.toInstant() // obtém o Instant (representa o timestamp)
    // converte para um timezone
    .atZone(zone)
    // obtém a data e o início do dia seguinte
    .toLocalDate().plusDays(1).atStartOfDay(zone)
    // subtrai 1 nanossegundo (para obter o último instante do dia anterior)
    .minusNanos(1);
// converte de volta para Date
d = Date.from(zdt.toInstant());
// este código também funciona para java.sql.Timestamp
// pois esta classe também possui os métodos toInstant() e from(Instant)

The result is a java.time.ZonedDateTime. The difference is that this class considers the timezone, and LocalDateTime does not.

You're probably wondering why all this complication of getting the start of the next day and subtracting 1 nanosecond. This happens because timezones have Daylight Saving Time and if I simply set the time to 23: 59: 59.999, not always the result will be the last moment of that day. In the example before this I was able to do this because LocalDateTime has no information about the timezone and so does not suffer interference from Daylight Saving Time.

For example, when Summer Time ends in Brazil, at midnight the clock is delayed 1 hour, back to 23: 00. That is, minutes between 23:00 and 23:59 occur twice, once in daylight saving time and once in normal time. If I set the time to 23:59 manually, which of these occurrences will be set, that of Daylight Saving Time or normal time? To avoid these problems, the above method is more guaranteed, since I catch the last moment of the day, in the timezone in question, regardless of whether it has Daylight Saving Time or not.


If the database you are using has a driver compatible with JDBC 4.2, you can work directly with the java.time classes, using the setObject methods of the class java.sql.PreparedStatement and getObject from Class java.sql.ResultSet. An example with LocalDateTime would be:

LocalDateTime dt = ...
PreparedStatement ps = ...
// seta o java.time.LocalDateTime
ps.setObject(1, dt);

// obter o LocalDateTime do banco
ResultSet rs = ...
LocalDateTime dt = rs.getObject(1, LocalDateTime.class);
...

Just remembering that not all databases support all types of java.time. See the documentation and see which classes are mapped to which types in the database.

 3
Author: hkotsubo, 2019-03-31 23:57:46
// data: Data e hora original
// horas: Horas que se deseja asicionar
public Date adicionaHoras(Date data, int horas) {
   Calendar calendario = Calendar.getInstance(); // Cria um objeto de data
   calendario.setTime(data); // Inicia o objeto com a data original
   calendario.add(Calendar.HOUR_OF_DAY, horas); // Adiciona as horas necessárias

   return calendario.getTime(); // Retorna a hora formatada
}
 0
Author: Elihofni Lima, 2019-03-31 17:16:17