LocalDateTime, ZonedDateTime et Timestamp
j'ai une application SpringBoot. en utilisant Spring Initializer, embedded Tomcat, Thymeleaf template engine, et package comme un fichier JAR exécutable.
j'ai un objet de domaine avec 2 propriétés (initDate, endDate). Je veux créer 2 convertisseurs pour traiter avec mySQL DB
@Convert(converter = LocalDateTimeAttributeConverter.class)
private LocalDateTime initDate;
@Convert(converter = ZonedDateTimeAttributeConverter.class)
private ZonedDateTime endDate;
le convertisseur 1 (OK)
@Converter
public class LocalDateTimeAttributeConverter implements AttributeConverter<LocalDateTime, Timestamp> {
@Override
public Timestamp convertToDatabaseColumn(LocalDateTime localDateTime) {
return (localDateTime == null ? null : Timestamp.valueOf(localDateTime));
}
@Override
public LocalDateTime convertToEntityAttribute(Timestamp sqlTimestamp) {
return (sqlTimestamp == null ? null : sqlTimestamp.toLocalDateTime());
}
}
C'est celui que je veux créer
@Converter
public class ZonedDateTimeAttributeConverter implements AttributeConverter<ZonedDateTime, Timestamp> {
@Override
public Timestamp convertToDatabaseColumn(ZonedDateTime zoneDateTime) {
return (zoneDateTime == null ? null : Timestamp.valueOf(zoneDateTime));
}
@Override
public ZonedDateTime convertToEntityAttribute(Timestamp sqlTimestamp) {
return (sqlTimestamp == null ? null : sqlTimestamp.toZonedDateTime());
}
}
Mais je ne peux pas parce que j'ai 2 erreurs:
The method valueOf(String) in the type Timestamp is not applicable for the arguments (ZonedDateTime)
et la TimeStamp n'a pas la méthode toZonedDateTime()
et si je n'ajoute aucun convertisseur pour la ZonedDate, JPA crée une table avec le type varbinary(255)
2 réponses
Timestamp extends Date pour fournir une précision de nanoseconde. Ni Date ni Timestamp sont conçus pour se référer à un fuseau horaire ZoneDateTime.
si vous devez convertir ZonedDateTime -> Timestamp vous devrez supprimer les informations du fuseau horaire/offset. E. g.
LocalDateTime withoutTimezone = zoneDateTime.toLocalDateTime();
Timestamp timestamp = Timestamp.valueOf(withoutTimezone));
et pour la conversion Timestamp -> ZonedDateTime vous devez spécifier un décalage:
LocalDateTime withoutTimezone = sqlTimestamp.toLocalDateTime();
ZonedDateTime withTimezone = withoutTimezone.atZone(ZoneId.of("+03:00"));
ou fuseau horaire:
ZonedDateTime withTimezone = withoutTimezone.atZone(ZoneId.of("Europe/Paris"));
si votre intention est enregistrer ZonedDateTime variables dans la base de données et de préserver les différents délais qui y sont spécifiés, je recommande de concevoir votre base de données en conséquence. Suggestions:
- utilisez une colonne de type
DATETIMEpour enregistrer unLocalDateTimeetVARCHARenregistrer un fuseau horaire comme"Europe/Paris"etSMALLINTsauvegarde d'un offset en quelques minutes. - convertissez le
ZonedDateTimeStringet l'enregistrer dans unVARCHARcolonne"2017-05-16T14:12:48.983682+01:00[Europe/London]". Vous aurez alors à analyser lors de la lecture de l' la base de données.
Jon Skeet l'a déjà dit:
@Override
public Timestamp convertToDatabaseColumn(ZonedDateTime zoneDateTime) {
return zoneDateTime == null ? null : Timestamp.from(zoneDateTime.toInstant());
}
@Override
public ZonedDateTime convertToEntityAttribute(Timestamp sqlTimestamp) {
return sqlTimestamp == null ? null : sqlTimestamp.toInstant().atZone(ZoneId.systemDefault());
}
Jon a aussi posé la bonne question, quel fuseau horaire voulez-vous? Je l'ai deviné à ZoneId.systemDefault(). Évidemment un fuseau horaire différent donnera un résultat différent, donc j'espère que vous allez réfléchir à deux fois et sera en mesure de trouver le bon fuseau horaire de votre but.
PS j'ai réduit l'usage des parenthèses car je l'ai trouvé plus lisible avec moins. Vous pouvez rajouter si vous le souhaitez.