-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Delay step stuck indefinitely due to timezone mismatch with PostgreSQL persistence #1419
Description
Describe the bug
When using WorkflowCore with PostgreSQL persistence provider and the Delay primitive step, workflows get stuck indefinitely at the Delay step instead of resuming after the specified period. This occurs in environments with non-UTC timezones (e.g., CET/UTC+1).
The root cause is a timezone conversion mismatch in the persistence layer:
- PostgreSQL stores timestamp with time zone columns correctly in UTC
- When EF Core reads these values, they are automatically converted to local timezone (e.g., CET)
- The ExtensionMethods.ToWorkflowInstance() method uses DateTime.SpecifyKind(..., DateTimeKind.Utc) which only changes the Kind flag without actually converting the value
- This causes a timezone offset equal to the server's UTC difference (e.g., +1 hour for CET)
To Reproduce
Steps to reproduce the behavior:
- Set up WorkflowCore with PostgreSQL persistence provider using timestamp with time zone columns
- Run the application in a non-UTC timezone environment (e.g., Europe/Rome, CET/UTC+1)
- Create a workflow with a Delay step (e.g., 10 seconds delay)
- Execute the workflow
- Observe that the Delay step never completes
Expected behavior
The Delay step should resume after exactly the specified period (e.g., 10 seconds), regardless of the server's timezone.
Additional context
Diagnostic logs showing the issue:
Workflow started: 12:30:21 (local time, CET/UTC+1) Delay saved: SleepUntil = 2026-03-26T11:30:31Z (UTC) Delay loaded: SleepUntil = 2026-03-26T12:30:31Z (UTC) <- +1 hour offset! Expected resume: 12:30:31 local (11:30:31 UTC) Actual resume: never (waiting for 12:30:31 UTC = 13:30:31 local)
The issue occurs in the ExtensionMethods.ToWorkflowInstance() method where DateTime values are read from the database. When using PostgreSQL with timestamp with time zone columns, EF Core returns DateTime objects with Kind=Local. The current code uses DateTime.SpecifyKind() which only changes the Kind flag without performing actual timezone conversion.
Workaround/Fix:
The issue can be fixed by properly converting timezone values instead of just changing the Kind flag. Replace DateTime.SpecifyKind() calls with proper timezone conversion logic that checks the DateTime.Kind property and uses ToUniversalTime() when needed.
This fix should be applied to all DateTime fields being read from the database:
- SleepUntil
- StartTime
- EndTime
- CreateTime
- CompleteTime
Environment:
WorkflowCore version: 3.17.0
Database: PostgreSQL with timestamp with time zone columns
ORM: Entity Framework Core
Timezone: CET (UTC+1)
OS: macOS / Linux
The fix involves replacing simple SpecifyKind calls with conditional logic that properly handles timezone conversion based on the DateTimeKind of the incoming value.