web-dev-qa-db-fra.com

Java 8 Java.time: Ajout de TemporalUnit dans Instant vs LocalDateTime

Je joue avec le nouveau package Java.time dans Java 8. J'ai une base de données héritée qui me donne Java.util.Date, Que je convertis en Instant .

Ce que j'essaie de faire est d'ajouter une période de temps basée sur un autre indicateur de base de données. Je pourrais ajouter des jours, des semaines, des mois ou des années. Je ne veux pas avoir à faire attention à ce que j'ajoute, et j'aimerais pouvoir ajouter plus d'options à l'avenir.

Ma première pensée a été Instant.plus(), mais cela me donne un UnsupportedTemporalTypeException pour les valeurs supérieures à un jour. Apparemment, Instant ne prend pas en charge les opérations sur de grandes unités de temps. Très bien, peu importe, LocalDateTime fait.

Cela me donne donc ce code:

private Date adjustDate(Date myDate, TemporalUnit unit){
    Instant instant = myDate.toInstant();
    LocalDateTime dateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
    dateTime = dateTime.plus(1, unit);
    Instant updatedInstant = dateTime.atZone(ZoneId.systemDefault()).toInstant();
    return new Date(dueInstant.toEpochMilli());
}

Maintenant, c'est la première fois que j'utilise la nouvelle API de temps, donc j'ai peut-être manqué quelque chose ici. Mais il me semble maladroit que je dois y aller:

Date --> Instant --> LocalDateTime --> do stuff--> Instant --> Date.

Même si je n'avais pas à utiliser la partie Date, je penserais quand même que c'était un peu gênant. Donc ma question est la suivante: est-ce que je fais cela complètement mal et quelle est la meilleure façon de le faire?


Edit: Développer la discussion dans les commentaires.

Je pense que j'ai maintenant une meilleure idée de la façon dont LocalDateTime et Instant jouent avec Java.util.Date et Java.sql.Timestamp. Merci tout le monde.

Maintenant, une considération plus pratique. Disons qu'un utilisateur m'envoie une date de n'importe où dans le monde, un fuseau horaire arbitraire. Ils m'envoient 2014-04-16T13:00:00 Que je peux analyser dans un LocalDateTime. Je convertis ensuite cela directement en Java.sql.Timestamp et persiste dans ma base de données.

Maintenant, sans rien faire d'autre, je tire mon Java.sql.timestamp de ma base de données, le convertis en LocalDateTime en utilisant timestamp.toLocalDateTime(). Tout bon. Ensuite, je renvoie cette valeur à mon utilisateur à l'aide du formatage ISO_DATE_TIME. Le résultat est 2014-04-16T09:00:00.

Je suppose que cette différence est due à un certain type de conversion implicite vers/depuis UTC. Je pense que mon fuseau horaire par défaut peut être appliqué à la valeur (EDT, UTC-4), ce qui expliquerait pourquoi le nombre est désactivé de 4 heures.

Nouvelle (s) question (s). Où se passe la conversion implicite de l'heure locale en UTC ici? Quelle est la meilleure façon de préserver les fuseaux horaires. Ne dois-je pas passer directement de l'heure locale sous forme de chaîne (2014-04-16T13: 00: 00) à LocalDateTime? Dois-je m'attendre à un fuseau horaire de la part de l'utilisateur?

23
jacobhyphenated

Je vais aller de l'avant et poster une réponse basée sur ma solution finale et une sorte de résumé de la très longue chaîne de commentaires.

Pour commencer, toute la chaîne de conversion de:

Date --> Instant --> LocalDateTime --> Do stuff --> Instant --> Date

Est nécessaire pour conserver les informations de fuseau horaire et toujours effectuer des opérations sur un objet de type Date qui connaît un calendrier et tout le contexte qu'il contient. Sinon, nous courons le risque de convertir implicitement au fuseau horaire local, et si nous essayons de le mettre dans un format de date lisible par l'homme, les heures peuvent avoir changé à cause de cela.

Par exemple, la méthode toLocalDateTime() de la classe Java.sql.Timestamp Est implicitement convertie en fuseau horaire par défaut. Ce n'était pas souhaitable pour mes besoins, mais ce n'est pas nécessairement un mauvais comportement. Il est cependant important d'en être conscient. C'est le problème de la conversion directe d'un ancien objet Java date date en un objet LocalDateTime. Comme les objets hérités sont généralement supposés être UTC, la conversion utilise le décalage de fuseau horaire local.

Supposons maintenant que notre programme prenne l'entrée de 2014-04-16T13:00:00 Et l'enregistre dans une base de données en tant que Java.sql.Timestamp.

//Parse string into local date. LocalDateTime has no timezone component
LocalDateTime time = LocalDateTime.parse("2014-04-16T13:00:00");

//Convert to Instant with no time zone offset
Instant instant = time.atZone(ZoneOffset.ofHours(0)).toInstant();

//Easy conversion from Instant to the Java.sql.Timestamp object
Timestamp timestamp = Timestamp.from(instant);

Nous prenons maintenant un horodatage et y ajoutons un certain nombre de jours:

Timestamp timestamp = ...

//Convert to LocalDateTime. Use no offset for timezone
LocalDateTime time = LocalDateTime.ofInstant(timestamp.toInstant(), ZoneOffset.ofHours(0));

//Add time. In this case, add one day.
time = time.plus(1, ChronoUnit.DAYS);

//Convert back to instant, again, no time zone offset.
Instant output = time.atZone(ZoneOffset.ofHours(0)).toInstant();

Timestamp savedTimestamp = Timestamp.from(output);

Maintenant, nous avons juste besoin de sortir en tant que chaîne lisible par l'homme au format ISO_LOCAL_DATE_TIME.

Timestamp timestamp = ....
LocalDateTime time = LocalDateTime.ofInstant(timestamp.toInstant(), ZoneOffset.ofHours(0));
String formatted = DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(time);
20
jacobhyphenated