6

I know there are several threads about this and i read them all but couldn't fix this for days. I might found a solution but it seems to be do dirty for me.

So as other users I have the same problem with the datePicker. For me it's the Angular Material Datepicker mat-datepicker

When I log the value I get the correct result:

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

but in the request it is

1999-12-23T23:00:00.000Z

What I tried yet:

I've added { provide: MAT_MOMENT_DATE_ADAPTER_OPTIONS, useValue: { useUtc: true } } to my component and to my app.module.ts. That does not make any difference for me.

My dirty solution (before sending the request):

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

When I am doing this the console logs:

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

and the request is right:

1997-12-24T00:00:00.000Z

But if somebody now would be from a different timezone like GMT-0100 this would again not work. How to fix this correctly?

I also change the adapter dynamically if it's necessary to know:

 this._adapter.setLocale(this.translateService.currentLang);
Akber Iqbal
  • 14,487
  • 12
  • 48
  • 70
CptDayDreamer
  • 1,526
  • 6
  • 25
  • 61
  • in your console.log... convert to UTC string... https://www.w3schools.com/jsref/jsref_toutcstring.asp – Akber Iqbal Jun 13 '19 at 10:48
  • @AkberIqbal okay I see how it looks in UTC now but how this does help me with my problem? It's still wrong when I don't fix it manually "dirty" – CptDayDreamer Jun 13 '19 at 10:55
  • 1
    you need to import `MatMomentDateModule` instead of `MatNativeDateModule` to work `{ provide: MAT_MOMENT_DATE_ADAPTER_OPTIONS, useValue: { useUtc: true } }` – karthik selvaraj Mar 15 '21 at 07:32

3 Answers3

3

In response to your own incorrect Answer

11 PM UTC = 00:00+01:00 the next day (same moment)

Wed Dec 24 1999 00:00:00 GMT+0100 (Mitteleuropäische Normalzeit) but in the request it is

1999-12-23T23:00:00.000Z

These two strings represent the same value. They are two ways of viewing the same moment. One is 11 PM at UTC, the other is an hour ahead of UTC, so it represents the first moment of the next day. Adding one hour to 11 PM on the 23rd in a 24-hour long day takes past the stroke of midnight, to the first moment of the 24th.

Parsing

Parse your input string as an Instant. The Z on the end means UTC and is pronounced “Zulu”. An Instant represent a moment in UTC, always UTC, by definition.

Your input string is in standard ISO 8601 format. The java.time classes use these standard formats by default when parsing/generating strings. So no need to specify a formatting pattern.

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

A moment (a date, a time-of-day, and an assigned time zone or offset-from-UTC) must be saved into a database column of a type akin to the standard-SQL type TIMESTAMP WITH TIME ZONE. A column of a type akin to TIMESTAMP WITHOUT TIME ZONE would be the wrong data type.

To write to your database, we need to switch from the basic building-block class of Instant to the more flexible OffsetDateTime class. Support for the OffsetDateTime class in JDBC drivers is required by JDBC 4.2 and later, while Instant support is optional.

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

Write to database via a PreparedStatement with a placeholder ?.

myPreparedStatement.setObject( … , odt ) ;

Retrieval.

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

This has been discussed many many times on Stack Overflow. So search to learn more. And please search Stack Overflow thoroughly before posting.

LocalDate

If your goal is to track just a date, and you don’t really care about time-of-day and time zone, you should be using LocalDate in Java and DATE column in standard SQL.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • With my structure it's not even possible to it like this. I just use my repo to save the given entity with all of my values. And this is just a date. In the end it works for me and do not see anything wrong and I will use it again if I don't work with preparedStatements which is not really needed with JPA and Spring – CptDayDreamer Jun 14 '19 at 00:02
  • So is this a better attempt or a right one: `this.dateOfBirth = new Timestamp(ZonedDateTime.of(dob, ZoneId.of("UTC")).toInstant().toEpochMilli());` – CptDayDreamer Jul 01 '19 at 08:37
2

The value which is picked and displayed is the same... it is the different formats of the date-time which show us a different result (where the date also changes)!!

For e.g:

  • (by default): 2019-06-11T19:00:00.000Z
  • is equal to (by UTCString): Tue, 11 Jun 2019 19:00:00 GMT
  • is equal to (by LocaleString): 6/12/2019, 12:00:00 AM
  • is equal to (by LocaleTimeString): 12:00:00 AM

We don't need to convert it, because the date object contains the same exact time.

