Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: location open hours are incorrect when location and user timezones are different #14

Merged
merged 1 commit into from
Oct 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions src/helpers/datetime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export const getDateObjInTimezone = (dateString: string, timeString: TimeString,
* @param {string} locale Hyphenized language/country locale combo, e.g. en-US (BCP 47).
* @param {Object} format Options object specifying the date/time parts to be included in the formatted string
* @param {string} timezone string specifying a timezone offset the time into
* @param {boolean} hour12 boolean specifying whether to use 12-hour time format
* @returns {string}
*/
export const localizeDate = (dateObj: Date, locale: string, format: localizeDateFormats, timezone: string, hour12?: boolean) => {
Expand Down Expand Up @@ -68,6 +69,7 @@ export const getDayOfWeekKey = (dateObj: Date, locale: string, timezone: string)
* @param {localizeDateFormats} timeFormat
* @param {string} storeLocale - Hyphenized language/country locale combo, e.g. en-US (BCP 47).
* @param {string} timezone
* @param {string} tzOffsetString
* @return {String} Ex: 9:00 am
*/
export const getFormattedTime = (
Expand All @@ -76,13 +78,20 @@ export const getFormattedTime = (
timeFormat: localizeDateFormats,
storeLocale: string,
timezone: string,
tzOffsetString: string,
) => {
const timeStrings = timeString.split(':');
let hours = timeStrings[0];
const minutes = timeStrings[1];
const seconds = timeStrings[2];
if (Number.parseInt(hours, 10) === 24) {
hours = '0';
hours = '00';
}
date.setHours(Number.parseInt(hours, 10), Number.parseInt(minutes, 10));
return localizeDate(date, storeLocale, timeFormat, timezone).replace('AM', 'am').replace('PM', 'pm');
const currentDateString = localizeDate(date, 'en-US', DateFormats.yearNmonthNdayN, timezone);
const dateObj = getDateObjInTimezone(
currentDateString,
`${hours}:${minutes}:${seconds}` as TimeString,
tzOffsetString,
);
return localizeDate(dateObj, storeLocale, timeFormat, timezone).replace('AM', 'am').replace('PM', 'pm');
};
28 changes: 12 additions & 16 deletions src/helpers/location.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export class Location {
* @param {LocationResource} location
* @param {string} locale - Hyphenized language/country locale combo, e.g. en-US (BCP 47).
* @param {'PICKUP' | 'DELIVERY'} fulfillment
* @return { OpenStatusDayAndTime | null }
* @return { OpenStatusDayAndTime | null }
*/
getLocationFulfillmentOpenStatusDayAndTime(
location: LocationResource,
Expand All @@ -51,7 +51,7 @@ export class Location {
*
* @param {LocationResource} location
* @param {string} locale - Hyphenized language/country locale combo, e.g. en-US (BCP 47).
* @return { OpenStatusDayAndTime | null }
* @return { OpenStatusDayAndTime | null }
*/
getLocationBusinessHoursOpenStatusDayAndTime(
location: LocationResource,
Expand Down Expand Up @@ -92,8 +92,8 @@ export class Location {
}

const closeTime = localizeDate(dateObj, locale, DateFormats.hourNminuteNsecondN, timezone, false);
const openUntil = getFormattedTime(dateObj, closeTime, DateFormats.hourNminuteN, locale, timezone);
const openUntilDay = getFormattedTime(dateObj, closeTime, DateFormats.weekdayLong, locale, timezone);
const openUntil = getFormattedTime(dateObj, closeTime, DateFormats.hourNminuteN, locale, timezone, tzOffsetString);
const openUntilDay = getFormattedTime(dateObj, closeTime, DateFormats.weekdayLong, locale, timezone, tzOffsetString);
return {
status: this.OpenStatus.CURRENTLY_OPEN,
time: openUntil,
Expand All @@ -103,8 +103,8 @@ export class Location {

const nextOpenIntervalToday = this.getNextOpenIntervalToday(locale, timezone, hours);
if (nextOpenIntervalToday) {
const opensAt = getFormattedTime(dateNow, nextOpenIntervalToday.open, DateFormats.hourNminuteN, locale, timezone);
const opensAtDay = getFormattedTime(dateNow, nextOpenIntervalToday.open, DateFormats.weekdayLong, locale, timezone);
const opensAt = getFormattedTime(dateNow, nextOpenIntervalToday.open, DateFormats.hourNminuteN, locale, timezone, tzOffsetString);
const opensAtDay = getFormattedTime(dateNow, nextOpenIntervalToday.open, DateFormats.weekdayLong, locale, timezone, tzOffsetString);
return {
status: this.OpenStatus.OPENS_LATER_TODAY,
time: opensAt,
Expand All @@ -115,8 +115,8 @@ export class Location {
const nextOpenIntervalAfterToday = this.getNextOpenIntervalAfterToday(locale, timezone, hours);
if (nextOpenIntervalAfterToday) {
const { date, interval: nextInterval } = nextOpenIntervalAfterToday;
const opensAt = getFormattedTime(date, nextInterval.open, DateFormats.hourNminuteN, locale, timezone);
const opensAtDay = getFormattedTime(date, nextInterval.open, DateFormats.weekdayLong, locale, timezone);
const opensAt = getFormattedTime(date, nextInterval.open, DateFormats.hourNminuteN, locale, timezone, tzOffsetString);
const opensAtDay = getFormattedTime(date, nextInterval.open, DateFormats.weekdayLong, locale, timezone, tzOffsetString);
return {
status: this.OpenStatus.OPENS_ANOTHER_DAY,
time: opensAt,
Expand Down Expand Up @@ -166,11 +166,11 @@ export class Location {
locale,
timezone,
);

const openIntervals = hours[dayOfWeekKey];
if (openIntervals.length) {
const openInterval = openIntervals.find(time => time.open === '00:00:00');

// If interval opens at 00:00:00 and closes at 24:00:00, then the business is open that full day
// continue to the next day
if (openInterval && openInterval.close === '24:00:00') {
Expand Down Expand Up @@ -224,7 +224,7 @@ export class Location {
);
return hours[dayOfWeekKey];
}

/**
* Get the open interval if we are currently in an open interval. Otherwise null
*
Expand Down Expand Up @@ -339,13 +339,9 @@ export class Location {
return null;
}

// Set the hours and minutes of nextDateObj to nextOpenPeriod.open
const [nextHours, nextMinutes,] = nextOpenInterval.open.split(':');
nextDateObj.setHours(Number.parseInt(nextHours, 10), Number.parseInt(nextMinutes, 10), 0);

return {
date: nextDateObj,
interval: nextOpenInterval,
};
}
}
}
57 changes: 57 additions & 0 deletions test/helpers.location.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -771,6 +771,63 @@ describe('Location fulfillment status day and time variations', () => {
day: 'Saturday',
});
});

it('For a location in a different timezone, should be currently open, open until 3pm', () => {
const location = createTestLocation();
location.timezone = {
'name': 'America/Los_Angeles', // Location timezone must be the same as the system timezone
'offset_string': '-08:00',
'offset_minutes': -480
};
// Monday January 1, 2024 1:00:00 pm (America/Los_Angeles timezone)
const date = new Date(2024, 0, 1, 21); // UTC is 9pm
vi.setSystemTime(date);

const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP');
expect(result).toStrictEqual({
status: sdk.helpers.location.OpenStatus.CURRENTLY_OPEN,
time: '3:00 pm',
day: 'Monday',
});
});

it('For a location in a different timezone, should open later today, opens 4pm', () => {
const location = createTestLocation();
location.timezone = {
'name': 'America/Los_Angeles', // Location timezone must be the same as the system timezone
'offset_string': '-08:00',
'offset_minutes': -480
};
// Monday January 1, 2024 3:30:00 pm (America/Los_Angeles timezone)
const date = new Date(2024, 0, 1, 23, 30); // UTC is 11:30pm
vi.setSystemTime(date);

const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP');
expect(result).toStrictEqual({
status: sdk.helpers.location.OpenStatus.OPENS_LATER_TODAY,
time: '4:00 pm',
day: 'Monday',
});
});

it('For a location in a different timezone, should open another day, opens next available time Tuesday 12:00 pm', () => {
const location = createTestLocation();
location.timezone = {
'name': 'America/Los_Angeles', // Location timezone must be the same as the system timezone
'offset_string': '-08:00',
'offset_minutes': -480
};
// Monday January 1, 2024 11:00:00 pm (America/Los_Angeles timezone)
const date = new Date(2024, 0, 2, 7, 0); // UTC is 7am next day
vi.setSystemTime(date);

const result = sdk.helpers.location.getLocationFulfillmentOpenStatusDayAndTime(location, 'en-US', 'PICKUP');
expect(result).toStrictEqual({
status: sdk.helpers.location.OpenStatus.OPENS_ANOTHER_DAY,
time: '12:00 pm',
day: 'Tuesday',
});
});
});

describe('Location business hours status day and time variations', () => {
Expand Down
Loading