feat: update Office365 getMainTimeZone to handle new Graph API format and convert Windows timezones to IANA (#23763)

* feat: update Office365 getMainTimeZone to handle new Graph API format and convert Windows timezones to IANA

- Handle new Graph API response format with 'value' field containing Windows timezone names
- Add windows-iana dependency for timezone conversion from Windows to IANA format
- Maintain backward compatibility with legacy direct string response format
- Add proper error handling with fallback to original timezone name if conversion fails
- Log conversion details for debugging and monitoring

Co-Authored-By: morgan@cal.com <morgan@cal.com>

* fix: example app select default value

* fixup! fix: example app select default value

* fix: preserve legacy string timezone behavior, only convert new object format

- Address GitHub comments from supalarry about behavior change concerns
- Preserve original behavior: return legacy string responses as-is without conversion
- Only convert Windows timezones from new Graph API object format
- Add explanatory comments about API format evolution
- Restructure conditional logic to handle formats independently

Co-Authored-By: morgan@cal.com <morgan@cal.com>

* trigger CI with ready-for-e2e label

Co-Authored-By: morgan@cal.com <morgan@cal.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: morgan@cal.com <morgan@cal.com>
Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
This commit is contained in:
devin-ai-integration[bot]
2025-10-28 12:15:34 +00:00
committed by GitHub
co-authored by Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> morgan@cal.com <morgan@cal.com> Morgan
parent fc159fb9ea
commit 6f5b8ead75
4 changed files with 60 additions and 10 deletions
@@ -1,5 +1,6 @@
import type { Calendar as OfficeCalendar, User, Event } from "@microsoft/microsoft-graph-types-beta";
import type { DefaultBodyType } from "msw";
import { findIana } from "windows-iana";
import { MSTeamsLocationType } from "@calcom/app-store/constants";
import dayjs from "@calcom/dayjs";
@@ -688,20 +689,58 @@ export default class Office365CalendarService implements Calendar {
return response.json();
}
/**
* Gets the main timezone from Office365 mailbox settings.
* Handles both legacy string format and new Graph API object format due to API evolution.
* Legacy format: returns string timezone as-is (preserves existing behavior)
* New format: converts Windows timezone from {"value": "Windows Timezone"} to IANA format
*/
async getMainTimeZone(): Promise<string> {
try {
const response = await this.fetcher(`${await this.getUserEndpoint()}/mailboxSettings/timeZone`);
const timezone = await handleErrorsJson<string>(response);
const timezoneResponse = await handleErrorsJson<string | { value: string }>(response);
if (!timezone || typeof timezone !== "string") {
if (typeof timezoneResponse === "object" && timezoneResponse !== null && "value" in timezoneResponse) {
const windowsTimezoneName = timezoneResponse.value;
this.log.info("timezone found in outlook mailbox settings (new format)", {
windowsTimezoneName,
});
try {
const ianaTimezone = findIana(windowsTimezoneName);
if (ianaTimezone && ianaTimezone.length > 0) {
const convertedTimezone = ianaTimezone[0];
this.log.info("Successfully converted Windows timezone to IANA", {
windowsTimezoneName,
convertedTimezone,
});
return convertedTimezone;
} else {
this.log.warn("Could not convert Windows timezone to IANA, using original value", {
windowsTimezoneName,
});
return windowsTimezoneName;
}
} catch (conversionError) {
this.log.warn("Error converting Windows timezone to IANA, using original value", {
windowsTimezoneName,
conversionError,
});
return windowsTimezoneName;
}
}
if (!timezoneResponse || typeof timezoneResponse !== "string") {
this.log.warn("No timezone found in outlook mailbox settings, defaulting to Europe/London", {
timezone,
timezoneResponse,
});
return "Europe/London";
}
this.log.info("timezone found in outlook mailbox settings", { timezone });
return timezone;
this.log.info("timezone found in outlook mailbox settings (legacy format)", {
timezone: timezoneResponse,
});
return timezoneResponse;
} catch (error) {
this.log.error("Error getting main timezone from Office365 Calendar", { error });
throw error;
@@ -8,7 +8,8 @@
"dependencies": {
"@calcom/lib": "workspace:*",
"@calcom/prisma": "workspace:*",
"msw": "^2.7.0"
"msw": "^2.7.0",
"windows-iana": "^5.1.0"
},
"devDependencies": {
"@calcom/types": "workspace:*"
@@ -73,7 +73,7 @@ export default function App({ Component, pageProps }: AppProps) {
}
}, []);
useEffect(() => {
if (!!selectedUser) {
if (selectedUser) {
setAccessToken(selectedUser.accessToken);
setUserEmail(selectedUser.email);
setUsername(selectedUser.username);
@@ -83,13 +83,15 @@ export default function App({ Component, pageProps }: AppProps) {
return (
<div className={`${poppins.className} text-black`}>
{options.length > 0 && (
<Select defaultValue={selectedUser} onChange={setSelectedUser} options={options} />
<Select
defaultValue={options.find((opt: TUser | null) => opt?.email.includes("lauris"))}
onChange={(opt: TUser | null) => setSelectedUser(opt)}
options={options}
/>
)}
<CalProvider
accessToken={accessToken}
// eslint-disable-next-line turbo/no-undeclared-env-vars
clientId={process.env.NEXT_PUBLIC_X_CAL_ID ?? ""}
// eslint-disable-next-line turbo/no-undeclared-env-vars
options={{ apiUrl: process.env.NEXT_PUBLIC_CALCOM_API_URL ?? "", refreshUrl: "/api/refresh" }}>
{email ? (
<>
+8
View File
@@ -3750,6 +3750,7 @@ __metadata:
"@calcom/prisma": "workspace:*"
"@calcom/types": "workspace:*"
msw: ^2.7.0
windows-iana: ^5.1.0
languageName: unknown
linkType: soft
@@ -49166,6 +49167,13 @@ __metadata:
languageName: node
linkType: hard
"windows-iana@npm:^5.1.0":
version: 5.1.0
resolution: "windows-iana@npm:5.1.0"
checksum: 0dc9ababde5f839e83a4bcd448347d8da563efd9e23ce61ac777e9992774ff85de2a9f343f87b80b37b181742fe4d783cff3ba39191cfedb6357ae7d5f58aadc
languageName: node
linkType: hard
"winston-transport@npm:^4.5.0, winston-transport@npm:^4.9.0":
version: 4.9.0
resolution: "winston-transport@npm:4.9.0"