Skip to content

Delay step stuck indefinitely due to timezone mismatch with PostgreSQL persistence #1419

@icrocket

Description

@icrocket

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:

  1. PostgreSQL stores timestamp with time zone columns correctly in UTC
  2. When EF Core reads these values, they are automatically converted to local timezone (e.g., CET)
  3. The ExtensionMethods.ToWorkflowInstance() method uses DateTime.SpecifyKind(..., DateTimeKind.Utc) which only changes the Kind flag without actually converting the value
  4. 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:

  1. Set up WorkflowCore with PostgreSQL persistence provider using timestamp with time zone columns
  2. Run the application in a non-UTC timezone environment (e.g., Europe/Rome, CET/UTC+1)
  3. Create a workflow with a Delay step (e.g., 10 seconds delay)
  4. Execute the workflow
  5. 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:

  1. SleepUntil
  2. StartTime
  3. EndTime
  4. CreateTime
  5. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions