Imagine a situation, you send a user notification saying “Your meeting starts at 9:00 AM”. At first glance everything looks fine but that time is 9:00 AM in NewYork. The user however is in Greece, where it’s actually 4:00 PM. Terrible user experience isn’t it?
Recently, we had solved a similar issue. We needed to send appointment reminders in which the time should have been formatted correctly to the user’s local time.
Today I propose to review this situation and come up with a proper solution.
It’s easy to send a push notification and much harder to send it with the right context
On the server we store time in UTC but for the user we should calculate the time relative to where the user is located right now.
Thankfully to OneSignal this information is collected automatically for us. When a user subscribes to the push notification, OneSignal collects the timezone of the device.

1. Use the user’s external id to get their date from OneSignal
2. Extract the timezone and utilize it on the client to provide proper local time
3. Send personalized notification

Let’s take a look on the properly functioning logic that would help us to solve this problem.
Part 1 Getting the User’s Timezone
The user’s entity in OneSignal contains a lot of information about the user, subscription and device properties. To get the local timezone of the device we will need to fetch the user’s profile by external_id and then retrieve timezone_id from it.

Here is the code implementation:
const axios = require("axios");
// Helper to get user timezone from OneSignal
const getUserTimezone = async (externalUserId) => {
const appId = process.env.ONESIGNAL_APP_ID;
const apiKey = process.env.ONESIGNAL_API_KEY;
// Endpoint to fetch user data
const url =
`https://api.onesignal.com/apps/${appId}/users/by/external_id/${externalUserId}`
;
try {
const response = await axios.get(url, {
headers: {
Authorization: `Basic ${apiKey}`,
Accept: "application/json",
},
});
// If we get a timezone, return it. Otherwise, default to UTC.
if (response.data && response.data.properties.timezone_id) {
return response.data.properties.timezone_id;
}
console.warn(`No timezone found for ${externalUserId}, using fallback.`);
return "UTC";
} catch (error) {
// If the API fails (e.g. user not found), log it and fail safe to UTC
console.error(`Error fetching timezone: ${error.message}`);
return "UTC";
}
};
const axios = require("axios");
// Helper to get user timezone from OneSignal
const getUserTimezone = async (externalUserId) => {
const appId = process.env.ONESIGNAL_APP_ID;
const apiKey = process.env.ONESIGNAL_API_KEY;
// Endpoint to fetch user data
const url =
`https://api.onesignal.com/apps/${appId}/users/by/external_id/${externalUserId}`
;
try {
const response = await axios.get(url, {
headers: {
Authorization: `Basic ${apiKey}`,
Accept: "application/json",
},
});
// If we get a timezone, return it. Otherwise, default to UTC.
if (response.data && response.data.properties.timezone_id) {
return response.data.properties.timezone_id;
}
console.warn(`No timezone found for ${externalUserId}, using fallback.`);
return "UTC";
} catch (error) {
// If the API fails (e.g. user not found), log it and fail safe to UTC
console.error(`Error fetching timezone: ${error.message}`);
return "UTC";
}
};
const axios = require("axios");
// Helper to get user timezone from OneSignal
const getUserTimezone = async (externalUserId) => {
const appId = process.env.ONESIGNAL_APP_ID;
const apiKey = process.env.ONESIGNAL_API_KEY;
// Endpoint to fetch user data
const url =
`https://api.onesignal.com/apps/${appId}/users/by/external_id/${externalUserId}`
;
try {
const response = await axios.get(url, {
headers: {
Authorization: `Basic ${apiKey}`,
Accept: "application/json",
},
});
// If we get a timezone, return it. Otherwise, default to UTC.
if (response.data && response.data.properties.timezone_id) {
return response.data.properties.timezone_id;
}
console.warn(`No timezone found for ${externalUserId}, using fallback.`);
return "UTC";
} catch (error) {
// If the API fails (e.g. user not found), log it and fail safe to UTC
console.error(`Error fetching timezone: ${error.message}`);
return "UTC";
}
};
const axios = require("axios");
// Helper to get user timezone from OneSignal
const getUserTimezone = async (externalUserId) => {
const appId = process.env.ONESIGNAL_APP_ID;
const apiKey = process.env.ONESIGNAL_API_KEY;
// Endpoint to fetch user data
const url =
`https://api.onesignal.com/apps/${appId}/users/by/external_id/${externalUserId}`
;
try {
const response = await axios.get(url, {
headers: {
Authorization: `Basic ${apiKey}`,
Accept: "application/json",
},
});
// If we get a timezone, return it. Otherwise, default to UTC.
if (response.data && response.data.properties.timezone_id) {
return response.data.properties.timezone_id;
}
console.warn(`No timezone found for ${externalUserId}, using fallback.`);
return "UTC";
} catch (error) {
// If the API fails (e.g. user not found), log it and fail safe to UTC
console.error(`Error fetching timezone: ${error.message}`);
return "UTC";
}
};
const axios = require("axios");
// Helper to get user timezone from OneSignal
const getUserTimezone = async (externalUserId) => {
const appId = process.env.ONESIGNAL_APP_ID;
const apiKey = process.env.ONESIGNAL_API_KEY;
// Endpoint to fetch user data
const url =
`https://api.onesignal.com/apps/${appId}/users/by/external_id/${externalUserId}`
;
try {
const response = await axios.get(url, {
headers: {
Authorization: `Basic ${apiKey}`,
Accept: "application/json",
},
});
// If we get a timezone, return it. Otherwise, default to UTC.
if (response.data && response.data.properties.timezone_id) {
return response.data.properties.timezone_id;
}
console.warn(`No timezone found for ${externalUserId}, using fallback.`);
return "UTC";
} catch (error) {
// If the API fails (e.g. user not found), log it and fail safe to UTC
console.error(`Error fetching timezone: ${error.message}`);
return "UTC";
}
};
const axios = require("axios");
// Helper to get user timezone from OneSignal
const getUserTimezone = async (externalUserId) => {
const appId = process.env.ONESIGNAL_APP_ID;
const apiKey = process.env.ONESIGNAL_API_KEY;
// Endpoint to fetch user data
const url =
`https://api.onesignal.com/apps/${appId}/users/by/external_id/${externalUserId}`
;
try {
const response = await axios.get(url, {
headers: {
Authorization: `Basic ${apiKey}`,
Accept: "application/json",
},
});
// If we get a timezone, return it. Otherwise, default to UTC.
if (response.data && response.data.properties.timezone_id) {
return response.data.properties.timezone_id;
}
console.warn(`No timezone found for ${externalUserId}, using fallback.`);
return "UTC";
} catch (error) {
// If the API fails (e.g. user not found), log it and fail safe to UTC
console.error(`Error fetching timezone: ${error.message}`);
return "UTC";
}
};
const axios = require("axios");
// Helper to get user timezone from OneSignal
const getUserTimezone = async (externalUserId) => {
const appId = process.env.ONESIGNAL_APP_ID;
const apiKey = process.env.ONESIGNAL_API_KEY;
// Endpoint to fetch user data
const url =
`https://api.onesignal.com/apps/${appId}/users/by/external_id/${externalUserId}`
;
try {
const response = await axios.get(url, {
headers: {
Authorization: `Basic ${apiKey}`,
Accept: "application/json",
},
});
// If we get a timezone, return it. Otherwise, default to UTC.
if (response.data && response.data.properties.timezone_id) {
return response.data.properties.timezone_id;
}
console.warn(`No timezone found for ${externalUserId}, using fallback.`);
return "UTC";
} catch (error) {
// If the API fails (e.g. user not found), log it and fail safe to UTC
console.error(`Error fetching timezone: ${error.message}`);
return "UTC";
}
};
const axios = require("axios");
// Helper to get user timezone from OneSignal
const getUserTimezone = async (externalUserId) => {
const appId = process.env.ONESIGNAL_APP_ID;
const apiKey = process.env.ONESIGNAL_API_KEY;
// Endpoint to fetch user data
const url =
`https://api.onesignal.com/apps/${appId}/users/by/external_id/${externalUserId}`
;
try {
const response = await axios.get(url, {
headers: {
Authorization: `Basic ${apiKey}`,
Accept: "application/json",
},
});
// If we get a timezone, return it. Otherwise, default to UTC.
if (response.data && response.data.properties.timezone_id) {
return response.data.properties.timezone_id;
}
console.warn(`No timezone found for ${externalUserId}, using fallback.`);
return "UTC";
} catch (error) {
// If the API fails (e.g. user not found), log it and fail safe to UTC
console.error(`Error fetching timezone: ${error.message}`);
return "UTC";
}
};
const axios = require("axios");
// Helper to get user timezone from OneSignal
const getUserTimezone = async (externalUserId) => {
const appId = process.env.ONESIGNAL_APP_ID;
const apiKey = process.env.ONESIGNAL_API_KEY;
// Endpoint to fetch user data
const url =
`https://api.onesignal.com/apps/${appId}/users/by/external_id/${externalUserId}`
;
try {
const response = await axios.get(url, {
headers: {
Authorization: `Basic ${apiKey}`,
Accept: "application/json",
},
});
// If we get a timezone, return it. Otherwise, default to UTC.
if (response.data && response.data.properties.timezone_id) {
return response.data.properties.timezone_id;
}
console.warn(`No timezone found for ${externalUserId}, using fallback.`);
return "UTC";
} catch (error) {
// If the API fails (e.g. user not found), log it and fail safe to UTC
console.error(`Error fetching timezone: ${error.message}`);
return "UTC";
}
};
const axios = require("axios");
// Helper to get user timezone from OneSignal
const getUserTimezone = async (externalUserId) => {
const appId = process.env.ONESIGNAL_APP_ID;
const apiKey = process.env.ONESIGNAL_API_KEY;
// Endpoint to fetch user data
const url =
`https://api.onesignal.com/apps/${appId}/users/by/external_id/${externalUserId}`
;
try {
const response = await axios.get(url, {
headers: {
Authorization: `Basic ${apiKey}`,
Accept: "application/json",
},
});
// If we get a timezone, return it. Otherwise, default to UTC.
if (response.data && response.data.properties.timezone_id) {
return response.data.properties.timezone_id;
}
console.warn(`No timezone found for ${externalUserId}, using fallback.`);
return "UTC";
} catch (error) {
// If the API fails (e.g. user not found), log it and fail safe to UTC
console.error(`Error fetching timezone: ${error.message}`);
return "UTC";
}
};
const axios = require("axios");
// Helper to get user timezone from OneSignal
const getUserTimezone = async (externalUserId) => {
const appId = process.env.ONESIGNAL_APP_ID;
const apiKey = process.env.ONESIGNAL_API_KEY;
// Endpoint to fetch user data
const url =
`https://api.onesignal.com/apps/${appId}/users/by/external_id/${externalUserId}`
;
try {
const response = await axios.get(url, {
headers: {
Authorization: `Basic ${apiKey}`,
Accept: "application/json",
},
});
// If we get a timezone, return it. Otherwise, default to UTC.
if (response.data && response.data.properties.timezone_id) {
return response.data.properties.timezone_id;
}
console.warn(`No timezone found for ${externalUserId}, using fallback.`);
return "UTC";
} catch (error) {
// If the API fails (e.g. user not found), log it and fail safe to UTC
console.error(`Error fetching timezone: ${error.message}`);
return "UTC";
}
};
const axios = require("axios");
// Helper to get user timezone from OneSignal
const getUserTimezone = async (externalUserId) => {
const appId = process.env.ONESIGNAL_APP_ID;
const apiKey = process.env.ONESIGNAL_API_KEY;
// Endpoint to fetch user data
const url =
`https://api.onesignal.com/apps/${appId}/users/by/external_id/${externalUserId}`
;
try {
const response = await axios.get(url, {
headers: {
Authorization: `Basic ${apiKey}`,
Accept: "application/json",
},
});
// If we get a timezone, return it. Otherwise, default to UTC.
if (response.data && response.data.properties.timezone_id) {
return response.data.properties.timezone_id;
}
console.warn(`No timezone found for ${externalUserId}, using fallback.`);
return "UTC";
} catch (error) {
// If the API fails (e.g. user not found), log it and fail safe to UTC
console.error(`Error fetching timezone: ${error.message}`);
return "UTC";
}
};
const axios = require("axios");
// Helper to get user timezone from OneSignal
const getUserTimezone = async (externalUserId) => {
const appId = process.env.ONESIGNAL_APP_ID;
const apiKey = process.env.ONESIGNAL_API_KEY;
// Endpoint to fetch user data
const url =
`https://api.onesignal.com/apps/${appId}/users/by/external_id/${externalUserId}`
;
try {
const response = await axios.get(url, {
headers: {
Authorization: `Basic ${apiKey}`,
Accept: "application/json",
},
});
// If we get a timezone, return it. Otherwise, default to UTC.
if (response.data && response.data.properties.timezone_id) {
return response.data.properties.timezone_id;
}
console.warn(`No timezone found for ${externalUserId}, using fallback.`);
return "UTC";
} catch (error) {
// If the API fails (e.g. user not found), log it and fail safe to UTC
console.error(`Error fetching timezone: ${error.message}`);
return "UTC";
}
};
const axios = require("axios");
// Helper to get user timezone from OneSignal
const getUserTimezone = async (externalUserId) => {
const appId = process.env.ONESIGNAL_APP_ID;
const apiKey = process.env.ONESIGNAL_API_KEY;
// Endpoint to fetch user data
const url =
`https://api.onesignal.com/apps/${appId}/users/by/external_id/${externalUserId}`
;
try {
const response = await axios.get(url, {
headers: {
Authorization: `Basic ${apiKey}`,
Accept: "application/json",
},
});
// If we get a timezone, return it. Otherwise, default to UTC.
if (response.data && response.data.properties.timezone_id) {
return response.data.properties.timezone_id;
}
console.warn(`No timezone found for ${externalUserId}, using fallback.`);
return "UTC";
} catch (error) {
// If the API fails (e.g. user not found), log it and fail safe to UTC
console.error(`Error fetching timezone: ${error.message}`);
return "UTC";
}
};
const axios = require("axios");
// Helper to get user timezone from OneSignal
const getUserTimezone = async (externalUserId) => {
const appId = process.env.ONESIGNAL_APP_ID;
const apiKey = process.env.ONESIGNAL_API_KEY;
// Endpoint to fetch user data
const url =
`https://api.onesignal.com/apps/${appId}/users/by/external_id/${externalUserId}`
;
try {
const response = await axios.get(url, {
headers: {
Authorization: `Basic ${apiKey}`,
Accept: "application/json",
},
});
// If we get a timezone, return it. Otherwise, default to UTC.
if (response.data && response.data.properties.timezone_id) {
return response.data.properties.timezone_id;
}
console.warn(`No timezone found for ${externalUserId}, using fallback.`);
return "UTC";
} catch (error) {
// If the API fails (e.g. user not found), log it and fail safe to UTC
console.error(`Error fetching timezone: ${error.message}`);
return "UTC";
}
};
const axios = require("axios");
// Helper to get user timezone from OneSignal
const getUserTimezone = async (externalUserId) => {
const appId = process.env.ONESIGNAL_APP_ID;
const apiKey = process.env.ONESIGNAL_API_KEY;
// Endpoint to fetch user data
const url =
`https://api.onesignal.com/apps/${appId}/users/by/external_id/${externalUserId}`
;
try {
const response = await axios.get(url, {
headers: {
Authorization: `Basic ${apiKey}`,
Accept: "application/json",
},
});
// If we get a timezone, return it. Otherwise, default to UTC.
if (response.data && response.data.properties.timezone_id) {
return response.data.properties.timezone_id;
}
console.warn(`No timezone found for ${externalUserId}, using fallback.`);
return "UTC";
} catch (error) {
// If the API fails (e.g. user not found), log it and fail safe to UTC
console.error(`Error fetching timezone: ${error.message}`);
return "UTC";
}
};
const axios = require("axios");
// Helper to get user timezone from OneSignal
const getUserTimezone = async (externalUserId) => {
const appId = process.env.ONESIGNAL_APP_ID;
const apiKey = process.env.ONESIGNAL_API_KEY;
// Endpoint to fetch user data
const url =
`https://api.onesignal.com/apps/${appId}/users/by/external_id/${externalUserId}`
;
try {
const response = await axios.get(url, {
headers: {
Authorization: `Basic ${apiKey}`,
Accept: "application/json",
},
});
// If we get a timezone, return it. Otherwise, default to UTC.
if (response.data && response.data.properties.timezone_id) {
return response.data.properties.timezone_id;
}
console.warn(`No timezone found for ${externalUserId}, using fallback.`);
return "UTC";
} catch (error) {
// If the API fails (e.g. user not found), log it and fail safe to UTC
console.error(`Error fetching timezone: ${error.message}`);
return "UTC";
}
};
const axios = require("axios");
// Helper to get user timezone from OneSignal
const getUserTimezone = async (externalUserId) => {
const appId = process.env.ONESIGNAL_APP_ID;
const apiKey = process.env.ONESIGNAL_API_KEY;
// Endpoint to fetch user data
const url =
`https://api.onesignal.com/apps/${appId}/users/by/external_id/${externalUserId}`
;
try {
const response = await axios.get(url, {
headers: {
Authorization: `Basic ${apiKey}`,
Accept: "application/json",
},
});
// If we get a timezone, return it. Otherwise, default to UTC.
if (response.data && response.data.properties.timezone_id) {
return response.data.properties.timezone_id;
}
console.warn(`No timezone found for ${externalUserId}, using fallback.`);
return "UTC";
} catch (error) {
// If the API fails (e.g. user not found), log it and fail safe to UTC
console.error(`Error fetching timezone: ${error.message}`);
return "UTC";
}
};
Now let’s utilize previously received timezone information to localize notifications and send them to final users

