When creating a new event via the Node/JS SDK the event is 5 hours before the actual time

When I use the nylas events api via the node/JavaScript SDK I am able to schedule an event successfully. The problem I am experiencing is that the scheduled time is 5 hours before the ACTUAL time. For example, if the time is 14:00 (4pm) the time on my calendar will be scheduled for 09:00(9am). I’m using convex as my backend to process event scheduling. Any help to figure this out would be really helpful.

The code I am using is below:

'use node'
import Nylas  from 'nylas'
import { action, internalAction, mutation } from '../_generated/server'
import { v } from 'convex/values'
import { DateTime } from "luxon";

export const nylas = new Nylas({
    apiKey: process.env.NYLAS_API_KEY!,
    apiUri: process.env.NYLAS_API_URI!,
})

export const  createMeetingAction = action({
    args: {
        name: v.string(),
        email: v.string(),
        grandId: v.string(),
        grantEmail: v.string(),
        fromTime: v.string(),
        eventDate: v.string(),
        meetingLength: v.number(),
        title: v.string(),
        description: v.string(),
        provider: v.string(),
    },
    handler: async (ctx, args) => {
        const startDateTime = DateTime.fromISO(`${args.eventDate}T${args.fromTime}:00`)
        const endDateTime = startDateTime.plus({ minutes: args.meetingLength })
        await nylas.events.create({
            identifier: args.grandId,
            requestBody: {
                title: args.title,
                description: args.description,
                when: {
                    startTime: Math.floor(startDateTime.toMillis() / 1000),
                    endTime: Math.floor(endDateTime.toMillis() / 1000),
                    startTimezone: 'America/Chicago',
                    endTimezone: 'America/Chicago',
                },
                conferencing: {
                    autocreate: {},
                    provider: args.provider as any,
                },
                participants: [{
                    name: args.name,
                    email: args.email,
                    status: 'yes',
                }],
            },
            queryParams: {
                calendarId: args.grantEmail,
                notifyParticipants: true,
            }
        })
    }
})

Hey carter3689! Thanks for providing clear context with your question—makes it super easy to debug.

For the requestBody’s when field, the startTime and endTime are absolute Unix timestamps (UTC-based), meaning the timezone arguments (startTimezone and endTimezone) don’t affect these timestamps. They only tell calendar apps how to display the event to users.

To ensure your scheduled events appear at the right local time, explicitly factor in your intended timezone when constructing the datetime. Here’s how it should look:

const startDateTime = DateTime
  .fromISO(`${args.eventDate}T${args.fromTime}:00`, { zone: 'America/Chicago' })
  .toUTC();

const endDateTime = startDateTime.plus({ minutes: args.meetingLength });

This defines your desired timezone (America/Chicago), then converts the resulting datetime to UTC.

Let me know if that does the trick, and thanks again for the great question!

That worked!! I’ve been banging my head on this all week! :sweat_smile: Thank you SOOO much!!

@Isaac That does bring up a different question for me though. If I wanted to create events dynamically based on the user’s location would that be taken care of inside of the when section of the API call?

Hey I’m glad it worked, that’s fantastic!

Regarding dynamic timezone, this is a bit tricky (isn’t everything with coding and time though?)… The most common approach is to detect the user’s timezone on your frontend:

const userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;

Then pass that along to your backend in the API request. On your backend, explicitly set the timezone when constructing your startDateTime, and then convert that to UTC.

The startTimezone and endTimezone fields in the Nylas payload are informational only, they help calendar apps display the event properly in the specified timezone.

Here’s how that would look:

const startDateTime = DateTime
  .fromISO(`${args.eventDate}T${args.fromTime}:00`, { zone: args.timezone })
  .toUTC();

const endDateTime = startDateTime.plus({ minutes: args.meetingLength });

await nylas.events.create({
  identifier: args.grandId,
  requestBody: {
    title: args.title,
    description: args.description,
    when: {
      startTime: Math.floor(startDateTime.toMillis() / 1000),
      endTime: Math.floor(endDateTime.toMillis() / 1000),
      startTimezone: args.timezone, // helps calendar apps show the correct timezone to users
      endTimezone: args.timezone,   // same here
    },
    conferencing: {
      autocreate: {},
      provider: args.provider as any,
    },
    participants: [{
      name: args.name,
      email: args.email,
      status: 'yes',
    }],
  },
  queryParams: {
    calendarId: args.grantEmail,
    notifyParticipants: true,
  }
})

Note: It is safe to omit startTimezone and endTimezone from the when payload; calendar apps will then default to each user’s local timezone when displaying the event. Whether to omit or include depends on your app’s use case, happy to help you decide if you share some more business context!

Let me know if that helps!