ngx-bootstrap icon indicating copy to clipboard operation
ngx-bootstrap copied to clipboard

DatePicker timezone problem

Open callawey opened this issue 7 years ago • 51 comments

[bsConfig]="{ dateInputFormat: 'YYYY-MM-DD' }"

when this format is used, i may expect the same entry come back regardless of client. because we are selecting a date not datetime. But its considering it as datetime and puts timezone info to it.

So, for example, 2018-01-30 00:00:00 becomes 2018-01-29 if your timezone is +

There is no config option to set timezone or any thing that allows to retreive the input directly.

If there is a way, i am not aware but per the source code, i dont see any option that lets this.

callawey avatar Jan 31 '18 19:01 callawey

+1

sasastojkovicgfi avatar Feb 08 '18 12:02 sasastojkovicgfi

Help us!!!!

beppe1992 avatar Feb 14 '18 14:02 beppe1992

Same problem!

bhavesh-neo avatar Feb 20 '18 19:02 bhavesh-neo

+1

shyamal890 avatar Mar 13 '18 18:03 shyamal890

The problem shows up when the Date is converted to JSON. I solved the problem overriding Date.prototype.toJSON method returning Date in string format yyyy-MM-dd'T'hh:mm:ss.

This work for me.

beppe1992 avatar Mar 13 '18 18:03 beppe1992

Same Problem Here. Can you post some example code? How many years this issue exists?? Isn't this a bit unacceptable??

mixtou avatar Mar 16 '18 11:03 mixtou

This is my code

`Date.prototype.toJSON = function(){ // this method transform a Date in a string (format "yyyy-MM-dd'T'HH:mm:ss") return DateService.formatDate(this); };

Server side I accept date in "yyyy-MM-dd'T'HH:mm:ss" format

`

beppe1992 avatar Mar 16 '18 12:03 beppe1992

Would https://github.com/valor-software/ngx-bootstrap/issues/3440 solve this issue?

fralewsmi avatar May 01 '18 06:05 fralewsmi

This is actually expected date behaviour in js. And yes js Dates is a pain. Just call .toUTCstring() and you should see the same date

valorkin avatar May 14 '18 23:05 valorkin

@valorkin where would you call .toUTCstring()? the date itself gets sent appropriately, but someone at +9 timezone sees a different date in the front-end than what we're storing. Maybe there's a way for the datepicker to just care about the date?

Edit: fwiw, we're working around this by just making sure that the date before getting shown in the datepicker doesn't have any timezone info on it (so that Date renders it in the client's timezone) and then chopping off the time information before sending it back to the server.

simonv3 avatar Sep 20 '18 19:09 simonv3

why is this closed... still a problem and no solution!?

roscoedeemarutam avatar Apr 16 '19 10:04 roscoedeemarutam

I had the same problem. Just look at this examples, maybe it will be useful for someone. Timezon local maching is GMT+3. It is the oportunity to pase time to your timezone without changing time (dateTime2 in example). it is force timezone converting)

var dateStr = '2018-01-30 04:00:00';
var dateTime1 = moment(dateStr).toDate();
var dateTime2 = moment.tz(dateStr, 'YYYY-MM-DDTHH:mm:ss[Z]', true, "ETC/UTC").toDate();
console.log(dateTime1);
//Tue Jan 30 2018 04:00:00 GMT+0300 (GMT+03:00)
console.log(dateTime2);
//Tue Jan 30 2018 07:00:00 GMT+0300 (GMT+03:00)

U need to include moment.js and moment-timezon.js

viktor-mezhenskyi avatar May 09 '19 12:05 viktor-mezhenskyi

@valorkin where would you call .toUTCstring()? the date itself gets sent appropriately, but someone at +9 timezone sees a different date in the front-end than what we're storing. Maybe there's a way for the datepicker to just care about the date?

Edit: fwiw, we're working around this by just making sure that the date before getting shown in the datepicker doesn't have any timezone info on it (so that Date renders it in the client's timezone) and then chopping off the time information before sending it back to the server.

Was that workaround then found ?

waldof123 avatar May 09 '19 13:05 waldof123

Is there workaround for timezone issue? I want to have datepicker for UTC Date selection.

imben1109 avatar Jun 29 '19 16:06 imben1109

https://medium.com/self-learning/ngx-datepicker-utc-datepicker-design-77e33789e9d7

I have tried a UTC Datepicker which is a wrapper to original datepicker. The wrapper try to convert the input with a timezone offset procession.

image

import { Component, OnInit, forwardRef, ChangeDetectorRef } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';

@Component({
  selector: 'app-utc-datepicker',
  templateUrl: './utc-datepicker.component.html',
  styleUrls: ['./utc-datepicker.component.css'],
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => UtcDatepickerComponent),
    multi: true
  }]
})
export class UtcDatepickerComponent implements OnInit, ControlValueAccessor{

