diff --git a/lib/core/src/lib/common/utils/date-fns-utils.spec.ts b/lib/core/src/lib/common/utils/date-fns-utils.spec.ts index 897cc404a18..058c6bee936 100644 --- a/lib/core/src/lib/common/utils/date-fns-utils.spec.ts +++ b/lib/core/src/lib/common/utils/date-fns-utils.spec.ts @@ -138,37 +138,4 @@ describe('DateFnsUtils', () => { expect(forceLocalDateJapan.getMonth()).toBe(0); expect(forceLocalDateJapan.getFullYear()).toBe(2020); }); - - it('should detect if a formatted string contains a timezone', () => { - let result = DateFnsUtils.stringDateContainsTimeZone('2021-06-09T14:10'); - expect(result).toEqual(false); - - result = DateFnsUtils.stringDateContainsTimeZone('2021-06-09T14:10:00'); - expect(result).toEqual(false); - - result = DateFnsUtils.stringDateContainsTimeZone('2021-06-09T14:10:00Z'); - expect(result).toEqual(true); - - result = DateFnsUtils.stringDateContainsTimeZone('2021-06-09T14:10:00+00:00'); - expect(result).toEqual(true); - - result = DateFnsUtils.stringDateContainsTimeZone('2021-06-09T14:10:00-00:00'); - expect(result).toEqual(true); - }); - - it('should get the date from number', () => { - const spyUtcToLocal = spyOn(DateFnsUtils, 'utcToLocal').and.callThrough(); - - const date = DateFnsUtils.getDate(1623232200000); - expect(date.toISOString()).toBe('2021-06-09T09:50:00.000Z'); - expect(spyUtcToLocal).not.toHaveBeenCalled(); - }); - - it('should get transformed date when string date does not contain the timezone', () => { - const spyUtcToLocal = spyOn(DateFnsUtils, 'utcToLocal').and.callThrough(); - - DateFnsUtils.getDate('2021-06-09T14:10:00'); - - expect(spyUtcToLocal).toHaveBeenCalled(); - }); }); diff --git a/lib/core/src/lib/common/utils/date-fns-utils.ts b/lib/core/src/lib/common/utils/date-fns-utils.ts index 7f8daf4f4cb..12af9a5e66e 100644 --- a/lib/core/src/lib/common/utils/date-fns-utils.ts +++ b/lib/core/src/lib/common/utils/date-fns-utils.ts @@ -225,18 +225,4 @@ export class DateFnsUtils { const utcDate = `${date.getFullYear()}-${panDate(date.getMonth() + 1)}-${panDate(date.getDate())}T00:00:00.000Z`; return new Date(utcDate); } - - static stringDateContainsTimeZone(value: string): boolean { - return /(Z|([+|-]\d\d:?\d\d))$/.test(value); - } - - static getDate(value: string | number | Date): Date { - let date = new Date(value); - - if (typeof value === 'string' && !DateFnsUtils.stringDateContainsTimeZone(value)) { - date = this.utcToLocal(date); - } - - return date; - } } diff --git a/lib/core/src/lib/form/components/widgets/core/error-message.model.ts b/lib/core/src/lib/form/components/widgets/core/error-message.model.ts index ecba680bc87..e1987c185fb 100644 --- a/lib/core/src/lib/form/components/widgets/core/error-message.model.ts +++ b/lib/core/src/lib/form/components/widgets/core/error-message.model.ts @@ -33,9 +33,11 @@ export class ErrorMessageModel { getAttributesAsJsonObj() { let result = {}; if (this.attributes.size > 0) { + const obj = Object.create(null); this.attributes.forEach((value, key) => { - result[key] = typeof value === 'string' ? value : JSON.stringify(value); + obj[key] = value; }); + result = JSON.stringify(obj); } return result; } diff --git a/lib/core/src/lib/form/components/widgets/core/form-field-validator.ts b/lib/core/src/lib/form/components/widgets/core/form-field-validator.ts index 79f0f63de75..ce097b7dd84 100644 --- a/lib/core/src/lib/form/components/widgets/core/form-field-validator.ts +++ b/lib/core/src/lib/form/components/widgets/core/form-field-validator.ts @@ -159,7 +159,7 @@ export class DateTimeFieldValidator implements FormFieldValidator { } static isValidDateTime(input: string): boolean { - const date = DateFnsUtils.getDate(input); + const date = new Date(input); return isDateValid(date); } @@ -245,11 +245,19 @@ export class MaxDateFieldValidator extends BoundaryDateFieldValidator { } } -export abstract class BoundaryDateTimeFieldValidator implements FormFieldValidator { +/** + * Validates the min constraint for the datetime value. + * + * Notes for developers: + * the format of the min/max values is always the ISO datetime: i.e. 2023-10-01T15:21:00.000Z. + * Min/Max values can be parsed with standard `new Date(value)` calls. + * + */ +export class MinDateTimeFieldValidator implements FormFieldValidator { private supportedTypes = [FormFieldTypes.DATETIME]; isSupported(field: FormFieldModel): boolean { - return field && this.supportedTypes.indexOf(field.type) > -1 && !!field[this.getSubjectField()]; + return field && this.supportedTypes.indexOf(field.type) > -1 && !!field.minValue; } validate(field: FormFieldModel): boolean { @@ -267,65 +275,57 @@ export abstract class BoundaryDateTimeFieldValidator implements FormFieldValidat private checkDateTime(field: FormFieldModel): boolean { let isValid = true; - const fieldValueDate = DateFnsUtils.getDate(field.value); - const subjectFieldDate = DateFnsUtils.getDate(field[this.getSubjectField()]); + const fieldValueDate = new Date(field.value); + const min = new Date(field.minValue); - if (this.compareDates(fieldValueDate, subjectFieldDate)) { - field.validationSummary.message = this.getErrorMessage(); - field.validationSummary.attributes.set(this.getSubjectField(), DateFnsUtils.formatDate(subjectFieldDate, field.dateDisplayFormat)); + if (isBefore(fieldValueDate, min)) { + field.validationSummary.message = `FORM.FIELD.VALIDATOR.NOT_LESS_THAN`; + field.validationSummary.attributes.set('minValue', DateFnsUtils.formatDate(min, field.dateDisplayFormat).replace(':', '-')); isValid = false; } return isValid; } - - protected abstract compareDates(fieldValueDate: Date, subjectFieldDate: Date): boolean; - - protected abstract getSubjectField(): string; - - protected abstract getErrorMessage(): string; } /** - * Validates the min constraint for the datetime value. + * Validates the max constraint for the datetime value. * * Notes for developers: * the format of the min/max values is always the ISO datetime: i.e. 2023-10-01T15:21:00.000Z. * Min/Max values can be parsed with standard `new Date(value)` calls. * */ -export class MinDateTimeFieldValidator extends BoundaryDateTimeFieldValidator { - protected compareDates(fieldValueDate: Date, subjectFieldDate: Date): boolean { - return isBefore(fieldValueDate, subjectFieldDate); - } - - protected getSubjectField(): string { - return 'minValue'; - } +export class MaxDateTimeFieldValidator implements FormFieldValidator { + private supportedTypes = [FormFieldTypes.DATETIME]; - protected getErrorMessage(): string { - return `FORM.FIELD.VALIDATOR.NOT_LESS_THAN`; + isSupported(field: FormFieldModel): boolean { + return field && this.supportedTypes.indexOf(field.type) > -1 && !!field.maxValue; } -} -/** - * Validates the max constraint for the datetime value. - * - * Notes for developers: - * the format of the min/max values is always the ISO datetime: i.e. 2023-10-01T15:21:00.000Z. - * Min/Max values can be parsed with standard `new Date(value)` calls. - * - */ -export class MaxDateTimeFieldValidator extends BoundaryDateTimeFieldValidator { - protected compareDates(fieldValueDate: Date, subjectFieldDate: Date): boolean { - return isAfter(fieldValueDate, subjectFieldDate); + validate(field: FormFieldModel): boolean { + let isValid = true; + if (this.isSupported(field) && field.value && field.isVisible) { + if (!DateTimeFieldValidator.isValidDateTime(field.value)) { + field.validationSummary.message = 'FORM.FIELD.VALIDATOR.INVALID_DATE'; + isValid = false; + } else { + isValid = this.checkDateTime(field); + } + } + return isValid; } - protected getSubjectField(): string { - return 'maxValue'; - } + private checkDateTime(field: FormFieldModel): boolean { + let isValid = true; + const fieldValueDate = new Date(field.value); + const max = new Date(field.maxValue); - protected getErrorMessage(): string { - return `FORM.FIELD.VALIDATOR.NOT_GREATER_THAN`; + if (isAfter(fieldValueDate, max)) { + field.validationSummary.message = `FORM.FIELD.VALIDATOR.NOT_GREATER_THAN`; + field.validationSummary.attributes.set('maxValue', DateFnsUtils.formatDate(max, field.dateDisplayFormat).replace(':', '-')); + isValid = false; + } + return isValid; } } diff --git a/lib/core/src/lib/form/components/widgets/core/form-field.model.ts b/lib/core/src/lib/form/components/widgets/core/form-field.model.ts index b0ad4a1f639..aae980098a7 100644 --- a/lib/core/src/lib/form/components/widgets/core/form-field.model.ts +++ b/lib/core/src/lib/form/components/widgets/core/form-field.model.ts @@ -299,7 +299,7 @@ export class FormFieldModel extends FormWidgetModel { } parseValue(json: any): any { - const value = Object.prototype.hasOwnProperty.call(json, 'value') && json.value !== undefined ? json.value : null; + let value = Object.prototype.hasOwnProperty.call(json, 'value') && json.value !== undefined ? json.value : null; /* This is needed due to Activiti issue related to reading dropdown values as value string @@ -440,12 +440,7 @@ export class FormFieldModel extends FormWidgetModel { this.value = new Date(); } - let dateValue; - try { - dateValue = DateFnsUtils.parseDate(this.value, this.dateDisplayFormat); - } catch (e) { - dateValue = new Date('error'); - } + const dateValue = DateFnsUtils.parseDate(this.value, this.dateDisplayFormat); if (isValidDate(dateValue)) { const datePart = DateFnsUtils.formatDate(dateValue, 'yyyy-MM-dd'); @@ -461,7 +456,7 @@ export class FormFieldModel extends FormWidgetModel { this.value = new Date(); } - const dateTimeValue = this.value !== null ? DateFnsUtils.getDate(this.value) : null; + const dateTimeValue = this.value !== null ? new Date(this.value) : null; if (isValidDate(dateTimeValue)) { this.form.values[this.id] = dateTimeValue.toISOString(); diff --git a/lib/core/src/lib/form/components/widgets/date-time/date-time.widget.ts b/lib/core/src/lib/form/components/widgets/date-time/date-time.widget.ts index c5e57b933d7..a6cc9844c11 100644 --- a/lib/core/src/lib/form/components/widgets/date-time/date-time.widget.ts +++ b/lib/core/src/lib/form/components/widgets/date-time/date-time.widget.ts @@ -77,15 +77,15 @@ export class DateTimeWidgetComponent extends WidgetComponent implements OnInit { if (this.field) { if (this.field.minValue) { - this.minDate = DateFnsUtils.getDate(this.field.minValue); + this.minDate = DateFnsUtils.localToUtc(new Date(this.field.minValue)); } if (this.field.maxValue) { - this.maxDate = DateFnsUtils.getDate(this.field.maxValue); + this.maxDate = DateFnsUtils.localToUtc(new Date(this.field.maxValue)); } if (this.field.value) { - this.value = DateFnsUtils.getDate(this.field.value); + this.value = DateFnsUtils.localToUtc(new Date(this.field.value)); } } } @@ -95,12 +95,11 @@ export class DateTimeWidgetComponent extends WidgetComponent implements OnInit { const newValue = this.dateTimeAdapter.parse(input.value, this.field.dateDisplayFormat); if (isValid(newValue)) { - this.field.value = newValue.toISOString(); + this.field.value = DateFnsUtils.utcToLocal(newValue).toISOString(); } else { this.field.value = input.value; } - this.value = DateFnsUtils.getDate(this.field.value); this.onFieldChanged(this.field); } @@ -109,7 +108,7 @@ export class DateTimeWidgetComponent extends WidgetComponent implements OnInit { const input = event.targetElement as HTMLInputElement; if (newValue && isValid(newValue)) { - this.field.value = newValue.toISOString(); + this.field.value = DateFnsUtils.utcToLocal(newValue).toISOString(); } else { this.field.value = input.value; }