fix(datetime): do not report timezone in ionChange (#26183)

resolves #25577

BREAKING CHANGE:

Datetime no longer incorrectly reports the time zone when `value` is updated. Datetime does not manage time zones, so any time zone information provided is ignored.
This commit is contained in:
Liam DeBeasi
2022-10-31 14:03:14 -05:00
committed by GitHub
parent 322a1dbcd0
commit 3fb4caf21f
10 changed files with 23 additions and 110 deletions

View File

@@ -86,6 +86,8 @@ This section details the desktop browser, JavaScript framework, and mobile platf
- Datetime no longer automatically adjusts the `value` property when passed an array and `multiple="false"`. Developers should update their apps to ensure they are using the API correctly.
- Datetime no longer incorrectly reports the time zone when `value` is updated. Datetime does not manage time zones, so any time zone information provided is ignored.
<h4 id="version-7x-input">Input</h4>
- `ionChange` is no longer emitted when the `value` of `ion-input` is modified externally. `ionChange` is only emitted from user committed changes, such as typing in the input and the input losing focus or from clicking the clear action within the input.

View File

@@ -204,11 +204,6 @@ export class DatetimeButton implements ComponentInterface {
const firstParsedDatetime = parsedDatetimes[0];
const use24Hour = is24Hour(locale, hourCycle);
// TODO(FW-1865) - Remove once FW-1831 is fixed.
parsedDatetimes.forEach((parsedDatetime) => {
parsedDatetime.tzOffset = undefined;
});
this.dateText = this.timeText = undefined;
switch (datetimePresentation) {

View File

@@ -19,7 +19,6 @@ export interface DatetimeParts {
hour?: number;
minute?: number;
ampm?: 'am' | 'pm';
tzOffset?: number;
}
export type DatetimePresentation = 'date-time' | 'time-date' | 'date' | 'time' | 'month' | 'year' | 'month-year';

View File

@@ -503,25 +503,6 @@ export class Datetime implements ComponentInterface {
if (activePartsIsArray && activeParts.length === 0) {
this.setValue(undefined);
} else {
/**
* Prevent convertDataToISO from doing any
* kind of transformation based on timezone
* This cancels out any change it attempts to make
*
* Important: Take the timezone offset based on
* the date that is currently selected, otherwise
* there can be 1 hr difference when dealing w/ DST
*/
if (activePartsIsArray) {
const dates = convertDataToISO(activeParts).map((str) => new Date(str));
for (let i = 0; i < dates.length; i++) {
activeParts[i].tzOffset = dates[i].getTimezoneOffset() * -1;
}
} else {
const date = new Date(convertDataToISO(activeParts));
activeParts.tzOffset = date.getTimezoneOffset() * -1;
}
this.setValue(convertDataToISO(activeParts));
}
}
@@ -1211,7 +1192,7 @@ export class Datetime implements ComponentInterface {
*/
const singleValue = Array.isArray(valueToProcess) ? valueToProcess[0] : valueToProcess;
const { month, day, year, hour, minute, tzOffset } = clampDate(singleValue, minParts, maxParts);
const { month, day, year, hour, minute } = clampDate(singleValue, minParts, maxParts);
const ampm = parseAmPm(hour!);
this.setWorkingParts({
@@ -1220,7 +1201,6 @@ export class Datetime implements ComponentInterface {
year,
hour,
minute,
tzOffset,
ampm,
});
@@ -1240,7 +1220,6 @@ export class Datetime implements ComponentInterface {
year,
hour,
minute,
tzOffset,
ampm,
};
}

View File

@@ -99,11 +99,7 @@ test.describe('datetime: selecting a day', () => {
await ionChange.next();
const value = await datetime.evaluate((el: HTMLIonDatetimeElement) => el.value);
await expect(typeof value).toBe('string');
// Check to make sure value includes current time
await expect(value!.includes('2022-10-01T16:22')).toBe(true);
await expect(datetime).toHaveJSProperty('value', '2022-10-01T16:22:00');
});
});
@@ -134,6 +130,7 @@ test.describe('datetime: confirm date', () => {
</script>
`);
const ionChange = await page.spyOnEvent('ionChange');
const datetimeMonthDidChange = await page.spyOnEvent('datetimeMonthDidChange');
const eventButton = page.locator('button#bind');
await eventButton.click();
@@ -146,10 +143,9 @@ test.describe('datetime: confirm date', () => {
const datetime = page.locator('ion-datetime');
await datetime.evaluate((el: HTMLIonDatetimeElement) => el.confirm());
// Value may include timezone information so we need to check
// that the value at least includes the correct date/time info.
const value = (await datetime.evaluate((el: HTMLIonDatetimeElement) => el.value))!;
expect(value.includes('2021-12-25T12:40:00')).toBe(true);
await ionChange.next();
expect(datetime).toHaveJSProperty('value', '2021-12-25T12:40:00');
});
});

View File

@@ -102,34 +102,6 @@ describe('getLocalizedDayPeriod', () => {
});
describe('getLocalizedTime', () => {
describe('with a timezone offset', () => {
it('should ignore the offset and localize the time to PM', () => {
const datetimeParts = {
day: 1,
month: 1,
year: 2022,
hour: 13,
minute: 40,
tzOffset: -240,
};
expect(getLocalizedTime('en-US', datetimeParts, false)).toEqual('1:40 PM');
});
it('should ignore the offset and localize the time to AM', () => {
const datetimeParts = {
day: 1,
month: 1,
year: 2022,
hour: 9,
minute: 40,
tzOffset: -240,
};
expect(getLocalizedTime('en-US', datetimeParts, false)).toEqual('9:40 AM');
});
});
it('should localize the time to PM', () => {
const datetimeParts = {
day: 1,
@@ -137,7 +109,6 @@ describe('getLocalizedTime', () => {
year: 2022,
hour: 13,
minute: 40,
tzOffset: 0,
};
expect(getLocalizedTime('en-US', datetimeParts, false)).toEqual('1:40 PM');
@@ -150,7 +121,6 @@ describe('getLocalizedTime', () => {
year: 2022,
hour: 9,
minute: 40,
tzOffset: 0,
};
expect(getLocalizedTime('en-US', datetimeParts, false)).toEqual('9:40 AM');
@@ -163,7 +133,6 @@ describe('getLocalizedTime', () => {
year: 2022,
hour: 0,
minute: 0,
tzOffset: 0,
};
expect(getLocalizedTime('en-GB', datetimeParts, false)).toEqual('12:00 am');

View File

@@ -257,9 +257,7 @@ test.describe('datetime: minmax', () => {
await ionChange.next();
const value = await datetime.evaluate((el: HTMLIonDatetimeElement) => el.value);
await expect(typeof value).toBe('string');
await expect(value!.includes('2022-10-10T08:00')).toBe(true);
await expect(datetime).toHaveJSProperty('value', '2022-10-10T08:00:00');
});
test('should reset to max time if out of bounds', async ({ page }) => {
await page.setContent(`
@@ -278,9 +276,7 @@ test.describe('datetime: minmax', () => {
await ionChange.next();
const value = await datetime.evaluate((el: HTMLIonDatetimeElement) => el.value);
await expect(typeof value).toBe('string');
await expect(value!.includes('2022-10-10T08:00')).toBe(true);
await expect(datetime).toHaveJSProperty('value', '2022-10-10T08:00:00');
});
});
});

