I need to retrieve some data from the db and I need to check if the given date matches the one in the table, this is what I come up with:
@Transactional(readOnly = true)
public UtenteDTO getUtenteFromAsta(
String nomeGiocatore, String tempoInizio
) throws PlayerNotFoundException, UserNotFoundException, ParseException {
Giocatore giocatore = giocatoreService.getGiocatoreByNome(nomeGiocatore);
Date data = df.parse(tempoInizio);
LocalDateTime ldt = data.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
Optional<Utente> u = astaRepository.getUtenteFromAstaByGiocatore(giocatore, ldt);
if (u.isEmpty())
throw new UserNotFoundException();
Utente utente = u.get();
UtenteDTO uDTO = new UtenteDTO();
uDTO.setEmail(utente.getEmail());
uDTO.setNome(utente.getNome());
uDTO.setCognome(utente.getCognome());
uDTO.setRuolo(false);
return uDTO;
}
So I am converting the String
I get from the front end app, transform it in a java.util.Date
and then in a LocalDateTime
.
This is the query:
@Query("SELECT ua.utente FROM Asta a, UtenteAsta ua WHERE a.id = ua.id.astaId AND a.giocatore_nome = ?1 AND a.tempo_inizio = ?2")
Optional<Utente> getUtenteFromAstaByGiocatore(Giocatore giocatore, LocalDateTime tempoInizio);
Is this approach correct, or am i missing something?
Edit:
Format of the string from the front end and also stored in db:
2025-01-29 15:09:57.475000
I need to retrieve some data from the db and I need to check if the given date matches the one in the table, this is what I come up with:
@Transactional(readOnly = true)
public UtenteDTO getUtenteFromAsta(
String nomeGiocatore, String tempoInizio
) throws PlayerNotFoundException, UserNotFoundException, ParseException {
Giocatore giocatore = giocatoreService.getGiocatoreByNome(nomeGiocatore);
Date data = df.parse(tempoInizio);
LocalDateTime ldt = data.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
Optional<Utente> u = astaRepository.getUtenteFromAstaByGiocatore(giocatore, ldt);
if (u.isEmpty())
throw new UserNotFoundException();
Utente utente = u.get();
UtenteDTO uDTO = new UtenteDTO();
uDTO.setEmail(utente.getEmail());
uDTO.setNome(utente.getNome());
uDTO.setCognome(utente.getCognome());
uDTO.setRuolo(false);
return uDTO;
}
So I am converting the String
I get from the front end app, transform it in a java.util.Date
and then in a LocalDateTime
.
This is the query:
@Query("SELECT ua.utente FROM Asta a, UtenteAsta ua WHERE a.id = ua.id.astaId AND a.giocatore_nome = ?1 AND a.tempo_inizio = ?2")
Optional<Utente> getUtenteFromAstaByGiocatore(Giocatore giocatore, LocalDateTime tempoInizio);
Is this approach correct, or am i missing something?
Edit:
Format of the string from the front end and also stored in db:
2025-01-29 15:09:57.475000
To directly answer your Question, the Answer by deHaar is correct.
Alternatively, you could skip the formatter. Just replace the SPACE in the middle with a T
. The resulting string complies with the ISO 8601 standard formats used by default in java.time.
T
to separate date from time.Example code:
String text = input.replace ( " " , "T" ) ;
LocalDateTime ldt = LocalDateTime.parse ( text ) ;
But you have a much bigger problem.
You seem to be trying to capture a moment when something started. If so, you are using the wrong data types.
To represent a moment, a point on the timeline, in Postgres use a column of the type TIMESTAMP WITH TIME ZONE
.
Any time zone or offset in an input is used by Postgres to adjust to UTC (an offset of zero hours-minutes -seconds from the temporal meridian of UTC). Values in a TIMESTAMP WITH TIME ZONE
column are always stored in UTC (offset of zero) despite the misleading name of the type.
Beware: some tools may dynamically apply some time zone to retrieved values. But that is a distortion as Postgres always stores this type in UTC.
java.time.OffsetDateTime
In Java, represent a moment using Instant
, OffsotDateTime
, or ZonedDateTime
. For JDBC specifically, always use OffsetDateTime
for database values of type TIMESTAMP WITH TIME ZONE
.
OffsetDateTime now = OffsetDateTime.now( ZoneOffset.UTC ) ;
myPreparedStatement.setObject ( … , now ) ;
…
OffsetDateTime odt = myResultSet.getObject ( … , OffsetDateTime.class ) ;
In such code, the OffsetDateTime
instantiated by the JDBC driver will have an offset of zero. See the constant ZoneOffset.UTC
.
The Java class LocalDateTime
represents only a date with time-of-day while lacking the context of a time zone or offset. Such values are inherently ambiguous. Never try to use this class to represent a moment, a point on the timeline. Never call LocalDateTime.now
.
In Postgres, the matching type is TIMESTAMP WITHOUT TIME ZONE
.
Get rid of java.util.Date
, you don't need it in this case.
Instead of
Date data = df.parse(tempoInizio);
LocalDateTime ldt = data.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDateTime();
create a constant (or somehow else reusable) DateTimeFormatter
private static final DateTimeFormatter DTF = DateTimeFormatter.ofPattern(
"uuuu-MM-dd HH:mm:ss.SSSSSS",
Locale.ENGLISH
);
and use it to parse the tempoInizio
directly to a LocalDateTime
:
LocalDateTime ldt = LocalDateTime.parse(tempoInizio, DTF);
java.util.Date
when you get thetempoInizio
asString
? It's much better to use plainjava.time
if you don't have to create compatibility with legacy code. Use aDateTimeFormatter
to parsetempoInizio
, what's the expected format? Your use ofZoneId.systemDefault()
is a possible source for trouble when the code is expected to be deployed on different systems. – deHaar Commented Jan 30 at 9:57String
look? Like"2025-01-30T10:48:49"
? Please provide an example. – deHaar Commented Jan 30 at 9:59LocalDateTime.parse(String, DateTimeFormatter)
, which means you can skip parsing to aDate
and parse directly to aLocalDateTime
or any other (possibly more) suitable class fromjava.time
. If there are more suitable classes cannot be said without having seen an exampletempoInizio
value. – deHaar Commented Jan 30 at 10:07SimpleDateFormat
andDate
and go straight to heaven. – Anonymous Commented Jan 30 at 12:32Optional
instead of the 3 lines where you callu.isEmpty()
andu.get()
use just one line:Utente utente = u.orElseThrow(UserNotFoundException::new);
. – Anonymous Commented Jan 30 at 12:34