From 7099f3e679c78b79508a043187dddb77a3218029 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 11 Jul 2024 10:53:28 +0300 Subject: [PATCH] Add settings.storeUtcDates options to store datetime-local in UTC format (toISOString) fix #8542 (#8543) --- src/question.ts | 17 ++++++++++++----- src/question_text.ts | 29 ++++++++++++++++++++++++----- src/settings.ts | 3 ++- tests/question_texttests.ts | 27 ++++++++++++++++++++++++++- 4 files changed, 64 insertions(+), 12 deletions(-) diff --git a/src/question.ts b/src/question.ts index acefde8a35..8647514aa8 100644 --- a/src/question.ts +++ b/src/question.ts @@ -1542,10 +1542,19 @@ export class Question extends SurveyElement public getFilteredValue(): any { return this.value; } public getFilteredName(): any { return this.getValueName(); } public get valueForSurvey(): any { + return this.valueForSurveyCore(this.value); + } + protected valueForSurveyCore(val: any): any { if (!!this.valueToDataCallback) { - return this.valueToDataCallback(this.value); + return this.valueToDataCallback(val); } - return this.value; + return val; + } + protected valueFromDataCore(val: any): any { + if (!!this.valueFromDataCallback) { + return this.valueFromDataCallback(val); + } + return val; } /** * Sets the question's `value` and `comment` properties to `undefined`. @@ -2337,9 +2346,7 @@ export class Question extends SurveyElement //IQuestion updateValueFromSurvey(newValue: any, clearData: boolean = false): void { newValue = this.getUnbindValue(newValue); - if (!!this.valueFromDataCallback) { - newValue = this.valueFromDataCallback(newValue); - } + newValue = this.valueFromDataCore(newValue); if (!this.checkIsValueCorrect(newValue)) return; const isEmpty = this.isValueEmpty(newValue); if(!isEmpty && this.defaultValueExpression) { diff --git a/src/question_text.ts b/src/question_text.ts index 796305f7fc..7d37e7fb64 100644 --- a/src/question_text.ts +++ b/src/question_text.ts @@ -346,7 +346,24 @@ export class QuestionTextModel extends QuestionTextBase { this._inputValue = this.maskInstance.getMaskedValue(this.value); } } - + private hasToConvertToUTC(val: any): boolean { + return settings.storeUtcDates && this.isDateTimeLocaleType() && !!val; + } + protected valueForSurveyCore(val: any): any { + if(this.hasToConvertToUTC(val)) { + val = new Date(val).toISOString(); + } + return super.valueForSurveyCore(val); + } + protected valueFromDataCore(val: any): any { + if(this.hasToConvertToUTC(val)) { + const d = new Date(val); + const locale_d = new Date(d.getTime() - d.getTimezoneOffset() * 60 * 1000); + let res = locale_d.toISOString(); + val = res.substring(0, res.length - 2); + } + return super.valueFromDataCore(val); + } protected onCheckForErrors( errors: Array, isOnValueChanged: boolean @@ -431,7 +448,10 @@ export class QuestionTextModel extends QuestionTextBase { ); } private get isDateInputType(): boolean { - return this.inputType === "date" || this.inputType === "datetime-local"; + return this.inputType === "date" || this.isDateTimeLocaleType(); + } + private isDateTimeLocaleType(): boolean { + return this.inputType === "datetime-local"; } private getCalculatedMinMax(minMax: any): any { if (this.isValueEmpty(minMax)) return minMax; @@ -488,11 +508,10 @@ export class QuestionTextModel extends QuestionTextBase { return this.maskTypeIsEmpty ? super.getIsInputTextUpdate() : false; } supportGoNextPageAutomatic(): boolean { - return !this.getIsInputTextUpdate() && - ["date", "datetime-local"].indexOf(this.inputType) < 0; + return !this.getIsInputTextUpdate() && !this.isDateInputType; } public supportGoNextPageError(): boolean { - return ["date", "datetime-local"].indexOf(this.inputType) < 0; + return !this.isDateInputType; } /** * An array of predefined options from which users can select. This property configures an HTML [``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/datalist) element and associates it with the underlying `input` element. diff --git a/src/settings.ts b/src/settings.ts index 82d29ebc77..9b7a444605 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -770,5 +770,6 @@ export var settings = { "a": /[a-zA-Z]/, "#": /[a-zA-Z0-9]/ } - } + }, + storeUtcDates: false }; \ No newline at end of file diff --git a/tests/question_texttests.ts b/tests/question_texttests.ts index 25ac80f8f5..97cfe0d552 100644 --- a/tests/question_texttests.ts +++ b/tests/question_texttests.ts @@ -432,4 +432,29 @@ QUnit.test("Test maxLength & getMaxLength", function (assert) { q.inputType = "password"; assert.equal(q.isTextInput, true, "isTextInput - password"); assert.equal(q.getMaxLength(), 10, "getMaxLength() - password"); -}); \ No newline at end of file +}); +QUnit.test("settings.storeUtcDates = true, #8542", function(assert) { + const survey = new SurveyModel({ + "elements": [ + { + "name": "q1", + "type": "text", + "inputType": "datetime-local" + } + ] + }); + const q1 = survey.getQuestionByName("q1"); + settings.storeUtcDates = true; + const locD = new Date(Date.now()); + const utcD = new Date(locD.toISOString()); + q1.value = locD; + assert.deepEqual(survey.data, { q1: utcD.toISOString() }, "#1"); + let str = survey.data.q1; + assert.equal(str.indexOf("Z"), str.length - 1, "Has z symbol"); + q1.clearValue(); + assert.deepEqual(survey.data, { }, "#2"); + survey.data = { q1: utcD.toISOString() }; + assert.equal(new Date(q1.value).toTimeString(), locD.toTimeString(), "#3"); + assert.equal(q1.value.indexOf("Z"), -1, "Has no z symbol"); + settings.storeUtcDates = false; +});