// A simple date formatter using built-in Intl (no external libs needed)
const formatLocalTime = (isoDateString, timezone) => {
return new Intl.DateTimeFormat("en-US", {
timeZone: timezone,
hour: "numeric",
minute: "numeric",
hour12: true,
}).format(new Date(isoDateString));
};
// Main function to process the batch
async function sendAppointmentReminders(userIds, appointmentTimeUTC) {
// Create a promise for each user
const notifications = userIds.map(async (userId) => {
try {
// 1. Get the timezone (or fallback to UTC)
const timezone = await getUserTimezone(userId);
// 2. Format the time for THAT user
// If timezone is 'America/New_York', this might output "9:00 AM"
const localTimeStr = formatLocalTime(appointmentTimeUTC, timezone);
// 3. Construct the personalized message
const message = `You have an appointment at ${localTimeStr}.`;
console.log(`Preparing message for ${userId} (${timezone}): "${message}"`);
// 4. Send the notification (Stub for the actual OneSignal SDK call)
// await oneSignalClient.createNotification({ ... });
return { success: true, userId };
} catch (error) {
console.error(`Failed to process user ${userId}:`, error.message);
return { success: false, userId };
}
});
// Execute all operations concurrently
await Promise.all(notifications);
console.log("All notifications processed.");
}
// A simple date formatter using built-in Intl (no external libs needed)
const formatLocalTime = (isoDateString, timezone) => {
return new Intl.DateTimeFormat("en-US", {
timeZone: timezone,
hour: "numeric",
minute: "numeric",
hour12: true,
}).format(new Date(isoDateString));
};
// Main function to process the batch
async function sendAppointmentReminders(userIds, appointmentTimeUTC) {
// Create a promise for each user
const notifications = userIds.map(async (userId) => {
try {
// 1. Get the timezone (or fallback to UTC)
const timezone = await getUserTimezone(userId);
// 2. Format the time for THAT user
// If timezone is 'America/New_York', this might output "9:00 AM"
const localTimeStr = formatLocalTime(appointmentTimeUTC, timezone);
// 3. Construct the personalized message
const message = `You have an appointment at ${localTimeStr}.`;
console.log(`Preparing message for ${userId} (${timezone}): "${message}"`);
// 4. Send the notification (Stub for the actual OneSignal SDK call)
// await oneSignalClient.createNotification({ ... });
return { success: true, userId };
} catch (error) {
console.error(`Failed to process user ${userId}:`, error.message);
return { success: false, userId };
}
});
// Execute all operations concurrently
await Promise.all(notifications);
console.log("All notifications processed.");
}
// A simple date formatter using built-in Intl (no external libs needed)
const formatLocalTime = (isoDateString, timezone) => {
return new Intl.DateTimeFormat("en-US", {
timeZone: timezone,
hour: "numeric",
minute: "numeric",
hour12: true,
}).format(new Date(isoDateString));
};
// Main function to process the batch
async function sendAppointmentReminders(userIds, appointmentTimeUTC) {
// Create a promise for each user
const notifications = userIds.map(async (userId) => {
try {
// 1. Get the timezone (or fallback to UTC)
const timezone = await getUserTimezone(userId);
// 2. Format the time for THAT user
// If timezone is 'America/New_York', this might output "9:00 AM"
const localTimeStr = formatLocalTime(appointmentTimeUTC, timezone);
// 3. Construct the personalized message
const message = `You have an appointment at ${localTimeStr}.`;
console.log(`Preparing message for ${userId} (${timezone}): "${message}"`);
// 4. Send the notification (Stub for the actual OneSignal SDK call)
// await oneSignalClient.createNotification({ ... });
return { success: true, userId };
} catch (error) {
console.error(`Failed to process user ${userId}:`, error.message);
return { success: false, userId };
}
});
// Execute all operations concurrently
await Promise.all(notifications);
console.log("All notifications processed.");
}
// A simple date formatter using built-in Intl (no external libs needed)
const formatLocalTime = (isoDateString, timezone) => {
return new Intl.DateTimeFormat("en-US", {
timeZone: timezone,
hour: "numeric",
minute: "numeric",
hour12: true,
}).format(new Date(isoDateString));
};
// Main function to process the batch
async function sendAppointmentReminders(userIds, appointmentTimeUTC) {
// Create a promise for each user
const notifications = userIds.map(async (userId) => {
try {
// 1. Get the timezone (or fallback to UTC)
const timezone = await getUserTimezone(userId);
// 2. Format the time for THAT user
// If timezone is 'America/New_York', this might output "9:00 AM"
const localTimeStr = formatLocalTime(appointmentTimeUTC, timezone);
// 3. Construct the personalized message
const message = `You have an appointment at ${localTimeStr}.`;
console.log(`Preparing message for ${userId} (${timezone}): "${message}"`);
// 4. Send the notification (Stub for the actual OneSignal SDK call)
// await oneSignalClient.createNotification({ ... });
return { success: true, userId };
} catch (error) {
console.error(`Failed to process user ${userId}:`, error.message);
return { success: false, userId };
}
});
// Execute all operations concurrently
await Promise.all(notifications);
console.log("All notifications processed.");
}
// A simple date formatter using built-in Intl (no external libs needed)
const formatLocalTime = (isoDateString, timezone) => {
return new Intl.DateTimeFormat("en-US", {
timeZone: timezone,
hour: "numeric",
minute: "numeric",
hour12: true,
}).format(new Date(isoDateString));
};
// Main function to process the batch
async function sendAppointmentReminders(userIds, appointmentTimeUTC) {
// Create a promise for each user
const notifications = userIds.map(async (userId) => {
try {
// 1. Get the timezone (or fallback to UTC)
const timezone = await getUserTimezone(userId);
// 2. Format the time for THAT user
// If timezone is 'America/New_York', this might output "9:00 AM"
const localTimeStr = formatLocalTime(appointmentTimeUTC, timezone);
// 3. Construct the personalized message
const message = `You have an appointment at ${localTimeStr}.`;
console.log(`Preparing message for ${userId} (${timezone}): "${message}"`);
// 4. Send the notification (Stub for the actual OneSignal SDK call)
// await oneSignalClient.createNotification({ ... });
return { success: true, userId };
} catch (error) {
console.error(`Failed to process user ${userId}:`, error.message);
return { success: false, userId };
}
});
// Execute all operations concurrently
await Promise.all(notifications);
console.log("All notifications processed.");
}
// A simple date formatter using built-in Intl (no external libs needed)
const formatLocalTime = (isoDateString, timezone) => {
return new Intl.DateTimeFormat("en-US", {
timeZone: timezone,
hour: "numeric",
minute: "numeric",
hour12: true,
}).format(new Date(isoDateString));
};
// Main function to process the batch
async function sendAppointmentReminders(userIds, appointmentTimeUTC) {
// Create a promise for each user
const notifications = userIds.map(async (userId) => {
try {
// 1. Get the timezone (or fallback to UTC)
const timezone = await getUserTimezone(userId);
// 2. Format the time for THAT user
// If timezone is 'America/New_York', this might output "9:00 AM"
const localTimeStr = formatLocalTime(appointmentTimeUTC, timezone);
// 3. Construct the personalized message
const message = `You have an appointment at ${localTimeStr}.`;
console.log(`Preparing message for ${userId} (${timezone}): "${message}"`);
// 4. Send the notification (Stub for the actual OneSignal SDK call)
// await oneSignalClient.createNotification({ ... });
return { success: true, userId };
} catch (error) {
console.error(`Failed to process user ${userId}:`, error.message);
return { success: false, userId };
}
});
// Execute all operations concurrently
await Promise.all(notifications);
console.log("All notifications processed.");
}
// A simple date formatter using built-in Intl (no external libs needed)
const formatLocalTime = (isoDateString, timezone) => {
return new Intl.DateTimeFormat("en-US", {
timeZone: timezone,
hour: "numeric",
minute: "numeric",
hour12: true,
}).format(new Date(isoDateString));
};
// Main function to process the batch
async function sendAppointmentReminders(userIds, appointmentTimeUTC) {
// Create a promise for each user
const notifications = userIds.map(async (userId) => {
try {
// 1. Get the timezone (or fallback to UTC)
const timezone = await getUserTimezone(userId);
// 2. Format the time for THAT user
// If timezone is 'America/New_York', this might output "9:00 AM"
const localTimeStr = formatLocalTime(appointmentTimeUTC, timezone);
// 3. Construct the personalized message
const message = `You have an appointment at ${localTimeStr}.`;
console.log(`Preparing message for ${userId} (${timezone}): "${message}"`);
// 4. Send the notification (Stub for the actual OneSignal SDK call)
// await oneSignalClient.createNotification({ ... });
return { success: true, userId };
} catch (error) {
console.error(`Failed to process user ${userId}:`, error.message);
return { success: false, userId };
}
});
// Execute all operations concurrently
await Promise.all(notifications);
console.log("All notifications processed.");
}
// A simple date formatter using built-in Intl (no external libs needed)
const formatLocalTime = (isoDateString, timezone) => {
return new Intl.DateTimeFormat("en-US", {
timeZone: timezone,
hour: "numeric",
minute: "numeric",
hour12: true,
}).format(new Date(isoDateString));
};
// Main function to process the batch
async function sendAppointmentReminders(userIds, appointmentTimeUTC) {
// Create a promise for each user
const notifications = userIds.map(async (userId) => {
try {
// 1. Get the timezone (or fallback to UTC)
const timezone = await getUserTimezone(userId);
// 2. Format the time for THAT user
// If timezone is 'America/New_York', this might output "9:00 AM"
const localTimeStr = formatLocalTime(appointmentTimeUTC, timezone);
// 3. Construct the personalized message
const message = `You have an appointment at ${localTimeStr}.`;
console.log(`Preparing message for ${userId} (${timezone}): "${message}"`);
// 4. Send the notification (Stub for the actual OneSignal SDK call)
// await oneSignalClient.createNotification({ ... });
return { success: true, userId };
} catch (error) {
console.error(`Failed to process user ${userId}:`, error.message);
return { success: false, userId };
}
});
// Execute all operations concurrently
await Promise.all(notifications);
console.log("All notifications processed.");
}
// A simple date formatter using built-in Intl (no external libs needed)
const formatLocalTime = (isoDateString, timezone) => {
return new Intl.DateTimeFormat("en-US", {
timeZone: timezone,
hour: "numeric",
minute: "numeric",
hour12: true,
}).format(new Date(isoDateString));
};
// Main function to process the batch
async function sendAppointmentReminders(userIds, appointmentTimeUTC) {
// Create a promise for each user
const notifications = userIds.map(async (userId) => {
try {
// 1. Get the timezone (or fallback to UTC)
const timezone = await getUserTimezone(userId);
// 2. Format the time for THAT user
// If timezone is 'America/New_York', this might output "9:00 AM"
const localTimeStr = formatLocalTime(appointmentTimeUTC, timezone);
// 3. Construct the personalized message
const message = `You have an appointment at ${localTimeStr}.`;
console.log(`Preparing message for ${userId} (${timezone}): "${message}"`);
// 4. Send the notification (Stub for the actual OneSignal SDK call)
// await oneSignalClient.createNotification({ ... });
return { success: true, userId };
} catch (error) {
console.error(`Failed to process user ${userId}:`, error.message);
return { success: false, userId };
}
});
// Execute all operations concurrently
await Promise.all(notifications);
console.log("All notifications processed.");
}
// A simple date formatter using built-in Intl (no external libs needed)
const formatLocalTime = (isoDateString, timezone) => {
return new Intl.DateTimeFormat("en-US", {
timeZone: timezone,
hour: "numeric",
minute: "numeric",
hour12: true,
}).format(new Date(isoDateString));
};
// Main function to process the batch
async function sendAppointmentReminders(userIds, appointmentTimeUTC) {
// Create a promise for each user
const notifications = userIds.map(async (userId) => {
try {
// 1. Get the timezone (or fallback to UTC)
const timezone = await getUserTimezone(userId);
// 2. Format the time for THAT user
// If timezone is 'America/New_York', this might output "9:00 AM"
const localTimeStr = formatLocalTime(appointmentTimeUTC, timezone);
// 3. Construct the personalized message
const message = `You have an appointment at ${localTimeStr}.`;
console.log(`Preparing message for ${userId} (${timezone}): "${message}"`);
// 4. Send the notification (Stub for the actual OneSignal SDK call)
// await oneSignalClient.createNotification({ ... });
return { success: true, userId };
} catch (error) {
console.error(`Failed to process user ${userId}:`, error.message);
return { success: false, userId };
}
});
// Execute all operations concurrently
await Promise.all(notifications);
console.log("All notifications processed.");
}
// A simple date formatter using built-in Intl (no external libs needed)
const formatLocalTime = (isoDateString, timezone) => {
return new Intl.DateTimeFormat("en-US", {
timeZone: timezone,
hour: "numeric",
minute: "numeric",
hour12: true,
}).format(new Date(isoDateString));
};
// Main function to process the batch
async function sendAppointmentReminders(userIds, appointmentTimeUTC) {
// Create a promise for each user
const notifications = userIds.map(async (userId) => {
try {
// 1. Get the timezone (or fallback to UTC)
const timezone = await getUserTimezone(userId);
// 2. Format the time for THAT user
// If timezone is 'America/New_York', this might output "9:00 AM"
const localTimeStr = formatLocalTime(appointmentTimeUTC, timezone);
// 3. Construct the personalized message
const message = `You have an appointment at ${localTimeStr}.`;
console.log(`Preparing message for ${userId} (${timezone}): "${message}"`);
// 4. Send the notification (Stub for the actual OneSignal SDK call)
// await oneSignalClient.createNotification({ ... });
return { success: true, userId };
} catch (error) {
console.error(`Failed to process user ${userId}:`, error.message);
return { success: false, userId };
}
});
// Execute all operations concurrently
await Promise.all(notifications);
console.log("All notifications processed.");
}
// A simple date formatter using built-in Intl (no external libs needed)
const formatLocalTime = (isoDateString, timezone) => {
return new Intl.DateTimeFormat("en-US", {
timeZone: timezone,
hour: "numeric",
minute: "numeric",
hour12: true,
}).format(new Date(isoDateString));
};
// Main function to process the batch
async function sendAppointmentReminders(userIds, appointmentTimeUTC) {
// Create a promise for each user
const notifications = userIds.map(async (userId) => {
try {
// 1. Get the timezone (or fallback to UTC)
const timezone = await getUserTimezone(userId);
// 2. Format the time for THAT user
// If timezone is 'America/New_York', this might output "9:00 AM"
const localTimeStr = formatLocalTime(appointmentTimeUTC, timezone);
// 3. Construct the personalized message
const message = `You have an appointment at ${localTimeStr}.`;
console.log(`Preparing message for ${userId} (${timezone}): "${message}"`);
// 4. Send the notification (Stub for the actual OneSignal SDK call)
// await oneSignalClient.createNotification({ ... });
return { success: true, userId };
} catch (error) {
console.error(`Failed to process user ${userId}:`, error.message);
return { success: false, userId };
}
});
// Execute all operations concurrently
await Promise.all(notifications);
console.log("All notifications processed.");
// A simple date formatter using built-in Intl (no external libs needed)
const formatLocalTime = (isoDateString, timezone) => {
return new Intl.DateTimeFormat("en-US", {
timeZone: timezone,
hour: "numeric",
minute: "numeric",
hour12: true,
}).format(new Date(isoDateString));
};
// Main function to process the batch
async function sendAppointmentReminders(userIds, appointmentTimeUTC) {
// Create a promise for each user
const notifications = userIds.map(async (userId) => {
try {
// 1. Get the timezone (or fallback to UTC)
const timezone = await getUserTimezone(userId);
// 2. Format the time for THAT user
// If timezone is 'America/New_York', this might output "9:00 AM"
const localTimeStr = formatLocalTime(appointmentTimeUTC, timezone);
// 3. Construct the personalized message
const message = `You have an appointment at ${localTimeStr}.`;
console.log(`Preparing message for ${userId} (${timezone}): "${message}"`);
// 4. Send the notification (Stub for the actual OneSignal SDK call)
// await oneSignalClient.createNotification({ ... });
return { success: true, userId };
} catch (error) {
console.error(`Failed to process user ${userId}:`, error.message);
return { success: false, userId };
}
});
// Execute all operations concurrently
await Promise.all(notifications);
console.log("All notifications processed.");
}
// A simple date formatter using built-in Intl (no external libs needed)
const formatLocalTime = (isoDateString, timezone) => {
return new Intl.DateTimeFormat("en-US", {
timeZone: timezone,
hour: "numeric",
minute: "numeric",
hour12: true,
}).format(new Date(isoDateString));
};
// Main function to process the batch
async function sendAppointmentReminders(userIds, appointmentTimeUTC) {
// Create a promise for each user
const notifications = userIds.map(async (userId) => {
try {
// 1. Get the timezone (or fallback to UTC)
const timezone = await getUserTimezone(userId);
// 2. Format the time for THAT user
// If timezone is 'America/New_York', this might output "9:00 AM"
const localTimeStr = formatLocalTime(appointmentTimeUTC, timezone);
// 3. Construct the personalized message
const message = `You have an appointment at ${localTimeStr}.`;
console.log(`Preparing message for ${userId} (${timezone}): "${message}"`);
// 4. Send the notification (Stub for the actual OneSignal SDK call)
// await oneSignalClient.createNotification({ ... });
return { success: true, userId };
} catch (error) {
console.error(`Failed to process user ${userId}:`, error.message);
return { success: false, userId };
}
});
// Execute all operations concurrently
await Promise.all(notifications);
console.log("All notifications processed.");
}
// A simple date formatter using built-in Intl (no external libs needed)
const formatLocalTime = (isoDateString, timezone) => {
return new Intl.DateTimeFormat("en-US", {
timeZone: timezone,
hour: "numeric",
minute: "numeric",
hour12: true,
}).format(new Date(isoDateString));
};
// Main function to process the batch
async function sendAppointmentReminders(userIds, appointmentTimeUTC) {
// Create a promise for each user
const notifications = userIds.map(async (userId) => {
try {
// 1. Get the timezone (or fallback to UTC)
const timezone = await getUserTimezone(userId);
// 2. Format the time for THAT user
// If timezone is 'America/New_York', this might output "9:00 AM"
const localTimeStr = formatLocalTime(appointmentTimeUTC, timezone);
// 3. Construct the personalized message
const message = `You have an appointment at ${localTimeStr}.`;
console.log(`Preparing message for ${userId} (${timezone}): "${message}"`);
// 4. Send the notification (Stub for the actual OneSignal SDK call)
// await oneSignalClient.createNotification({ ... });
return { success: true, userId };
} catch (error) {
console.error(`Failed to process user ${userId}:`, error.message);
return { success: false, userId };
}
});
// Execute all operations concurrently
await Promise.all(notifications);
console.log("All notifications processed.");
}
// A simple date formatter using built-in Intl (no external libs needed)
const formatLocalTime = (isoDateString, timezone) => {
return new Intl.DateTimeFormat("en-US", {
timeZone: timezone,
hour: "numeric",
minute: "numeric",
hour12: true,
}).format(new Date(isoDateString));
};
// Main function to process the batch
async function sendAppointmentReminders(userIds, appointmentTimeUTC) {
// Create a promise for each user
const notifications = userIds.map(async (userId) => {
try {
// 1. Get the timezone (or fallback to UTC)
const timezone = await getUserTimezone(userId);
// 2. Format the time for THAT user
// If timezone is 'America/New_York', this might output "9:00 AM"
const localTimeStr = formatLocalTime(appointmentTimeUTC, timezone);
// 3. Construct the personalized message
const message = `You have an appointment at ${localTimeStr}.`;
console.log(`Preparing message for ${userId} (${timezone}): "${message}"`);
// 4. Send the notification (Stub for the actual OneSignal SDK call)
// await oneSignalClient.createNotification({ ... });
return { success: true, userId };
} catch (error) {
console.error(`Failed to process user ${userId}:`, error.message);
return { success: false, userId };
}
});
// Execute all operations concurrently
await Promise.all(notifications);
console.log("All notifications processed.");
}
// A simple date formatter using built-in Intl (no external libs needed)
const formatLocalTime = (isoDateString, timezone) => {
return new Intl.DateTimeFormat("en-US", {
timeZone: timezone,
hour: "numeric",
minute: "numeric",
hour12: true,
}).format(new Date(isoDateString));
};
// Main function to process the batch
async function sendAppointmentReminders(userIds, appointmentTimeUTC) {
// Create a promise for each user
const notifications = userIds.map(async (userId) => {
try {
// 1. Get the timezone (or fallback to UTC)
const timezone = await getUserTimezone(userId);
// 2. Format the time for THAT user
// If timezone is 'America/New_York', this might output "9:00 AM"
const localTimeStr = formatLocalTime(appointmentTimeUTC, timezone);
// 3. Construct the personalized message
const message = `You have an appointment at ${localTimeStr}.`;
console.log(`Preparing message for ${userId} (${timezone}): "${message}"`);
// 4. Send the notification (Stub for the actual OneSignal SDK call)
// await oneSignalClient.createNotification({ ... });
return { success: true, userId };
} catch (error) {
console.error(`Failed to process user ${userId}:`, error.message);
return { success: false, userId };
}
});
// Execute all operations concurrently
await Promise.all(notifications);
console.log("All notifications processed.");
}
// A simple date formatter using built-in Intl (no external libs needed)
const formatLocalTime = (isoDateString, timezone) => {
return new Intl.DateTimeFormat("en-US", {
timeZone: timezone,
hour: "numeric",
minute: "numeric",
hour12: true,
}).format(new Date(isoDateString));
};
// Main function to process the batch
async function sendAppointmentReminders(userIds, appointmentTimeUTC) {
// Create a promise for each user
const notifications = userIds.map(async (userId) => {
try {
// 1. Get the timezone (or fallback to UTC)
const timezone = await getUserTimezone(userId);
// 2. Format the time for THAT user
// If timezone is 'America/New_York', this might output "9:00 AM"
const localTimeStr = formatLocalTime(appointmentTimeUTC, timezone);
// 3. Construct the personalized message
const message = `You have an appointment at ${localTimeStr}.`;
console.log(`Preparing message for ${userId} (${timezone}): "${message}"`);
// 4. Send the notification (Stub for the actual OneSignal SDK call)
// await oneSignalClient.createNotification({ ... });
return { success: true, userId };
} catch (error) {
console.error(`Failed to process user ${userId}:`, error.message);
return { success: false, userId };
}
});
// Execute all operations concurrently
await Promise.all(notifications);
console.log("All notifications processed.");
}
Today we were able to handle such a difficult topic of Timizones properly thanks to OneSignal. I hope this article helps you to improve your system’s user experience. Thanks for reading!