通过API读取Outlook日历事件时的时间偏移问题求助
问题:Outlook日历事件在FullCalendar中显示时间偏移2小时
问题现象
Outlook中创建的带时长事件(如2024年6月5日13:30-15:00),通过Microsoft Graph API获取后,在FullCalendar网页日历中显示为11:30-13:00,整体时间被减去2小时。控制台打印的API返回时间格式为:Wed Jun 05 2024 11:30:00 GMT+0200 (Central European Summer Time),而本地时区正是GMT+0200。
代码现状
获取事件的原代码
//EVENTS async function getCalendarEvents(accessToken) { const response = await fetch('https://graph.microsoft.com/v1.0/me/events', { headers: { 'Authorization': `Bearer ${accessToken}` } }); if (!response.ok) { const errorText = await response.text(); console.error(`Error al obtener los eventos del calendario: ${response.status} ${response.statusText}`, errorText); throw new Error('Error al obtener los eventos del calendario'); } const data = await response.json(); return data.value.map(event => ({ title: event.subject, start: event.start.dateTime, end: event.end.dateTime, allDay: event.isAllDay })); }
渲染日历的代码
function renderCalendar(events) { const calendarEl = document.getElementById('calendar'); const calendar = new FullCalendar.Calendar(calendarEl, { headerToolbar: { left: 'prev,next today', center: 'title', right: 'dayGridMonth,timeGridWeek,timeGridDay' }, buttonText: { today: 'Hoy', month: 'Mes', week: 'Semana', day: 'Día', list: 'Lista' }, initialView: 'dayGridMonth', locale: 'es', events: events, eventClick: function(info) { // Get the event properties const event = info.event; const title = event.title; const start = event.start; const end = event.end; const allDay = event.allDay; const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone; const startFormatted = start.toLocaleString('es-ES', { timeZone: timeZone }); const endFormatted = end ? end.toLocaleString('es-ES', { timeZone: timeZone }) : 'Evento de todo el día'; // Create the event information string let content = `<h2>${title}</h2>`; if (!allDay) { content += `<p><strong>Inicio:</strong> ${startFormatted}</p>`; content += `<p><strong>Fin:</strong> ${endFormatted}</p>`; } else { content += `<p><strong>Evento de todo el día</strong></p>`; } // Show SweetAlert2 popup with event information Swal.fire({ title: 'Información del Evento', html: content, icon: 'info' }); console.log(info.event); console.log(title); console.log(start); console.log(end); console.log(allDay); } }); calendar.render(); }
问题原因
Microsoft Graph API默认返回的dateTime字段是UTC时间,即使字符串中包含时区标识(如GMT+0200),FullCalendar在解析时仍会将其视为UTC时间再转换一次本地时区,导致重复偏移,最终显示时间比实际晚2小时。
解决方案
方案1:让Graph API直接返回本地时区时间(@derpirscher方案)
在请求Graph API时添加Prefer头指定本地时区,API会直接返回对应时区的时间,无需额外转换:
async function getCalendarEvents(accessToken) { const response = await fetch('https://graph.microsoft.com/v1.0/me/events', { headers: { 'Authorization': `Bearer ${accessToken}`, // 指定本地时区,示例为中欧夏令时,可替换为实际时区ID(如"Europe/Paris") 'Prefer': 'outlook.timezone="Central European Summer Time"' } }); if (!response.ok) { const errorText = await response.text(); console.error(`Error al obtener los eventos del calendario: ${response.status} ${response.statusText}`, errorText); throw new Error('Error al obtener los eventos del calendario'); } const data = await response.json(); return data.value.map(event => ({ title: event.subject, start: event.start.dateTime, end: event.end.dateTime, allDay: event.isAllDay })); }
方案2:手动调整时区偏移(同事推荐方案)
通过JavaScript手动将API返回的UTC时间转换为本地时间,再传给FullCalendar:
async function getCalendarEvents_Alt(accessToken) { const response = await fetch('https://graph.microsoft.com/v1.0/me/events', { headers: { 'Authorization': `Bearer ${accessToken}` } }); if (!response.ok) { const errorText = await response.text(); console.error(`Error al obtener los eventos del calendario: ${response.status} ${response.statusText}`, errorText); throw new Error('Error al obtener los eventos del calendario'); } const data = await response.json(); const events = data.value.map(event => { // 将API返回的时间字符串转为Date对象 const start = new Date(event.start.dateTime); const end = new Date(event.end.dateTime); // 计算本地时区偏移(分钟转毫秒),调整为本地时间 const timeZoneOffset = start.getTimezoneOffset() * 60000; const localStart = new Date(start.getTime() - timeZoneOffset); const localEnd = new Date(end.getTime() - timeZoneOffset); return { title: event.subject, start: localStart.toISOString(), // 转为ISO格式字符串确保正确解析 end: localEnd.toISOString(), allDay: event.isAllDay }; }); return events; }
内容的提问来源于stack exchange,提问作者Hugo Jiménez
相关产品推荐
相关产品推荐

