fix(ion-datetime): keep the model value consistently an ISO string (#15907)

This commit is contained in:
Ken Sodemann
2018-10-11 16:50:05 -05:00
committed by GitHub
parent fb3b752198
commit b46052bd83
5 changed files with 225 additions and 14 deletions

View File

@ -1288,9 +1288,9 @@ export namespace Components {
*/ */
'placeholder'?: string | null; 'placeholder'?: string | null;
/** /**
* the value of the datetime. * The value of the datetime as a valid ISO 8601 datetime string.
*/ */
'value'?: any; 'value'?: string;
/** /**
* Values used to create the list of selectable years. By default the year values range between the `min` and `max` datetime inputs. However, to control exactly which years to display, the `yearValues` input can take a number, an array of numbers, or string of comma separated numbers. For example, to show upcoming and recent leap years, then this input's value would be `yearValues="2024,2020,2016,2012,2008"`. * Values used to create the list of selectable years. By default the year values range between the `min` and `max` datetime inputs. However, to control exactly which years to display, the `yearValues` input can take a number, an array of numbers, or string of comma separated numbers. For example, to show upcoming and recent leap years, then this input's value would be `yearValues="2024,2020,2016,2012,2008"`.
*/ */
@ -1382,9 +1382,9 @@ export namespace Components {
*/ */
'placeholder'?: string | null; 'placeholder'?: string | null;
/** /**
* the value of the datetime. * The value of the datetime as a valid ISO 8601 datetime string.
*/ */
'value'?: any; 'value'?: string;
/** /**
* Values used to create the list of selectable years. By default the year values range between the `min` and `max` datetime inputs. However, to control exactly which years to display, the `yearValues` input can take a number, an array of numbers, or string of comma separated numbers. For example, to show upcoming and recent leap years, then this input's value would be `yearValues="2024,2020,2016,2012,2008"`. * Values used to create the list of selectable years. By default the year values range between the `min` and `max` datetime inputs. However, to control exactly which years to display, the `yearValues` input can take a number, an array of numbers, or string of comma separated numbers. For example, to show upcoming and recent leap years, then this input's value would be `yearValues="2024,2020,2016,2012,2008"`.
*/ */

View File

@ -0,0 +1,210 @@
import { convertDataToISO } from './datetime-util';
describe('datetime-util', () => {
describe('convertDataToISO', () => {
it('prints an emptry string for an empty datetime', () => {
expect(convertDataToISO({})).toEqual('');
});
describe('date', () => {
it('prints the year', () => {
expect(convertDataToISO({ year: 2018 })).toEqual('2018');
});
it('pads out the year', () => {
expect(convertDataToISO({ year: 1 })).toEqual('0001');
});
it('prints the month', () => {
expect(convertDataToISO({ year: 2018, month: 12 })).toEqual('2018-12');
});
it('pads the month', () => {
expect(convertDataToISO({ year: 2018, month: 3 })).toEqual('2018-03');
});
it('prints the day', () => {
expect(convertDataToISO({ year: 2018, month: 12, day: 25 })).toEqual(
'2018-12-25'
);
});
it('pads the day', () => {
expect(convertDataToISO({ year: 2018, month: 3, day: 13 })).toEqual(
'2018-03-13'
);
});
});
describe('time', () => {
it('prints the hour and minute', () => {
expect(convertDataToISO({ hour: 15, minute: 32 })).toEqual('15:32');
});
it('pads the hour and minute', () => {
expect(convertDataToISO({ hour: 3, minute: 4 })).toEqual('03:04');
});
it('prints seconds', () => {
expect(convertDataToISO({ hour: 15, minute: 32, second: 42 })).toEqual('15:32:42');
});
it('pads seconds', () => {
expect(convertDataToISO({ hour: 15, minute: 32, second: 2 })).toEqual('15:32:02');
});
it('prints milliseconds', () => {
expect(convertDataToISO({ hour: 15, minute: 32, second:42, millisecond: 143 })).toEqual('15:32:42.143');
});
it('pads milliseconds', () => {
expect(convertDataToISO({ hour: 15, minute: 32, second:42, millisecond: 7 })).toEqual('15:32:42.007');
});
});
describe('date-time', () => {
it('prints the hours and minutes', () => {
expect(
convertDataToISO({
year: 2018,
month: 12,
day: 25,
hour: 14,
minute: 42
})
).toEqual('2018-12-25T14:42:00Z');
});
it('pads the hours and minutes', () => {
expect(
convertDataToISO({
year: 2018,
month: 12,
day: 25,
hour: 0,
minute: 2
})
).toEqual('2018-12-25T00:02:00Z');
});
it('prints the seconds', () => {
expect(
convertDataToISO({
year: 2018,
month: 12,
day: 25,
hour: 14,
minute: 42,
second: 36
})
).toEqual('2018-12-25T14:42:36Z');
});
it('pads the seconds', () => {
expect(
convertDataToISO({
year: 2018,
month: 12,
day: 25,
hour: 14,
minute: 42,
second: 3
})
).toEqual('2018-12-25T14:42:03Z');
});
it('prints the milliseconds', () => {
expect(
convertDataToISO({
year: 2018,
month: 12,
day: 25,
hour: 14,
minute: 42,
second: 23,
millisecond: 250
})
).toEqual('2018-12-25T14:42:23.250Z');
});
it('pads the milliseconds', () => {
expect(
convertDataToISO({
year: 2018,
month: 12,
day: 25,
hour: 14,
minute: 42,
second: 23,
millisecond: 25
})
).toEqual('2018-12-25T14:42:23.025Z');
});
it('appends a whole hour positive offset timezone', () => {
expect(
convertDataToISO({
year: 2018,
month: 12,
day: 25,
hour: 14,
minute: 42,
tzOffset: 360
})
).toEqual('2018-12-25T14:42:00+06:00');
});
it('appends a partial hour positive offset timezone', () => {
expect(
convertDataToISO({
year: 2018,
month: 12,
day: 25,
hour: 14,
minute: 42,
tzOffset: 390
})
).toEqual('2018-12-25T14:42:00+06:30');
});
it('appends a whole hour negative offset timezone', () => {
expect(
convertDataToISO({
year: 2018,
month: 12,
day: 25,
hour: 14,
minute: 42,
tzOffset: -300
})
).toEqual('2018-12-25T14:42:00-05:00');
});
it('appends a partial hour negative offset timezone', () => {
expect(
convertDataToISO({
year: 2018,
month: 12,
day: 25,
hour: 14,
minute: 42,
tzOffset: -435
})
).toEqual('2018-12-25T14:42:00-07:15');
});
it('appends a zero offset timezone', () => {
expect(
convertDataToISO({
year: 2018,
month: 12,
day: 25,
hour: 14,
minute: 42,
tzOffset: 0
})
).toEqual('2018-12-25T14:42:00-00:00');
});
});
});
});

View File

@ -356,7 +356,7 @@ export function convertDataToISO(data: DatetimeData): string {
} else { } else {
// YYYY-MM-DDTHH:mm:SS+/-HH:mm // YYYY-MM-DDTHH:mm:SS+/-HH:mm
rtn += (data.tzOffset > 0 ? '+' : '-') + twoDigit(Math.floor(data.tzOffset / 60)) + ':' + twoDigit(data.tzOffset % 60); rtn += (data.tzOffset > 0 ? '+' : '-') + twoDigit(Math.floor(Math.abs(data.tzOffset / 60))) + ':' + twoDigit(data.tzOffset % 60);
} }
} }
} }

