@@ -422,6 +422,92 @@ schedules:
422422 expect ( loaded . schedules . daily ! . sources ) . toEqual ( [ "app" ] ) ;
423423 } ) ;
424424
425+ // Timezone validation tests
426+ describe ( "timezone validation" , ( ) => {
427+ test ( "accepts valid schedule timezone" , async ( ) => {
428+ const config = {
429+ ...validConfig ,
430+ schedules : {
431+ daily : {
432+ cron : "0 2 * * *" ,
433+ retention : { maxCount : 7 , maxDays : 30 } ,
434+ timezone : "America/New_York" ,
435+ } ,
436+ } ,
437+ } ;
438+ const loaded = await writeAndLoad ( config ) ;
439+ expect ( loaded . schedules . daily ! . timezone ) . toBe ( "America/New_York" ) ;
440+ } ) ;
441+
442+ test ( "accepts schedule without timezone" , async ( ) => {
443+ const loaded = await writeAndLoad ( validConfig ) ;
444+ expect ( loaded . schedules . daily ! . timezone ) . toBeUndefined ( ) ;
445+ } ) ;
446+
447+ test ( "throws for non-string schedule timezone" , async ( ) => {
448+ const config = {
449+ ...validConfig ,
450+ schedules : {
451+ daily : {
452+ cron : "0 2 * * *" ,
453+ retention : { maxCount : 7 , maxDays : 30 } ,
454+ timezone : 123 ,
455+ } ,
456+ } ,
457+ } ;
458+ await expect ( writeAndLoad ( config ) ) . rejects . toThrow (
459+ "schedules.daily.timezone must be a string" ,
460+ ) ;
461+ } ) ;
462+
463+ test ( "accepts valid global scheduler timezone" , async ( ) => {
464+ const config = {
465+ ...validConfig ,
466+ scheduler : {
467+ timezone : "Europe/London" ,
468+ } ,
469+ } ;
470+ const loaded = await writeAndLoad ( config ) ;
471+ expect ( loaded . scheduler ?. timezone ) . toBe ( "Europe/London" ) ;
472+ } ) ;
473+
474+ test ( "accepts config without scheduler section" , async ( ) => {
475+ const loaded = await writeAndLoad ( validConfig ) ;
476+ expect ( loaded . scheduler ) . toBeUndefined ( ) ;
477+ } ) ;
478+
479+ test ( "throws for non-object scheduler" , async ( ) => {
480+ const config = {
481+ ...validConfig ,
482+ scheduler : "invalid" ,
483+ } ;
484+ await expect ( writeAndLoad ( config ) ) . rejects . toThrow ( "scheduler must be an object" ) ;
485+ } ) ;
486+
487+ test ( "throws for non-string scheduler.timezone" , async ( ) => {
488+ const config = {
489+ ...validConfig ,
490+ scheduler : {
491+ timezone : 123 ,
492+ } ,
493+ } ;
494+ await expect ( writeAndLoad ( config ) ) . rejects . toThrow ( "scheduler.timezone must be a string" ) ;
495+ } ) ;
496+
497+ test ( "accepts common IANA timezones" , async ( ) => {
498+ const timezones = [ "UTC" , "America/Los_Angeles" , "Europe/Paris" , "Asia/Tokyo" ] ;
499+
500+ for ( const tz of timezones ) {
501+ const config = {
502+ ...validConfig ,
503+ scheduler : { timezone : tz } ,
504+ } ;
505+ const loaded = await writeAndLoad ( config ) ;
506+ expect ( loaded . scheduler ?. timezone ) . toBe ( tz ) ;
507+ }
508+ } ) ;
509+ } ) ;
510+
425511 // Docker containerStop validation tests
426512 describe ( "docker containerStop validation" , ( ) => {
427513 const dockerBaseConfig = {
0 commit comments