View File

@@ -18,21 +18,25 @@ export const getLocalizedTime = (locale: string, refParts: DatetimeParts, use24H
return new Intl.DateTimeFormat(locale, {
hour: 'numeric',
minute: 'numeric',
/**
* Setting the timeZone to UTC prevents
* new Intl.DatetimeFormat from subtracting
* the user's current timezone offset
* when formatting the time.
*/
timeZone: 'UTC',
/**
* We use hourCycle here instead of hour12 due to:
* https://bugs.chromium.org/p/chromium/issues/detail?id=1347316&q=hour12&can=2
*/
hourCycle: use24Hour ? 'h23' : 'h12',
}).format(
new Date(
convertDataToISO({
...refParts,
// TODO: FW-1831 will remove the need to manually set the tzOffset to undefined
tzOffset: undefined,
})
)
);
/**
* Setting Z at the end indicates that this
* date string is in the UTC time zone. This
* prevents new Date from adding the time zone
* offset when getting the ISO string.
*/
}).format(new Date(convertDataToISO(refParts) + 'Z'));
};
/**

View File

@@ -36,18 +36,6 @@ export function convertDataToISO(data: DatetimeParts | DatetimeParts[]): string
if (data.hour !== undefined) {
// YYYY-MM-DDTHH:mm:SS
rtn += `T${twoDigit(data.hour)}:${twoDigit(data.minute)}:00`;
if (data.tzOffset === undefined) {
// YYYY-MM-DDTHH:mm:SSZ
rtn += 'Z';
} else {
// YYYY-MM-DDTHH:mm:SS+/-HH:mm
rtn +=
(data.tzOffset > 0 ? '+' : '-') +
twoDigit(Math.floor(Math.abs(data.tzOffset / 60))) +
':' +
twoDigit(data.tzOffset % 60);
}
}
}
}

View File

@@ -93,20 +93,6 @@ export function parseDate(val: string | string[] | undefined | null): DatetimePa
parse[i] = parse[i] !== undefined ? parseInt(parse[i], 10) : undefined;
}
let tzOffset = 0;
if (parse[9] && parse[10]) {
// hours
tzOffset = parseInt(parse[10], 10) * 60;
if (parse[11]) {
// minutes
tzOffset += parseInt(parse[11], 10);
}
if (parse[9] === '-') {
// + or -
tzOffset *= -1;
}
}
// can also get second and millisecond from parse[6] and parse[7] if needed
return {
year: parse[1],
@@ -114,7 +100,6 @@ export function parseDate(val: string | string[] | undefined | null): DatetimePa
day: parse[3],
hour: parse[4],
minute: parse[5],
tzOffset,
ampm: parse[4] < 12 ? 'am' : 'pm',
};
}