This stackblitz will further show the difference a format can make on the surface but the date is the same underneath; If you have questions/comments, you can fork this stackblitz, make changes and post a comment against this answer, i'll try to clarify.

relevant TS:

import {Component} from '@angular/core';

/** @title Basic datepicker */
@Component({
  selector: 'datepicker-overview-example',
  templateUrl: 'datepicker-overview-example.html',
  styleUrls: ['datepicker-overview-example.css'],
})
export class DatepickerOverviewExample {
  planModel: any = {start_time: new Date() };
  constructor(){}

  dateChanged(evt){
    let selectedDate = new Date(evt);
    console.log("by default:", selectedDate);
    console.log("by UTCString:", selectedDate.toUTCString());
    console.log("by LocaleString:", selectedDate.toLocaleString());
    console.log("by LocaleTimeString:", selectedDate.toLocaleTimeString());
  }

}

relevant HTML:

<mat-form-field>
  <input matInput [matDatepicker]="picker" placeholder="Choose a date"
  [(ngModel)]="planModel.start_time"
  (ngModelChange)='dateChanged($event)'
  >
  <mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
  <mat-datepicker #picker></mat-datepicker>
</mat-form-field>

<hr/>

<p>By default: {{planModel.start_time}} </p>
<p>Medium date: {{planModel.start_time | date:'medium'}} </p>
<p>Short date: {{planModel.start_time | date:'short'}} </p>
<p>Short time: {{planModel.start_time | date: 'shortTime' }} </p>
<p>Medium time: {{planModel.start_time | date: 'mediumTime' }} </p>
<p>Long time: {{planModel.start_time | date: 'longTime' }} </p>
<p>Full time: {{planModel.start_time | date: 'fullTime' }} </p>
Akber Iqbal
  • 14,487
  • 12
  • 48
  • 70
  • It's not the same if I want to display it again in Angular. It's saved in my database with JPA as parsed date that will only be 1999-12-23 without timestamp or just 00:00 and so it's wrong when I want to display it. – CptDayDreamer Jun 13 '19 at 11:26
  • before saving in the database, if do: `let someVar = new Date(parsedDate)`... and then, insert this `someVar` in your database; you can fork my stackblitz to show me what you're experiencing... I or someone else in the community can then help easily – Akber Iqbal Jun 13 '19 at 11:32
  • Sorry I can't follow you. What is `parsedDate`? I mean how and what should I parse? I just want to save the dateOfBirth like I said in the question literally and be able to display it right again in the profile of the user. Maybe my problem is more in my Java backend parsing. https://stackblitz.com/edit/angular-mat-date-moment-utc?file=app%2Fmoment-utc-adapter.ts if you enter my date here you see what exactly what I get as JSON output for my backend. I format that to be able to save it to my database with a `DateTimeFormatter` and `LocalDate.parse` maybe I also need to use `LocalDateTime` – CptDayDreamer Jun 13 '19 at 11:38
  • 1
    even in your stackblitz, the Date object has the same date... the format on the surface can change... check this forked version: https://stackblitz.com/edit/angular-mat-date-moment-utc-k7wzth?file=app%2Fdatepicker-formats-example.html – Akber Iqbal Jun 13 '19 at 12:01
  • I think we actually talking about different things. I don't know what you want to tell me. I know that the date is always the same just with different timezones. In the end is this 1999-12-23T23:00:00.000Z what I get as request. When I'm saving this in my database and later retrieve it and display it (like I do) as longDate there stands 1999-12-23 what is wrong. I also found it that my lastUpdated date+time is wrong. I edited something minutes ago and it says: 13. June 2019 11:48:58 but it was 13:48:58 – CptDayDreamer Jun 13 '19 at 12:06
  • is it possible to get it in milis? – ScipioAfricanus Aug 16 '20 at 22:57
0

At the end the problem was in my backend and needed to understand what is going on.

To save the Date right into my Postgres Database:

     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());

Before I had

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

which was wrong. I just receive the request like it's given by Angular and Java saves it right into the database.

1999-12-23T23:00:00.000Z 

turns into

1997-12-24 00:00:00
CptDayDreamer
  • 1,526
  • 6
  • 25
  • 61
  • 1
    **Incorrect.** Your formatting pattern ignores the `Z` on the end, which means UTC and is pronounced “Zulu”. So you are needlessly discarding valuable information. And your input should be parsed as a `Instant` not a `LocalDateTime`. The `LocalDateTime` class **cannot represent a moment** as explained in its Javadoc. See corrected code example in [my Answer](https://stackoverflow.com/a/56588823/642706). – Basil Bourque Jun 13 '19 at 21:28