diff --git a/BREAKING.md b/BREAKING.md index 821ad7f28b..a7ac8778e8 100644 --- a/BREAKING.md +++ b/BREAKING.md @@ -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. +

Input

- `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. diff --git a/core/src/components/datetime-button/datetime-button.tsx b/core/src/components/datetime-button/datetime-button.tsx index 73f1e6ed46..3200b84ca9 100644 --- a/core/src/components/datetime-button/datetime-button.tsx +++ b/core/src/components/datetime-button/datetime-button.tsx @@ -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) { diff --git a/core/src/components/datetime/datetime-interface.ts b/core/src/components/datetime/datetime-interface.ts index 83aa501af8..7071b28696 100644 --- a/core/src/components/datetime/datetime-interface.ts +++ b/core/src/components/datetime/datetime-interface.ts @@ -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'; diff --git a/core/src/components/datetime/datetime.tsx b/core/src/components/datetime/datetime.tsx index 92ff2119d9..a3a40f8c6d 100644 --- a/core/src/components/datetime/datetime.tsx +++ b/core/src/components/datetime/datetime.tsx @@ -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, }; } diff --git a/core/src/components/datetime/test/basic/datetime.e2e.ts b/core/src/components/datetime/test/basic/datetime.e2e.ts index 3d01593a56..bec68f39c7 100644 --- a/core/src/components/datetime/test/basic/datetime.e2e.ts +++ b/core/src/components/datetime/test/basic/datetime.e2e.ts @@ -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', () => { `); + 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'); }); }); diff --git a/core/src/components/datetime/test/format.spec.ts b/core/src/components/datetime/test/format.spec.ts index 74e81bf890..7db7254e8d 100644 --- a/core/src/components/datetime/test/format.spec.ts +++ b/core/src/components/datetime/test/format.spec.ts @@ -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'); diff --git a/core/src/components/datetime/test/minmax/datetime.e2e.ts b/core/src/components/datetime/test/minmax/datetime.e2e.ts index 27e39281bb..71dfd209df 100644 --- a/core/src/components/datetime/test/minmax/datetime.e2e.ts +++ b/core/src/components/datetime/test/minmax/datetime.e2e.ts @@ -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'); }); }); }); diff --git a/core/src/components/datetime/utils/format.ts b/core/src/components/datetime/utils/format.ts index 53901f4a58..6a9b7d5724 100644 --- a/core/src/components/datetime/utils/format.ts +++ b/core/src/components/datetime/utils/format.ts @@ -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')); }; /** diff --git a/core/src/components/datetime/utils/manipulation.ts b/core/src/components/datetime/utils/manipulation.ts index 34ac4af7d4..af32a4a54c 100644 --- a/core/src/components/datetime/utils/manipulation.ts +++ b/core/src/components/datetime/utils/manipulation.ts @@ -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); - } } } } diff --git a/core/src/components/datetime/utils/parse.ts b/core/src/components/datetime/utils/parse.ts index 95c05b9be6..fda79085ac 100644 --- a/core/src/components/datetime/utils/parse.ts +++ b/core/src/components/datetime/utils/parse.ts @@ -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', }; }