@@ -351,6 +351,41 @@ func runExportOnce(cfg *config.Config, log logger.Logger, exportType, exportMode
351351 })
352352}
353353
354+ // getConfiguredTimezone returns the configured timezone or UTC as fallback
355+ func getConfiguredTimezone (cfg * config.Config , log logger.Logger ) * time.Location {
356+ // Try environment variable first (Docker TZ)
357+ if tz := os .Getenv ("TZ" ); tz != "" {
358+ if loc , err := time .LoadLocation (tz ); err == nil {
359+ log .Info ("scheduler.using_env_timezone" , map [string ]interface {}{
360+ "timezone" : tz ,
361+ })
362+ return loc
363+ }
364+ log .Warn ("scheduler.invalid_env_timezone" , map [string ]interface {}{
365+ "timezone" : tz ,
366+ })
367+ }
368+
369+ // Try config timezone
370+ if cfg .Export .Timezone != "" {
371+ if loc , err := time .LoadLocation (cfg .Export .Timezone ); err == nil {
372+ log .Info ("scheduler.using_config_timezone" , map [string ]interface {}{
373+ "timezone" : cfg .Export .Timezone ,
374+ })
375+ return loc
376+ }
377+ log .Warn ("scheduler.invalid_config_timezone" , map [string ]interface {}{
378+ "timezone" : cfg .Export .Timezone ,
379+ })
380+ }
381+
382+ // Fallback to UTC
383+ log .Info ("scheduler.using_default_timezone" , map [string ]interface {}{
384+ "timezone" : "UTC" ,
385+ })
386+ return time .UTC
387+ }
388+
354389// runWithSchedule sets up a cron scheduler and runs the export according to the schedule
355390func runWithSchedule (cfg * config.Config , log logger.Logger , schedule , exportType , exportMode string ) {
356391 log .Info ("scheduler.initializing" , map [string ]interface {}{
@@ -373,6 +408,9 @@ func runWithSchedule(cfg *config.Config, log logger.Logger, schedule, exportType
373408 })
374409 }
375410
411+ // Get configured timezone for display
412+ configuredTZ := getConfiguredTimezone (cfg , log )
413+
376414 // Validate cron expression
377415 cronParser := cron .NewParser (cron .Minute | cron .Hour | cron .Dom | cron .Month | cron .Dow )
378416 _ , err := cronParser .Parse (schedule )
@@ -419,7 +457,7 @@ func runWithSchedule(cfg *config.Config, log logger.Logger, schedule, exportType
419457 "export_type" : exportType ,
420458 "export_mode" : exportMode ,
421459 "duration" : duration .String (),
422- "next_run" : c .Entries ()[0 ].Next .Format (time .RFC3339 ),
460+ "next_run" : c .Entries ()[0 ].Next .In ( configuredTZ ). Format (time .RFC3339 ),
423461 })
424462 })
425463
@@ -441,38 +479,45 @@ func runWithSchedule(cfg *config.Config, log logger.Logger, schedule, exportType
441479 c .Start ()
442480 log .Info ("scheduler.cron_started" , nil )
443481
444- // Get the next run time
482+ // Get the next run time and display in configured timezone
445483 entries := c .Entries ()
446484 if len (entries ) > 0 {
447485 nextRun := entries [0 ].Next
486+ nextRunInTZ := nextRun .In (configuredTZ )
487+
448488 log .Info ("scheduler.started" , map [string ]interface {}{
449- "schedule" : schedule ,
450- "entry_id" : entryID ,
451- "next_run" : nextRun .Format (time .RFC3339 ),
452- "next_run_local" : nextRun .Format ("2006-01-02 15:04:05 MST" ),
489+ "schedule" : schedule ,
490+ "entry_id" : entryID ,
491+ "next_run" : nextRun .Format (time .RFC3339 ),
492+ "next_run_local" : nextRunInTZ .Format ("2006-01-02 15:04:05 MST" ),
493+ "timezone" : configuredTZ .String (),
453494 })
454495 fmt .Printf ("Scheduler started successfully!\n " )
455496 fmt .Printf ("Schedule: %s\n " , schedule )
456497 fmt .Printf ("Export Type: %s\n " , exportType )
457498 fmt .Printf ("Export Mode: %s\n " , exportMode )
458- fmt .Printf ("Next run: %s\n " , nextRun .Format ("2006-01-02 15:04:05 MST" ))
499+ fmt .Printf ("Timezone: %s\n " , configuredTZ .String ())
500+ fmt .Printf ("Next run: %s\n " , nextRunInTZ .Format ("2006-01-02 15:04:05 MST" ))
459501
460- // Log upcoming executions for the next hour
502+ // Log upcoming executions for the next hour in configured timezone
461503 now := time .Now ()
462504 oneHourLater := now .Add (time .Hour )
463505 log .Info ("scheduler.upcoming_executions_preview" , map [string ]interface {}{
464506 "next_hour_from" : now .Format (time .RFC3339 ),
465507 "next_hour_to" : oneHourLater .Format (time .RFC3339 ),
508+ "timezone" : configuredTZ .String (),
466509 })
467510
468511 count := 0
469512 if len (entries ) > 0 {
470513 entry := entries [0 ]
471514 nextExec := entry .Next
472515 for nextExec .Before (oneHourLater ) && count < 10 {
516+ nextExecInTZ := nextExec .In (configuredTZ )
473517 log .Info ("scheduler.upcoming_execution" , map [string ]interface {}{
474- "execution_time" : nextExec .Format ("2006-01-02 15:04:05 MST" ),
518+ "execution_time" : nextExecInTZ .Format ("2006-01-02 15:04:05 MST" ),
475519 "in_minutes" : int (time .Until (nextExec ).Minutes ()),
520+ "timezone" : configuredTZ .String (),
476521 })
477522 // Calculate next execution after this one
478523 schedule , _ := cronParser .Parse (schedule )
0 commit comments