  value: any;

  constructor() { }

  onChange: (value: any) => void;

  ngOnInit() {
  }

  bsValueChange(val){
    setTimeout(()=>{
      this.value = val;
      if (val instanceof Date){
        this.onChange(new Date(val.getTime() - val.getTimezoneOffset() * 60 * 1000));  
      }else {
        this.onChange(val);
      }
    });
  }

  writeValue(val: any): void {
    if (val){
      if (val instanceof Date){
        this.value = new Date(val.getTime() + val.getTimezoneOffset() * 60 * 1000);
      }else {
        this.value = val;
      }
    }
  }

  registerOnChange(fn: (value: any) => void): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
  }
  

}

Remark for this solution All time input should be converted to a time with a specific processing of timezone offset such as min day, max day handling.

imben1109 avatar Jun 30 '19 03:06 imben1109

Really?!? We need to a special custom function to handle such a simple problem!? This is so stupid!

gp187 avatar Jul 20 '19 08:07 gp187

Try the below function OnInit. Below function processes the timezone offset upon loading the record from db and you need to also use same when saving the record (or on date change event). Effect is saving and displaying the "Date chosen" by the user and not "Date chosen minus 1 day".

const dd = new Date(this.userEditForm.controls['dateOfDeployment'].value); dd.setHours(dd.getHours() - dd.getTimezoneOffset() / 60); this.userEditForm.patchValue({ dateOfDeployment: dd }); 2 1

Hojekunle avatar Jul 24 '19 07:07 Hojekunle

@imben1109 @Hojekunle @waldof123 @viktor-mezhenskyi @roscoedeemarutam

Please, try this version npm i ngx-bootstrap-ci@d94457108f5fd2527b7cab501e1cc0271a087a08 then rename ngx-bootstrap-ci => ngx-bootstrap inside node_modules

Domainv avatar Aug 21 '19 13:08 Domainv

@Domainv I took a look on ngx-bootstrap-ci@d94457108f5fd2527b7cab501e1cc0271a087a08. It should be my expected result.

I want to try on this version by npm install ngx-bootstrap-ci@d94457108f5fd2527b7cab501e1cc0271a087a08

npm ERR! code ETARGET
npm ERR! notarget No matching version found for ngx-bootstrap-ci@d94457108f5fd2527b7cab501e1cc0271a087a08
npm ERR! notarget In most cases you or one of your dependencies are requesting
npm ERR! notarget a package version that doesn't exist.

npm ERR! A complete log of this run can be found in:
npm ERR!     C:\Users\user\AppData\Roaming\npm-cache\_logs\2019-08-26T16_11_53_312Z-debug.log

I have another issue for custom timezone which I want to select a timezone which is different from the timezone browser timezone offset.

e.g. Browser Timezone: +7 Custom Timezone: +8 If I select a date of 27/08/2019. The expected UTC time is 26/08/2019 16:00

The below is my workaround solution. Could you take a look?

