mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
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:
@@ -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.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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'));
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user