@@ -27,7 +27,6 @@ export function validateCronExpression(
2727 }
2828
2929 try {
30- // Validate with timezone if provided for accurate next run calculation
3130 const cron = new Cron ( cronExpression , timezone ? { timezone } : undefined )
3231 const nextRun = cron . nextRun ( )
3332
@@ -324,13 +323,11 @@ export function generateCronExpression(
324323 * Uses Croner library with timezone support for accurate scheduling across timezones and DST transitions
325324 * @param scheduleType - Type of schedule (minutes, hourly, daily, etc)
326325 * @param scheduleValues - Object with schedule configuration values
327- * @param lastRanAt - Optional last execution time (currently unused, Croner calculates from current time)
328326 * @returns Date object for next execution time
329327 */
330328export function calculateNextRunTime (
331329 scheduleType : string ,
332- scheduleValues : ReturnType < typeof getScheduleTimeValues > ,
333- lastRanAt ?: Date | null
330+ scheduleValues : ReturnType < typeof getScheduleTimeValues >
334331) : Date {
335332 // Get timezone (default to UTC)
336333 const timezone = scheduleValues . timezone || 'UTC'
@@ -341,7 +338,7 @@ export function calculateNextRunTime(
341338 // If we have both a start date and time, use them together with timezone awareness
342339 if ( scheduleValues . scheduleStartAt && scheduleValues . scheduleTime ) {
343340 try {
344- logger . info (
341+ logger . debug (
345342 `Creating date with: startAt=${ scheduleValues . scheduleStartAt } , time=${ scheduleValues . scheduleTime } , timezone=${ timezone } `
346343 )
347344
@@ -351,7 +348,7 @@ export function calculateNextRunTime(
351348 timezone
352349 )
353350
354- logger . info ( `Combined date result: ${ combinedDate . toISOString ( ) } ` )
351+ logger . debug ( `Combined date result: ${ combinedDate . toISOString ( ) } ` )
355352
356353 // If the combined date is in the future, use it as our next run time
357354 if ( combinedDate > baseDate ) {
@@ -412,13 +409,10 @@ export function calculateNextRunTime(
412409 }
413410 }
414411
415- // For recurring schedules, use Croner with timezone support
416- // This ensures proper timezone handling and DST transitions
417412 try {
418413 const cronExpression = generateCronExpression ( scheduleType , scheduleValues )
419414 logger . debug ( `Using cron expression: ${ cronExpression } with timezone: ${ timezone } ` )
420415
421- // Create Croner instance with timezone support
422416 const cron = new Cron ( cronExpression , {
423417 timezone,
424418 } )
@@ -440,54 +434,24 @@ export function calculateNextRunTime(
440434}
441435
442436/**
443- * Helper function to get a friendly timezone abbreviation
437+ * Helper function to get a friendly timezone abbreviation.
438+ * Uses Intl.DateTimeFormat to get the correct abbreviation for the current time,
439+ * automatically handling DST transitions.
444440 */
445441function getTimezoneAbbreviation ( timezone : string ) : string {
446- const timezoneMap : Record < string , string > = {
447- // UTC
448- UTC : 'UTC' ,
449- // Americas
450- 'America/Los_Angeles' : 'PT' ,
451- 'America/Denver' : 'MT' ,
452- 'America/Chicago' : 'CT' ,
453- 'America/New_York' : 'ET' ,
454- 'America/Anchorage' : 'AKT' ,
455- 'Pacific/Honolulu' : 'HST' ,
456- 'America/Toronto' : 'ET' ,
457- 'America/Vancouver' : 'PT' ,
458- 'America/Mexico_City' : 'CST' ,
459- 'America/Sao_Paulo' : 'BRT' ,
460- 'America/Argentina/Buenos_Aires' : 'ART' ,
461- // Europe
462- 'Europe/London' : 'GMT/BST' ,
463- 'Europe/Paris' : 'CET/CEST' ,
464- 'Europe/Berlin' : 'CET/CEST' ,
465- 'Europe/Amsterdam' : 'CET/CEST' ,
466- 'Europe/Madrid' : 'CET/CEST' ,
467- 'Europe/Rome' : 'CET/CEST' ,
468- 'Europe/Moscow' : 'MSK' ,
469- // Middle East / Africa
470- 'Asia/Dubai' : 'GST' ,
471- 'Asia/Tel_Aviv' : 'IST' ,
472- 'Africa/Cairo' : 'EET' ,
473- 'Africa/Johannesburg' : 'SAST' ,
474- // Asia
475- 'Asia/Kolkata' : 'IST' ,
476- 'Asia/Bangkok' : 'ICT' ,
477- 'Asia/Jakarta' : 'WIB' ,
478- 'Asia/Singapore' : 'SGT' ,
479- 'Asia/Shanghai' : 'CST' ,
480- 'Asia/Hong_Kong' : 'HKT' ,
481- 'Asia/Seoul' : 'KST' ,
482- 'Asia/Tokyo' : 'JST' ,
483- // Australia / Pacific
484- 'Australia/Perth' : 'AWST' ,
485- 'Australia/Sydney' : 'AEST/AEDT' ,
486- 'Australia/Melbourne' : 'AEST/AEDT' ,
487- 'Pacific/Auckland' : 'NZST/NZDT' ,
488- }
442+ if ( timezone === 'UTC' ) return 'UTC'
489443
490- return timezoneMap [ timezone ] || timezone
444+ try {
445+ const formatter = new Intl . DateTimeFormat ( 'en-US' , {
446+ timeZone : timezone ,
447+ timeZoneName : 'short' ,
448+ } )
449+ const parts = formatter . formatToParts ( new Date ( ) )
450+ const tzPart = parts . find ( ( p ) => p . type === 'timeZoneName' )
451+ return tzPart ?. value || timezone
452+ } catch {
453+ return timezone
454+ }
491455}
492456
493457/**
@@ -500,13 +464,11 @@ function getTimezoneAbbreviation(timezone: string): string {
500464 */
501465export const parseCronToHumanReadable = ( cronExpression : string , timezone ?: string ) : string => {
502466 try {
503- // Use cronstrue for reliable cron expression parsing
504467 const baseDescription = cronstrue . toString ( cronExpression , {
505468 use24HourTimeFormat : false , // Use 12-hour format with AM/PM
506469 verbose : false , // Keep it concise
507470 } )
508471
509- // Add timezone information if provided and not UTC
510472 if ( timezone && timezone !== 'UTC' ) {
511473 const tzAbbr = getTimezoneAbbreviation ( timezone )
512474 return `${ baseDescription } (${ tzAbbr } )`
@@ -518,7 +480,6 @@ export const parseCronToHumanReadable = (cronExpression: string, timezone?: stri
518480 cronExpression,
519481 error : error instanceof Error ? error . message : String ( error ) ,
520482 } )
521- // Fallback to displaying the raw cron expression
522483 return `Schedule: ${ cronExpression } ${ timezone && timezone !== 'UTC' ? ` (${ getTimezoneAbbreviation ( timezone ) } )` : '' } `
523484 }
524485}
@@ -548,7 +509,6 @@ export const getScheduleInfo = (
548509 let scheduleTiming = 'Unknown schedule'
549510
550511 if ( cronExpression ) {
551- // Pass timezone to parseCronToHumanReadable for accurate display
552512 scheduleTiming = parseCronToHumanReadable ( cronExpression , timezone || undefined )
553513 } else if ( scheduleType ) {
554514 scheduleTiming = `${ scheduleType . charAt ( 0 ) . toUpperCase ( ) + scheduleType . slice ( 1 ) } `
0 commit comments