Skip to content

Commit

Permalink
fix: location open hours are incorrect when location and user timezon…
Browse files Browse the repository at this point in the history
…es are different (#14)
  • Loading branch information
allensquare authored Oct 3, 2024
1 parent 3cd768b commit b050739
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 19 deletions.
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

0 comments on commit b050739

Please sign in to comment.