View File

@ -4,7 +4,7 @@ import { InputChangeEvent, Mode, PickerColumn, PickerColumnOption, PickerOptions
import { clamp, deferEvent } from '../../utils/helpers'; import { clamp, deferEvent } from '../../utils/helpers';
import { hostContext } from '../../utils/theme'; import { hostContext } from '../../utils/theme';
import { DatetimeData, LocaleData, convertFormatToKey, convertToArrayOfNumbers, convertToArrayOfStrings, dateDataSortValue, dateSortValue, dateValueRange, daysInMonth, getValueFromFormat, parseDate, parseTemplate, renderDatetime, renderTextFormat, updateDate } from './datetime-util'; import { DatetimeData, LocaleData, convertDataToISO, convertFormatToKey, convertToArrayOfNumbers, convertToArrayOfStrings, dateDataSortValue, dateSortValue, dateValueRange, daysInMonth, getValueFromFormat, parseDate, parseTemplate, renderDatetime, renderTextFormat, updateDate } from './datetime-util';
@Component({ @Component({
tag: 'ion-datetime', tag: 'ion-datetime',
@ -177,16 +177,16 @@ export class Datetime implements ComponentInterface {
@Prop() placeholder?: string | null; @Prop() placeholder?: string | null;
/** /**
* the value of the datetime. * The value of the datetime as a valid ISO 8601 datetime string.
*/ */
@Prop({ mutable: true }) value?: any; @Prop({ mutable: true }) value?: string;
/** /**
* Update the datetime value when the value changes * Update the datetime value when the value changes
*/ */
@Watch('value') @Watch('value')
protected valueChanged() { protected valueChanged() {
this.updateValue(); this.updateDatetimeValue(this.value);
this.emitStyle(); this.emitStyle();
this.ionChange.emit({ this.ionChange.emit({
value: this.value value: this.value
@ -221,7 +221,7 @@ export class Datetime implements ComponentInterface {
dayShortNames: convertToArrayOfStrings(this.dayShortNames, 'dayShortNames') dayShortNames: convertToArrayOfStrings(this.dayShortNames, 'dayShortNames')
}; };
this.updateValue(); this.updateDatetimeValue(this.value);
} }
componentDidLoad() { componentDidLoad() {
@ -252,8 +252,8 @@ export class Datetime implements ComponentInterface {
}); });
} }
private updateValue() { private updateDatetimeValue(value: any) {
updateDate(this.datetimeValue, this.value); updateDate(this.datetimeValue, value);
this.updateText(); this.updateText();
} }
@ -276,7 +276,8 @@ export class Datetime implements ComponentInterface {
{ {
text: this.doneText, text: this.doneText,
handler: (data: any) => { handler: (data: any) => {
this.value = data; this.updateDatetimeValue(data);
this.value = convertDataToISO(this.datetimeValue);
} }
} }
]; ];

View File

@ -224,7 +224,7 @@ dates in JavaScript.
| `pickerFormat` | `picker-format` | The format of the date and time picker columns the user selects. A datetime input can have one or many datetime parts, each getting their own column which allow individual selection of that particular datetime part. For example, year and month columns are two individually selectable columns which help choose an exact date from the datetime picker. Each column follows the string parse format. Defaults to use `displayFormat`. | `string` | | `pickerFormat` | `picker-format` | The format of the date and time picker columns the user selects. A datetime input can have one or many datetime parts, each getting their own column which allow individual selection of that particular datetime part. For example, year and month columns are two individually selectable columns which help choose an exact date from the datetime picker. Each column follows the string parse format. Defaults to use `displayFormat`. | `string` |
| `pickerOptions` | -- | Any additional options that the picker interface can accept. See the [Picker API docs](../../picker/Picker) for the picker options. | `PickerOptions` | | `pickerOptions` | -- | Any additional options that the picker interface can accept. See the [Picker API docs](../../picker/Picker) for the picker options. | `PickerOptions` |
| `placeholder` | `placeholder` | The text to display when there's no date selected yet. Using lowercase to match the input attribute | `string`, `null` | | `placeholder` | `placeholder` | The text to display when there's no date selected yet. Using lowercase to match the input attribute | `string`, `null` |
| `value` | -- | the value of the datetime. | `any` | | `value` | `value` | The value of the datetime as a valid ISO 8601 datetime string. | `string` |
| `yearValues` | -- | Values used to create the list of selectable years. By default the year values range between the `min` and `max` datetime inputs. However, to control exactly which years to display, the `yearValues` input can take a number, an array of numbers, or string of comma separated numbers. For example, to show upcoming and recent leap years, then this input's value would be `yearValues="2024,2020,2016,2012,2008"`. | `number[]`, `number`, `string` | | `yearValues` | -- | Values used to create the list of selectable years. By default the year values range between the `min` and `max` datetime inputs. However, to control exactly which years to display, the `yearValues` input can take a number, an array of numbers, or string of comma separated numbers. For example, to show upcoming and recent leap years, then this input's value would be `yearValues="2024,2020,2016,2012,2008"`. | `number[]`, `number`, `string` |