读取 Outlook 日历事件时日期格式错误

问题描述 投票:0回答:1

通过 API,我读取我的 Outlook 日历并将其显示在 FullCalendar 库的日历中。

我的问题是在读取具有持续时间的事件时出现的,在我的网络日历中显示时在 Outlook 中创建的所有事件都会从开始和结束时间减去两个小时,我附上示例图片:

展望事件

Outlook Event

翻译:2024 年 6 月 5 日星期三下午 1:30 开始至下午 3:00

我的日历上的活动

event on my calendar

翻译:开始:06/05/2024 11:30 a.m. |结束于 06/05/2024 13:00 pm

两者都发生在 5 日,但 Outlook 中的原始事件于下午 1:30 开始。并于下午 3:00 结束我的日历显示好像是从上午 11:00 开始,到下午 1:30 结束,相隔两个小时。

在我的代码中,我在控制台中添加了一条消息,以查看通过 API 读取的时间是如何到达的,它以以下格式到达:Wed Jun 05 2024 11:30:00 GMT+0200(中欧夏令时间)

所以我认为从外观来看它到达的是错误的,但我很惊讶,因为我住的地方 GTM 是正确的。

这是获取事件的部分:

//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();
}

非常感谢!

编辑:虽然@derpirscher的解决方案工作得很好,但我添加了同事向我推荐的另一种方法,它也有效

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 => {
        // Convertir las fechas y horas a objetos de fecha considerando la zona horaria
        const start = new Date(event.start.dateTime);
        const end = new Date(event.end.dateTime);

        // Ajustar la hora para reflejar la zona horaria local
        const timeZoneOffset = start.getTimezoneOffset() * 60000; // Convertir minutos a milisegundos
        const localStart = new Date(start.getTime() - timeZoneOffset);
        const localEnd = new Date(end.getTime() - timeZoneOffset);

        return {
            title: event.subject,
            start: localStart.toISOString(), // Convertir a cadena ISO para asegurar la representación correcta de la fecha y hora
            end: localEnd.toISOString(),
            allDay: event.isAllDay
        };
    });

    return events;
}

javascript azure azure-active-directory calendar office365
1个回答
1
投票

您忽略了从 API 获取的时区信息。您的回复包含以下格式的时间戳

{
  dateTime: "2024-06-12T09:30:00.0000000", 
  timeZone: "UTC"
} 

但是在你的代码中,当读取 API 响应时,你只考虑

  ...
  start: event.start.dateTime,
  end: event.end.dateTime,
  ...

并忽略

event.start.timeZone
信息。

因此,在你的过程中的某个地方你可能会做类似的事情

new Date(start)

将时间字符串转换为

Date
对象。但由于时间字符串不包含任何有关时区的信息,因此它被认为是“本地时间”(即在您的情况下是 CEST,比 UTC 早 2 小时)。 Ie

"2024-06-12T09:30:00.0000000"

中的

UTC
"2024-06-12T11:30:00.0000000"
中的
CEST
是同一时间点。但是,当您在以 CEST 运行的计算机中执行
new Date("2024-06-12T09:30:00.0000000")
操作时,它将以 CEST 解释此时间字符串(因为它没有任何其他信息),从而导致时间戳发生 2 小时的偏移。
因此,在将字符串转换为 

Date

时,您必须考虑这些字符串所指的正确时区。一个非常简单的方法可能如下

检查 Outlook API 是否

始终

提供 UTC 时区的时间戳。如果是,只需在时间戳字符串中添加 Z 即可。这是日期解析的最简单标记,时间戳将被视为 UTC 时间戳。 IE

new Date("2024-06-12T09:30:00.0000000")
会给你
9:30 CEST
但是
new Date("2024-06-12T09:30:00.0000000Z")
会给你
9:30 UTC
,这与
11:30 CEST
相同
如果 Outlook API 还返回不同的时区,您必须以某种方式将该信息放入时间戳字符串中。但由于我不知道

timeZone

可以以什么格式交付,因此很难提供帮助......

    

© www.soinside.com 2019 - 2024. All rights reserved.