import { CustomTimezoneDatepickerService } from './custom-timezone-datepicker.service';
import { Component, OnInit, forwardRef, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
import { Subject } from 'rxjs';
import { takeUntil, tap } from 'rxjs/operators';

@Component({
  selector: 'app-custom-timezone-datepicker',
  templateUrl: './custom-timezone-datepicker.component.html',
  styleUrls: ['./custom-timezone-datepicker.component.css'],
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => CustomTimezoneDatepickerComponent),
    multi: true
  }],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class CustomTimezoneDatepickerComponent implements OnInit, ControlValueAccessor{

  destory$ : Subject<boolean> = new Subject<boolean>();

  timezone: number = 0;
  value: Date;

  onChange: (value: any) => void;

  constructor(private _changeDetectorRef: ChangeDetectorRef, 
              private _localTimeZoneDatepickerService: CustomTimezoneDatepickerService) {
    this.timezone = this._localTimeZoneDatepickerService.getTimeZone();
  }

  ngOnInit() {
    this._localTimeZoneDatepickerService.getTimeZoneUpdate().pipe(
      takeUntil(this.destory$),
      tap(val => {
        this.timezone = val;
        if (this.value instanceof Date){
          let tmpDate = new Date(this.value.getTime());
          tmpDate.setHours(0);
          tmpDate.setMinutes(0);
          tmpDate.setSeconds(0);
          tmpDate.setMilliseconds(0);
          tmpDate = new Date(tmpDate.getTime() - (this.value.getTimezoneOffset() + this.timezone * 60) * 60 * 1000)
          this.onChange(tmpDate);  
        }
      })
    ).subscribe()
  }

  bsValueChange(val){
    setTimeout(()=>{
      this.value = val;
      if (val instanceof Date){
        let tmpDate = new Date(val.getTime());
        tmpDate.setHours(0);
        tmpDate.setMinutes(0);
        tmpDate.setSeconds(0);
        tmpDate.setMilliseconds(0);
        tmpDate = new Date(tmpDate.getTime() - (val.getTimezoneOffset() + this.timezone * 60) * 60 * 1000)
        this.onChange(tmpDate);  
      }else {
        this.onChange(val);
      }
    });
  }

  writeValue(val: any): void {
    if (val){
      if (val instanceof Date){
        this.value = new Date(val.getTime() + (val.getTimezoneOffset() + + this.timezone * 60) * 60 * 1000);
      }else {
        this.value = val;
      }
      this._changeDetectorRef.detectChanges();
    }
  }

  registerOnChange(fn: (value: any) => void): void {
    this.onChange = fn;
  }

  registerOnTouched(fn: any): void {
  }
  

}

imben1109 avatar Aug 26 '19 16:08 imben1109

@imben1109 Please, retry this version npm i ngx-bootstrap-ci@430758f867a9d2b385f14d9bc60a3a5b15cf0bf4 then rename ngx-bootstrap-ci => ngx-bootstrap inside node_modules

Domainv avatar Sep 04 '19 13:09 Domainv

it doesn't work in 5.2.0 version

isalox avatar Oct 04 '19 14:10 isalox

@isalox Provide more info, please. Some reproduction.

Domainv avatar Oct 04 '19 14:10 Domainv

same problem, datepicker return one day behind

vaztimur avatar Oct 23 '19 09:10 vaztimur

@isalox Provide more info, please. Some reproduction.

take a look here: https://stackblitz.com/edit/angular-vbku3b

just click Set Date and you will see in control date 1 day back

isalox avatar Oct 23 '19 10:10 isalox

Thanks, guys, I'll fix it as soon as possible.

Domainv avatar Oct 23 '19 14:10 Domainv

@isalox @Domainv Sorry, i have some question about the date. As JavaScript Date actually is timezone dependent, new Date(2019, 11, 23) would be different in different timezone. In my timezone that is UTC+8, the date would be 2018/11/22 16:00. but in UTC +0, the date should be correct. i.e. 2018/11/23 00:00

Therefore, i think UTC Time should be used. https://stackblitz.com/edit/angular-mb7szh

new Date(Date.UTC(2019, 10, 23))

instead of

new Date(2019, 10, 23)

And that is why i need a wrapper for my workaround solution. As my situation, i need a datepicker for UTC+0 situation (by my workaround solution). Actually, I need a wrapper to handle problem of UTC + 0, UTC + Custom Timezone and also browser timezone.

imben1109 avatar Oct 23 '19 14:10 imben1109

@imben1109 If I understand right, for you datepicker works correctly now?

Domainv avatar Oct 24 '19 13:10 Domainv

And that is why i need a wrapper for my workaround solution. As my situation, i need a datepicker for UTC+0 situation. Actually, I need a wrapper to handle problem of UTC + 0, UTC + Custom Timezone and also browser timezone.

it is just for example. Actually my problem is when I am getting data from Rest API. In data base, i store only dates without time. That data is send to UI and deserializes and when I want to edit I am getting dates one day back.

isalox avatar Oct 24 '19 14:10 isalox

I had the same issue, for some reasons, bsDatepicker always picked the day before the date you choose. Here is a solution I found which uses datepipe from angular/common.

let dateFromBsdatepicker:Date = reactiveForm['formControlName']; let correctDate = this.datepipe.transform(dateFromBsdatepicker,'yyyy-MM-dd');

Parison125 avatar Oct 24 '19 15:10 Parison125

@isalox you could try my workaround solution that i post before. a utc date wrapper for ngx-bootstrap for version before 5.2.0 @Domainv No. Still have some problem in https://stackblitz.com/edit/angular-mb7szh. It work fine when setDate button is pressed. But when the date is selected by datepicker, it do not work.

image

image

image

imben1109 avatar Oct 24 '19 17:10 imben1109