Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 

Using System Time Instead of Workflow Time

Tip

time.Now() returns different values on replay, breaking determinism. Use workflow.Now() for the current time and workflow.Sleep() for delays -- both are deterministic and durable.

Using the system clock directly in workflow code is a common determinism violation. time.Now() in Go (or Date.now() in TypeScript, datetime.now() in Python) returns the current wall-clock time, which differs every time the code executes. Since workflow code re-executes during replay, this causes the workflow to potentially make different decisions.

using_system_time_instead_of_workflow_time/workflow.go

// BAD: system time in workflow code
func MyWorkflowV1Time(ctx workflow.Context) error {
	now := time.Now() // Different on replay!
	if now.Hour() < 12 {
		// Morning logic
	}
	return nil
}

using_system_time_instead_of_workflow_time/workflow.go

// GOOD: workflow time
func MyWorkflowV2Time(ctx workflow.Context) error {
	now := workflow.Now(ctx) // Same value on replay
	if now.Hour() < 12 {
		// Morning logic -- deterministic
	}
	return nil
}

using_system_time_instead_of_workflow_time/workflow.go

// BAD: language-native sleep
func MyWorkflowV1Sleep(ctx workflow.Context) error {
	time.Sleep(10 * time.Minute)
	return nil
}

using_system_time_instead_of_workflow_time/workflow.go

// GOOD: durable timer
func MyWorkflowV2Sleep(ctx workflow.Context) error {
	if err := workflow.Sleep(ctx, 10*time.Minute); err != nil {
		return err
	}
	return nil
}

Temporal timers (workflow.Sleep, workflow.NewTimer) are both deterministic and durable: they survive worker restarts and produce the same behavior on replay. In the TypeScript SDK, Date.now() and setTimeout are automatically patched inside workflow code.