Hi @demirmusa,
I sent the project to the email. Can you please have a look at this issue? It is causing us a lot of trouble...
Regards,
Hi Ismail,
The poject is quite big to send by email. However, it is very simple to reproduce the issue on your side as well. If you use RAD tool to add an entity (product) with a property DateTo (DateTime not null) then change your timezone on your machine to : "British Summer Time". Then try to add a product with datetime selected "01/05/2020". Once you hit the save then the date gets saved in the database as "2020-04-30 23:00:00.0000000". I don't want that. The date should be saved in the database as it is selected by not taking into account user timezone. it should just ignore client time zone and it should treat the selected date as UTC time and save it as it is.
I tried to add "Clock.Provider = ClockProviders.Utc;" in the core project but with no success.
Note that the screenshot are taken from freshly downloaded demo project from aspzero and only added Product Entity by using the Rad Tool.
Neither of the solution works.
this.product.dateTo type is moment.Moment
. I cannot have operation without wrapping it in a moment. when I call this.product.dateTo.utc() I get excecption as "utc is not a function".
Then when I wrap it in a moment as below:
moment(this.product.dateTo)
As soon as I wrap it in moment then the date becomes previous day.
I tried to remove ClockProviders.Utc as well with no success.
console.log(this.product.dateTo); console.log(moment(this.product.dateTo).utc(true).format()); console.log(moment(this.product.dateTo).utcOffset(0, false).format()); console.log(moment(this.product.dateTo).utc(true).format(moment.defaultFormatUtc)); console.log(moment(this.product.dateTo).format(moment.defaultFormatUtc));
here is the output
Wed Apr 01 2020 00:00:00 GMT+0100 (British Summer Time) 2020-03-31T23:00:00Z 2020-03-31T23:00:00Z 2020-03-31T23:00:00Z 2020-03-31T23:00:00Z
We really need a fix for this...
I tried option 2 already and i didn't see any change that dates still gets converted to utc. Is there any process that I should be running to prevent nswag to generate toISOString() after commenting out ClockProviders.Utc ?
Hi Ismail,
Thank you coming back.
AFAIK that conversion is happening in the service-proxies.ts and I don't think I should touch that at all... Can you please be more specific as this causing us big issue.
html:
<div class="form-group"> <label for="DateTo">{{l("DateTo")}} *</label> <input required class="form-control m-input" type="datetime" [bsConfig]="{format: 'YYYY-MM-DD', dateInputFormat: 'YYYY-MM', adaptivePosition: true}" (onShown)="onOpenCalendar($event)" bsDatepicker datePickerMomentModifier [(date)]="product.dateTo" id="DateTo" name="DateTo"> </div> ts:
`save(): void { this.saving = true;
this._productServiceProxy.createOrEdit(this.product) .pipe(finalize(() => { this.saving = false;})) .subscribe(() => { this.notify.info(this.l('SavedSuccessfully')); this.close(); this.modalSave.emit(null); });}
In service-proxies.ts file (which generated by nswag) I can see the following:
toJSON(data?: any) { data = typeof data === 'object' ? data : {}; data["description"] = this.description; data["dateTo"] = this.dateTo ? this.dateTo.toISOString() : <any>undefined; data["postedDate"] = this.postedDate ? this.postedDate.toISOString() : <any>undefined; data["id"] = this.id; return data; }
When DateTo field selected as "2020-05" and save button is hit. the payload becomes as below due the fact that client time zone is GMT+1 and toISOString() in the service-proxies.ts converts that to utc time.
So what do we change to make sure the date goes back as selected date instead of taking into account time zone?
Nope! <div class="form-group"> <label for="Product_DateTo">{{l("DateTo")}} *</label> <input required class="form-control m-input" type="datetime" [bsConfig]="{dateInputFormat: 'MM/YYYY', adaptivePosition: true}" (onShown)="onOpenCalendar($event)" bsDatepicker datePickerMomentModifier [(date)]="product.dateTo" id="Product_DateTo" name="Product_DateTo"> </div>
This a month selection field. When 05/2020 is selected that date (2020-05-01) should be sent back as payload. Not taking into account of user location time zone and changing it to utc with toIsoString(). As a result the date ends up in the server as "2020-04-30".
I understand that this could be an issue when the user selects times. But what is the intention here is to allow user to select a month or even a specific date. Ignore the time completely. So what ever the user selects that is the date or month should be sent back to the server by ignoring the timezone of the user.
{ "IsRegenerate": false, "MenuPosition": "main", "RelativeNamespace": "Contracts", "EntityName": "Product", "EntityNamePlural": "Products", "TableName": "Products", "PrimaryKeyType": "int", "BaseClass": "AuditedEntity", "EntityHistory": true, "AutoMigration": false, "UpdateDatabase": false, "CreateUserInterface": true, "CreateViewOnly": true, "CreateExcelExport": true, "PagePermission": { "Host": false, "Tenant": false }, "Properties": [ { "Name": "EffectiveFrom", "Type": "DateTime", "MaxLength": 0, "MinLength": 0, "Range": { "IsRangeSet": false, "MinimumValue": 0, "MaximumValue": 0 }, "Required": false, "Nullable": true, "Regex": "", "UserInterface": { "AdvancedFilter": true, "List": true, "CreateOrUpdate": true } }, { "Name": "EffectiveTo", "Type": "DateTime", "MaxLength": 0, "MinLength": 0, "Range": { "IsRangeSet": false, "MinimumValue": 0, "MaximumValue": 0 }, "Required": false, "Nullable": true, "Regex": "", "UserInterface": { "AdvancedFilter": true, "List": true, "CreateOrUpdate": true } } ], "NavigationProperties": [ { "Namespace": "AdamP.Contracts", "ForeignEntityName": "ContractProduct", "IdType": "int", "IsNullable": false, "PropertyName": "ContractProductId", "DisplayPropertyName": "SupplierRef", "DuplicationNumber": 0, "RelationType": "single" }, { "Namespace": "AdamP.Contracts", "ForeignEntityName": "ContractStatusHistory", "IdType": "int", "IsNullable": true, "PropertyName": "ContractStatusHistoryId", "DisplayPropertyName": "Description", "DuplicationNumber": 0, "RelationType": "single" }, { "Namespace": "AdamP.ProductLines", "ForeignEntityName": "ProductLine", "IdType": "int", "IsNullable": false, "PropertyName": "ProductLineId", "DisplayPropertyName": "Description", "DuplicationNumber": 0, "RelationType": "single" } ], "EnumDefinitions": [] }
Here is one of the entities config.
On the client side (angular) we get angular components auto generated.
If the DateTime is set as nullable in the Rad tool the following gets generated:
//.ts effectiveTo: Date;
save(): void { this.saving = true; if (this.effectiveTo) { if (!this.test.effectiveTo) { this.test.effectiveTo = moment(this.effectiveTo).startOf('day'); } else { this.test.effectiveTo = moment(this.effectiveTo); } } else { this.test.effectiveTo = null; }
//.html
<div class="form-group"> <label for="Test_EffectiveTo">{{l("EffectiveTo")}}</label> <input class="form-control m-input" type="datetime" bsDatepicker [(ngModel)]="effectiveTo" id="Test_EffectiveTo" name="Test_EffectiveTo"> </div>
As our dates need to be nulllable how can I achive the above hack without a hack? It seems very complicated to handle nullable dates as Rad Tool generates a Date local variable first and do operation on that variable instead of using Moment. I guess there is a reason behind that decision that I wonder...
Hi Musa,
Not sure if this answers my question... I am aware of the issue actually but I was looking for a solution. The RAD tool defines those date fields as Date object. And to be able to assign value to them I use toDate() to convert moment to date. If I use toISOString() then I will get a string representation of the date but actually I would need the date unless if I replace the Date types.
Are you planning to replace those Date type completely in the RAD tool in the future? As you can see you cannot rely on them for date operations.
This is my solution for now to overcome the problem:
let userTimezoneOffset = this.effectiveFrom.getTimezoneOffset() * 60000;
let selectedRealDate = this.effectiveFrom.getTime() - userTimezoneOffset;
this.effectiveTo = moment(selectedRealDate).add(this.contractProductLine.duration, 'months').toDate();
Considering above how would you fix the issue with toISOString()?
I also tried to replace data type from Date to Moment but in this case the date shows in the field as 'Invalid Date' at first load.
When RAD is used to generate fields for DateTime the client side code generated something like below assuming that date fields are called EffectiveFrom and EffectiveTo:
In angular create-edit component class:
effectiveFrom: Date; effectiveTo: Date;
in the save method:
if (this.effectiveTo) { if (!this.product.effectiveTo) { this.product.effectiveTo = moment(this.effectiveTo).startOf('day'); } else { this.product.effectiveTo = moment(this.effectiveTo); } } else { this.product.effectiveTo = null; } ... //similar for effective from
The problem is that when we are in a different time zone (GMT) then and we want to auto populate 'Effective To' once user selects a date for Effecitve From as below:
effectiveFromChanged(newValue): void {
this.effectiveFrom = newValue;
this.effectiveTo = moment(this.effectiveFrom).add(12, 'M').toDate();
}
}
This sets the effective to 1 day before the actual expected date. For example if the user selects effective from 01/04/2020 then effective to should be set to 01/04/2021 instead of 31/03/2021.
I guess this is happening due the fact that conversation takes the timezone into account. But we do not want that! How can we over come this sort of date issues? Any option that would disable this behaviour? Maybe the date pickers should return moment object instead of a date?
Note: Clock.Provider = ClockProviders.Utc;
is set in the core module.