web-dev-qa-db-fra.com

Angular: problème d'analyse de date UTC 1 jour avant

Je sais qu'il y a plusieurs discussions à ce sujet et je les ai toutes lues mais je n'ai pas pu résoudre ce problème pendant des jours. Je pourrais trouver une solution, mais elle semble être sale pour moi.

Donc, comme d'autres utilisateurs, j'ai le même problème avec le datePicker. Pour moi, c'est le Angular Material Datepicker mat-datepicker

Lorsque j'enregistre la valeur, j'obtiens le résultat correct:

Wed Dec 24 1999 00:00:00 GMT+0100 (Mitteleuropäische Normalzeit)

mais dans la demande c'est

1999-12-23T23:00:00.000Z

Ce que j'ai encore essayé:

J'ai ajouté { provide: MAT_MOMENT_DATE_ADAPTER_OPTIONS, useValue: { useUtc: true } } à mon component et à mon app.module.ts. Cela ne fait aucune différence pour moi.

Ma sale solution (avant d'envoyer la demande):

 let newDate= new Date(this.personalForm.get("dateOfBirth").value);
 newDate.setMinutes(newDate.getMinutes() - newDate.getTimezoneOffset());

Lorsque je fais cela, les journaux de la console:

Wed Dec 24 1999 01:00:00 GMT+0100 (Mitteleuropäische Normalzeit)

et la demande est juste:

1997-12-24T00:00:00.000Z

Mais si quelqu'un venait maintenant d'un fuseau horaire différent comme GMT-0100, cela ne fonctionnerait plus. Comment résoudre ce problème correctement?

Je change également l'adaptateur dynamiquement s'il est nécessaire de savoir:

 this._adapter.setLocale(this.translateService.currentLang);
3
CptDayDreamer

En réponse à votre propre réponse incorrecte

11 PM UTC = 00: 00 + 01: 00 le lendemain (même moment)

Mer 24 déc 1999 00:00:00 GMT + 0100 (Mitteleuropäische Normalzeit) mais dans la demande il est

1999-12-23T23: 00: 00.000Z

Ces deux chaînes représentent la même valeur. Ce sont deux façons de voir le même moment. L'un est 11 PM à UTC, l'autre a une heure d'avance sur UTC, il représente donc le premier moment du lendemain. Ajouter une heure à 11 PM = le 23 d'une journée de 24 heures passe le coup de minuit au premier moment du 24.

Analyse

Analysez votre chaîne d'entrée en tant que Instant. Le Z à la fin signifie UTC et se prononce "Zulu". Un Instant représente un moment en UTC, toujours UTC, par définition.

Votre chaîne d'entrée est au format standard ISO 8601. Les classes Java.time utilisent par défaut ces formats standard lors de l'analyse/génération de chaînes. Il n'est donc pas nécessaire de spécifier un modèle de formatage.

Instant instant = Instant.parse( "1999-12-23T23:00:00.000Z" ) ;

Un moment (une date, une heure et un fuseau horaire ou décalage par rapport à UTC) doit être enregistré dans une colonne de base de données d'un type semblable au type SQL standard TIMESTAMP WITH TIME ZONE. Une colonne d'un type semblable à TIMESTAMP WITHOUT TIME ZONE serait le type de données incorrect .

Pour écrire dans votre base de données, nous devons passer de la classe de base du bloc de construction de Instant à la classe plus flexible OffsetDateTime. La prise en charge de la classe OffsetDateTime dans les pilotes JDBC est requise par JDBC 4.2 et versions ultérieures, tandis que la prise en charge Instant est facultative.

OffsetDateTime odt = instant.atOffset( ZoneOffset.UTC ) ; 

Ecrire dans la base de données via un PreparedStatement avec un espace réservé ?.

myPreparedStatement.setObject( … , odt ) ;

Récupération.

OffsetDateTime odt = myResultSet.getObject( … , OffsetDateTime.class ) ;

Cela a été discuté plusieurs fois sur Stack Overflow. Recherchez donc pour en savoir plus. Et veuillez rechercher à fond Stack Overflow avant de poster.

LocalDate

Si votre objectif est de suivre uniquement une date et que vous ne vous souciez pas vraiment de l'heure et du fuseau horaire, vous devez utiliser LocalDate in Java and DATE colonne en SQL standard.

0
Basil Bourque

À la fin, le problème était dans mon backend et devait comprendre ce qui se passait.

Pour enregistrer la date directement dans ma base de données Postgres:

     DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH);
     LocalDateTime lu = LocalDateTime.parse(lastUpdated, inputFormatter);
     LocalDateTime dob = LocalDateTime.parse(dateOfBirth, inputFormatter);

     this.dateOfBirth = Date.from(dob.atZone(ZoneOffset.UTC).toInstant());
     this.lastUpdated = Date.from(lu.atZone(ZoneOffset.UTC).toInstant());

Avant je

 this.dateOfBirth = Date.from(dob.atZone(ZoneId.systemDefault()).toInstant()); 

ce qui était faux. Je viens de recevoir la demande telle qu'elle est donnée par Angular et Java l'enregistre directement dans la base de données.

1999-12-23T23:00:00.000Z 

se transforme en

1997-12-24 00:00:00
0
CptDayDreamer

Haha, ma sale solution pour cela était cette méthode, qui fait son travail pour ce qu'elle est, car je voulais juste renvoyer la date sous forme de chaîne sans aucune autre information, qui serait mappée à un objet datetime dans le backend et enregistrée ...

public setDate(date: any): string {
    const chosenDate = new Date(date);
    return `${chosenDate.getMonth() + 1}/${chosenDate.getDate()}/${chosenDate.getFullYear()}`;
}

puis définissez la valeur de l'objet dans le html

<mat-form-field appearance="outline">
    <mat-label>Birthdate</mat-label>
    <input matInput [matDatepicker]="birthdatePicker" placeholder="Birthdate" 
        (dateChange)="user.birthdate = setDate($event.value)">
    <mat-datepicker-toggle matSuffix [for]="birthdatePicker"></mat-datepicker-toggle>
    <mat-datepicker #birthdatePicker></mat-datepicker>
    <mat-error>error</mat-error>
</mat-form-field>
0
Noob