cron

package
v0.1.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 1, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

README

Candace cron

cron is a durable in-process scheduler for Candace Go services. It runs as part of the service lifecycle; it is not a separate daemon. An IStore is required, and PostgreSQL is the production implementation.

import (
	"context"
	"database/sql"
	"time"

	cron "github.com/candacelabs/csf/pkg/cron"
	cronpostgres "github.com/candacelabs/csf/pkg/cron/postgres"
	"github.com/gin-gonic/gin"
	_ "github.com/jackc/pgx/v5/stdlib"
	"golang.org/x/sync/errgroup"
)

func run(ctx context.Context, databaseURL string) error {
	db, err := sql.Open("pgx", databaseURL)
	if err != nil {
		return err
	}
	defer db.Close()

	store, err := cronpostgres.NewStore(db)
	if err != nil {
		return err
	}
	scheduler, err := cron.New(
		cron.WithStore(store),
		cron.WithJob(
			"daily-rollup",
			cron.Spec(cron.Daily(cron.At(3).AM())),
			buildDailyRollup,
			cron.WithCatchUp(cron.CatchUpAll),
			cron.WithOverlap(cron.OverlapAllow),
		),
		cron.WithJob(
			"cache-refresh",
			cron.Spec(cron.Every(15*time.Minute)),
			refreshCache,
		),
	)
	if err != nil {
		return err
	}

	router := gin.New()
	scheduler.Register(router.Group("/internal")) // GET /internal/cron only

	group, groupContext := errgroup.WithContext(ctx)
	group.Go(func() error { return scheduler.Run(groupContext) })
	group.Go(func() error { return serveHTTP(groupContext, router) })
	return group.Wait()
}

func buildDailyRollup(ctx context.Context, invocation cron.Invocation) error {
	// invocation.ID is the stable idempotency key for this scheduled instant.
	return nil
}

Declaring a schedule

Schedules are typed builders rather than strings. Each one normalizes to a canonical five-field cron form, and that string — not the builder call — is what a JobDefinition persists and the status route reports. These pairs are pinned by schedule_test.go:

Declaration Canonical()
cron.Spec(cron.Daily(cron.At(3).PM())) 0 15 * * *
cron.Spec(cron.Weekly(time.Monday, cron.At(8, 30).AM())) 30 8 * * 1
cron.Spec(cron.Monthly(1, cron.Noon())) 0 12 1 * *
cron.Spec(cron.LastDayOfMonth(cron.Midnight())) 0 0 L * *
cron.Spec(cron.Every(15 * time.Minute)) @every 15m0s
cron.Spec(cron.Raw("*/15 2-3 * * 1,3")) */15 2-3 * * 1,3

Schedule.String() is the human rendering of the same declaration: the first row reads daily at 3:00 PM (UTC).

At returns a MeridiemTime, not a TimeOfDay, so a twelve-hour clock declaration cannot reach a schedule until it says which half of the day it means — the ambiguous spelling is a compile error rather than a job that fires twelve hours off:

cron.Daily(cron.At(3))        // does not compile: MeridiemTime is not a TimeOfDay
cron.Daily(cron.At(3).PM())   // 15:00
cron.Daily(cron.At24(15))     // the same instant on a 24-hour clock

Spec schedules in UTC. Schedule.In(location) returns a copy in another location, and neither builder panics: an invalid declaration is carried until Validate, Canonical, or Next reports it.

Policies and their defaults

Both defaults are the conservative choice, and both are per job:

Job option Values Default Effect
WithCatchUp CatchUpNone · CatchUpLatest · CatchUpAll CatchUpNone What to do with occurrences missed while the process was down: skip past all of them (traditional cron), run only the most recent, or run every one up to the catch-up limit.
WithOverlap OverlapSkip · OverlapAllow OverlapSkip Whether a second occurrence may run while another holds a live lease. Enforced by the IStore, so it holds across processes sharing one database, not just within one.

Service-wide options: WithStore (required, no implicit default), WithLeaseDuration (30s; active jobs renew three times per duration), WithCatchUpLimit (1,000 due occurrences per job per cycle), and WithLeaseOwner for callers that already have a stable replica identity — New generates a random one otherwise.

Apply the relational migration in postgres/migrations/000001_create_cron_jobs_and_runs.up.sql through the owning service's normal migration runner. Queries are generated and checked in with:

./pkg/cron/postgres/generate.sh write
./pkg/cron/postgres/generate.sh check

The PostgreSQL integration suite is opt-in locally. Point it at a disposable database whose name ends in _test, then run the complete cron suite from the candace module root:

CANDACE_CRON_TEST_DATABASE_URL='postgresql://cron:cron@localhost:5432/candace_cron_test?sslmode=disable' \
  go test -race ./pkg/cron/...

The integration harness rejects database names without the _test suffix and runs each suite in a unique schema that it removes afterward. It never reuses the scheduler tables in public.

The PostgreSQL model is ordinary typed relational state: job definitions, schedule columns, cursors, occurrences, attempts, and fenced leases. The Liquid Proto messages under cron/v1 are only portable HTTP or messaging contracts; protobuf wire bytes are never stored in the database.

Execution is at least once. A lease can expire after the handler produced an external side effect but before completion was recorded, so handlers must use Invocation.ID as an idempotency key. CatchUpNone, CatchUpLatest, and CatchUpAll control missed occurrences; OverlapSkip is conservative by default, while OverlapAllow permits concurrent attempts. Handlers must honor context cancellation; Run drains cooperative handlers before returning.

The status snapshot includes active jobs and the 1,000 most recent durable occurrences, keeping the read-only Gin endpoint bounded as history grows.

For tests or deliberately disposable processes, opt into memory explicitly:

scheduler, err := cron.New(
	cron.WithStore(cron.NewMemoryStore()),
	cron.WithJob("test-job", cron.Spec(cron.Daily(cron.Noon())), handler),
)

MemoryStore never pretends to be durable and is never selected implicitly.

Documentation

Overview

Package cron provides durable in-process scheduling with human-readable declarations and explicit state stores.

Index

Constants

View Source
const (

	// SnapshotOccurrenceLimit bounds the recent occurrence history returned by
	// IStore.Snapshot and the read-only status route.
	SnapshotOccurrenceLimit = 1_000
)
View Source
const StatusPath = "/cron"

StatusPath is the sole read-only route mounted by Register.

Variables

View Source
var (
	// ErrInvalidConfiguration reports a constructor or persisted-definition
	// value that cannot form a safe scheduler.
	ErrInvalidConfiguration = errors.New("cron: invalid configuration")
	// ErrStoreRequired reports a Service constructed without an explicit IStore.
	ErrStoreRequired = errors.New("cron: store is required")
	// ErrNoJobs reports a Service constructed without any static jobs.
	ErrNoJobs = errors.New("cron: at least one job is required")
	// ErrJobNotFound reports a store operation for a job absent from the latest
	// static reconciliation.
	ErrJobNotFound = errors.New("cron: job not found")
	// ErrLeaseLost reports a stale or expired lease token. It is a fencing error:
	// the caller must not continue acting as the occurrence owner.
	ErrLeaseLost = errors.New("cron: occurrence lease lost")
	// ErrOccurrenceConflict reports reused occurrence identity with different
	// job or scheduled-time data.
	ErrOccurrenceConflict = errors.New("cron: occurrence identity conflict")
	// ErrOccurrenceRunning reports an attempt to skip an occurrence that is
	// already executing.
	ErrOccurrenceRunning = errors.New("cron: occurrence is running")
	// ErrAlreadyRunning reports concurrent Run calls on one Service.
	ErrAlreadyRunning = errors.New("cron: service is already running")
)

Functions

func OccurrenceID

func OccurrenceID(jobName string, scheduledAt time.Time) string

OccurrenceID returns the stable identity for one job's scheduled instant.

Types

type CatchUpPolicy

type CatchUpPolicy string

CatchUpPolicy controls which occurrences missed while the service was not running are considered when it starts again.

const (
	// CatchUpNone advances past every startup occurrence without invoking it.
	// This is the conservative default and matches traditional cron behavior.
	CatchUpNone CatchUpPolicy = "none"
	// CatchUpLatest invokes only the latest startup occurrence.
	CatchUpLatest CatchUpPolicy = "latest"
	// CatchUpAll invokes every startup occurrence, up to the service catch-up
	// limit. Overlap policy is still enforced while those invocations run.
	CatchUpAll CatchUpPolicy = "all"
)

type ClaimDisposition

type ClaimDisposition string

ClaimDisposition explains the durable outcome of Claim.

const (
	ClaimAcquired        ClaimDisposition = "acquired"
	ClaimAlreadyTerminal ClaimDisposition = "already_terminal"
	ClaimLeaseHeld       ClaimDisposition = "lease_held"
	ClaimSkippedOverlap  ClaimDisposition = "skipped_overlap"
)

type ClaimRequest

type ClaimRequest struct {
	OccurrenceID string
	JobName      string
	ScheduledAt  time.Time
	NextRunAt    time.Time
	LeaseOwner   string
	LeaseToken   string
	ClaimedAt    time.Time
	LeaseUntil   time.Time
}

ClaimRequest atomically advances the job cursor and attempts to acquire the occurrence's fenced lease.

type ClaimResult

type ClaimResult struct {
	Disposition ClaimDisposition
	Occurrence  OccurrenceRecord
}

ClaimResult is idempotent for a deterministic occurrence ID.

type Completion

type Completion struct {
	OccurrenceID string
	LeaseToken   string
	Status       OccurrenceStatus
	FinishedAt   time.Time
	Error        string
}

Completion terminally records one acquired invocation.

type IStore

type IStore interface {
	Reconcile(ctx context.Context, definitions []JobDefinition, now time.Time) ([]JobState, error)
	Claim(ctx context.Context, request ClaimRequest) (ClaimResult, error)
	Renew(ctx context.Context, renewal LeaseRenewal) error
	Complete(ctx context.Context, completion Completion) error
	Skip(ctx context.Context, request SkipRequest) error
	Expired(ctx context.Context, now time.Time, limit int) ([]OccurrenceRecord, error)
	Snapshot(ctx context.Context) (StoreSnapshot, error)
}

IStore is the durable scheduler boundary. Implementations must make Claim and Skip atomic with the associated NextRunAt advance. Complete and Renew must fence on LeaseToken. Reconcile must replace the active static definition set, preserve an already established interval anchor, and leave abandoned runs discoverable through Expired without rewinding the normal job cursor.

type Invocation

type Invocation struct {
	ID          string    `json:"id"`
	JobName     string    `json:"job_name"`
	ScheduledAt time.Time `json:"scheduled_at"`
	StartedAt   time.Time `json:"started_at"`
	Attempt     uint32    `json:"attempt"`
}

Invocation identifies the logical occurrence passed to a job handler.

type JobDefinition

type JobDefinition struct {
	Name     string             `json:"name"`
	Schedule ScheduleDefinition `json:"schedule"`
	CatchUp  CatchUpPolicy      `json:"catch_up"`
	Overlap  OverlapPolicy      `json:"overlap"`
}

JobDefinition is the static, persistence-neutral declaration reconciled at startup. Adapters map it to their own SQLC or wire types at the boundary.

type JobFunc

type JobFunc func(ctx context.Context, invocation Invocation) error

JobFunc executes one scheduled invocation. It should stop promptly when ctx is cancelled. Returning an error records a failed occurrence; it does not stop the scheduler.

type JobOption

type JobOption func(config *jobConfig) error

JobOption configures one job registered with WithJob.

func WithCatchUp

func WithCatchUp(policy CatchUpPolicy) JobOption

WithCatchUp sets a job's missed-occurrence policy.

func WithOverlap

func WithOverlap(policy OverlapPolicy) JobOption

WithOverlap sets a job's concurrent-occurrence policy.

type JobState

type JobState struct {
	Definition JobDefinition `json:"definition"`
	NextRunAt  time.Time     `json:"next_run_at"`
	CreatedAt  time.Time     `json:"created_at"`
	UpdatedAt  time.Time     `json:"updated_at"`
}

JobState is the durable scheduling cursor for one active definition.

type LeaseRenewal

type LeaseRenewal struct {
	OccurrenceID string
	LeaseToken   string
	RenewedAt    time.Time
	LeaseUntil   time.Time
}

LeaseRenewal extends a live lease while preserving its fencing token.

type MemoryStore

type MemoryStore struct {
	// contains filtered or unexported fields
}

MemoryStore is an explicit process-local IStore for tests and disposable services. It implements the same lease and reconciliation semantics as a durable adapter, but intentionally does not survive process restart.

func NewMemoryStore

func NewMemoryStore() *MemoryStore

NewMemoryStore returns an empty process-local store.

func (*MemoryStore) Claim

func (store *MemoryStore) Claim(ctx context.Context, request ClaimRequest) (ClaimResult, error)

Claim implements atomically fenced occurrence acquisition for MemoryStore.

func (*MemoryStore) Complete

func (store *MemoryStore) Complete(ctx context.Context, completion Completion) error

Complete implements fenced terminal recording for MemoryStore.

func (*MemoryStore) Expired

func (store *MemoryStore) Expired(ctx context.Context, now time.Time, limit int) ([]OccurrenceRecord, error)

Expired returns a bounded stable-order view of abandoned running occurrences for active jobs. It does not mutate leases or scheduling cursors; callers recover records through Claim's normal fencing path.

func (*MemoryStore) Reconcile

func (store *MemoryStore) Reconcile(
	ctx context.Context,
	definitions []JobDefinition,
	now time.Time,
) ([]JobState, error)

Reconcile implements static startup reconciliation for MemoryStore.

func (*MemoryStore) Renew

func (store *MemoryStore) Renew(ctx context.Context, renewal LeaseRenewal) error

Renew implements fenced lease renewal for MemoryStore.

func (*MemoryStore) Skip

func (store *MemoryStore) Skip(ctx context.Context, request SkipRequest) error

Skip implements idempotent terminal skip recording for MemoryStore.

func (*MemoryStore) Snapshot

func (store *MemoryStore) Snapshot(ctx context.Context) (StoreSnapshot, error)

Snapshot returns a defensive, stable-order copy of MemoryStore state.

type MeridiemTime

type MeridiemTime struct {
	// contains filtered or unexported fields
}

MeridiemTime is deliberately distinct from TimeOfDay. It can only become a schedule time after AM or PM is selected, preventing Daily(At(3)).

func At

func At(hour int, minute ...int) MeridiemTime

At starts a 12-hour clock declaration. It accepts an optional minute.

func (MeridiemTime) AM

func (value MeridiemTime) AM() TimeOfDay

AM completes a 12-hour declaration.

func (MeridiemTime) PM

func (value MeridiemTime) PM() TimeOfDay

PM completes a 12-hour declaration.

type OccurrenceRecord

type OccurrenceRecord struct {
	ID           string           `json:"id"`
	JobName      string           `json:"job_name"`
	ScheduledAt  time.Time        `json:"scheduled_at"`
	Status       OccurrenceStatus `json:"status"`
	Attempt      uint32           `json:"attempt"`
	StartedAt    time.Time        `json:"started_at,omitempty"`
	FinishedAt   time.Time        `json:"finished_at,omitempty"`
	LeaseOwner   string           `json:"lease_owner,omitempty"`
	LeaseToken   string           `json:"-"`
	LeaseUntil   time.Time        `json:"lease_until,omitempty"`
	Error        string           `json:"error,omitempty"`
	SkipReason   string           `json:"skip_reason,omitempty"`
	LastModified time.Time        `json:"last_modified"`
}

OccurrenceRecord is the durable execution record for one scheduled instant. LeaseToken is a fencing secret used only by IStore implementations and is deliberately omitted from JSON snapshots.

type OccurrenceStatus

type OccurrenceStatus string

OccurrenceStatus is the durable terminal or running state of an occurrence.

const (
	OccurrenceRunning   OccurrenceStatus = "running"
	OccurrenceSucceeded OccurrenceStatus = "succeeded"
	OccurrenceFailed    OccurrenceStatus = "failed"
	OccurrenceCanceled  OccurrenceStatus = "canceled"
	OccurrenceSkipped   OccurrenceStatus = "skipped"
)

type Option

type Option func(config *serviceConfig) error

Option configures a Service. New validates the complete option set before reconciling or starting any work.

func WithCatchUpLimit

func WithCatchUpLimit(limit int) Option

WithCatchUpLimit bounds the number of due occurrences one job may process in a single scheduling cycle.

func WithJob

func WithJob(name string, schedule Schedule, handler JobFunc, options ...JobOption) Option

WithJob registers a statically declared named job.

func WithLeaseDuration

func WithLeaseDuration(duration time.Duration) Option

WithLeaseDuration sets how long an invocation lease remains valid without a renewal. Active jobs renew their lease three times per duration.

func WithLeaseOwner

func WithLeaseOwner(owner string) Option

WithLeaseOwner sets the process identity recorded on leases. Most callers should use the random identity generated by New; this option is useful when an operator already has a stable, unique replica identity.

func WithStore

func WithStore(store IStore) Option

WithStore selects the required durable state store. Use NewMemoryStore explicitly when process-local state is sufficient.

type OverlapPolicy

type OverlapPolicy string

OverlapPolicy controls whether two occurrences of one job may execute at the same time. It is enforced by IStore, so it also covers multiple processes sharing a durable store.

const (
	// OverlapSkip records a skipped occurrence when another occurrence of the
	// same job owns a live lease. This is the default.
	OverlapSkip OverlapPolicy = "skip"
	// OverlapAllow permits concurrent occurrences of the same job.
	OverlapAllow OverlapPolicy = "allow"
)

type Rule

type Rule struct {
	// contains filtered or unexported fields
}

Rule is an opaque schedule declaration constructed by the fluent helpers. It intentionally has no exported fields so a caller cannot construct an unvalidated wire-shaped schedule.

func Daily

func Daily(at TimeOfDay) Rule

Daily returns a rule that fires every local calendar day at at.

func Every

func Every(interval time.Duration) Rule

Every returns an interval rule. Its cadence is anchored when Anchor is supplied by the durable runtime; absent an explicit anchor, Next uses the Unix epoch as a stable default rather than process start time.

func LastDayOfMonth

func LastDayOfMonth(at TimeOfDay) Rule

LastDayOfMonth returns a rule that fires on each month's final local day.

func Monthly

func Monthly(day int, at TimeOfDay) Rule

Monthly returns a rule that fires on day (1 through 31). Months without that day are skipped.

func Raw

func Raw(expression string) Rule

Raw declares a five-field cron expression. Raw is an escape hatch; it is parsed and normalized by Spec/Validate, never persisted as a wire blob.

func Weekly

func Weekly(day time.Weekday, at TimeOfDay) Rule

Weekly returns a rule that fires every week at at.

type Schedule

type Schedule struct {
	// contains filtered or unexported fields
}

Schedule is an immutable, validated-on-use scheduling definition. A zero Schedule is invalid. Its location defaults to UTC unless In is called.

Typed-rule DST semantics are deliberately instant-based: Next walks real UTC minutes and matches their local civil representation. Spring-forward civil times do not run because they do not exist; a matching repeated fall-back time runs once for each real instant. Raw rules use robfig/cron's semantics.

func ScheduleFromDefinition

func ScheduleFromDefinition(definition ScheduleDefinition) (Schedule, error)

ScheduleFromDefinition reconstructs and validates a Schedule from its neutral domain projection. It rejects a mismatched Canonical value so an adapter cannot silently reinterpret persisted schedule fields.

func Spec

func Spec(rule Rule) Schedule

Spec wraps a rule in a Schedule. It does not panic: invalid declarations are retained and reported by Validate, Canonical, or Next.

func (Schedule) Anchor

func (schedule Schedule) Anchor(anchor time.Time) Schedule

Anchor returns a copy with a durable interval anchor. It applies only to Every rules and lets a runtime retain cadence across restarts. The anchor is normalized to PostgreSQL's microsecond timestamp precision.

func (Schedule) Canonical

func (schedule Schedule) Canonical() (string, error)

Canonical returns a normalized five-field cron expression, or @every for an interval. The separate location and anchor are intentionally not encoded in that expression.

func (Schedule) Definition

func (schedule Schedule) Definition() (ScheduleDefinition, error)

Definition returns a structured, normalized, persistence-ready projection.

func (Schedule) In

func (schedule Schedule) In(location *time.Location) Schedule

In returns a copy scheduled in location. A nil location is reported by Validate instead of panicking.

func (Schedule) IntervalAnchor

func (schedule Schedule) IntervalAnchor() (time.Time, bool)

IntervalAnchor reports the explicit durable anchor, if any.

func (Schedule) Location

func (schedule Schedule) Location() *time.Location

Location returns the schedule location after validation. UTC is the default.

func (Schedule) Next

func (schedule Schedule) Next(after time.Time) (time.Time, error)

Next returns the first matching real instant strictly after after. Raw rules delegate five-field parsing and occurrence evaluation to robfig/cron. Typed calendar rules are bounded to five years so malformed state cannot spin.

func (Schedule) String

func (schedule Schedule) String() string

String is the human-readable representation for logs and status pages.

func (Schedule) Validate

func (schedule Schedule) Validate() error

Validate verifies and compiles all schedule input without panics.

type ScheduleDefinition

type ScheduleDefinition struct {
	Kind      ScheduleKind `json:"kind"`
	Timezone  string       `json:"timezone"`
	Canonical string       `json:"canonical"`

	Hour     int          `json:"hour,omitempty"`
	Minute   int          `json:"minute,omitempty"`
	Weekday  time.Weekday `json:"weekday,omitempty"`
	MonthDay int          `json:"month_day,omitempty"`

	Interval  time.Duration `json:"interval,omitempty"`
	Anchor    time.Time     `json:"anchor,omitempty"`
	HasAnchor bool          `json:"has_anchor,omitempty"`
}

ScheduleDefinition is the neutral persistence and boundary projection of a Schedule. It deliberately contains ordinary typed Go values: adapters map this into relational SQLC parameters or API messages at their own boundary. Canonical is a normalized expression, while the typed fields preserve the pleasant DSL shape for schedule kinds that have one.

type ScheduleKind

type ScheduleKind string

ScheduleKind identifies the structured scheduling form in a Definition. It is a domain value, deliberately independent of protobuf and SQLC types.

const (
	ScheduleKindDaily          ScheduleKind = "daily"
	ScheduleKindWeekly         ScheduleKind = "weekly"
	ScheduleKindMonthly        ScheduleKind = "monthly"
	ScheduleKindLastDayOfMonth ScheduleKind = "last_day_of_month"
	ScheduleKindEvery          ScheduleKind = "every"
	ScheduleKindRaw            ScheduleKind = "raw"
)

type Service

type Service struct {
	// contains filtered or unexported fields
}

Service reconciles static jobs, schedules deterministic occurrences, and executes handlers under durable leases.

func New

func New(options ...Option) (*Service, error)

New constructs a stopped Service. An IStore and at least one WithJob are required; construction performs no persistence and starts no goroutines.

func (*Service) Register

func (service *Service) Register(router gin.IRouter)

Register mounts the scheduler's read-only JSON status route. It never mounts mutation or run-now endpoints; callers choose the Gin group and therefore the authentication boundary.

func (*Service) Run

func (service *Service) Run(ctx context.Context) error

Run blocks until ctx is cancelled or durable scheduler state cannot be read or written safely. Job failures and panics are recorded as failed occurrences and do not stop Run. On return, all cooperative handlers have exited and their terminal state has been offered to IStore.

func (*Service) Snapshot

func (service *Service) Snapshot(ctx context.Context) (Snapshot, error)

Snapshot returns a point-in-time read-only view of scheduler state.

type SkipRequest

type SkipRequest struct {
	OccurrenceID string
	JobName      string
	ScheduledAt  time.Time
	NextRunAt    time.Time
	SkippedAt    time.Time
	Reason       string
}

SkipRequest terminally records a deliberately uninvoked occurrence and atomically advances its job cursor.

type Snapshot

type Snapshot struct {
	GeneratedAt time.Time          `json:"generated_at"`
	Running     bool               `json:"running"`
	Jobs        []JobState         `json:"jobs"`
	Occurrences []OccurrenceRecord `json:"occurrences"`
}

Snapshot is the read-only service and durable-store projection returned by Snapshot and the Gin status route.

type StoreSnapshot

type StoreSnapshot struct {
	Jobs        []JobState         `json:"jobs"`
	Occurrences []OccurrenceRecord `json:"occurrences"`
}

StoreSnapshot is a point-in-time copy of active jobs and at most the most recent SnapshotOccurrenceLimit durable occurrences.

type TimeOfDay

type TimeOfDay struct {
	// contains filtered or unexported fields
}

TimeOfDay is a 24-hour local civil time.

func At24

func At24(hour int, minute ...int) TimeOfDay

At24 declares a 24-hour local civil time.

func Midnight

func Midnight() TimeOfDay

func Noon

func Noon() TimeOfDay

func (TimeOfDay) String

func (value TimeOfDay) String() string

Directories

Path Synopsis
Package contract maps the cron domain model to validated Liquid Proto messages at HTTP and messaging boundaries.
Package contract maps the cron domain model to validated Liquid Proto messages at HTTP and messaging boundaries.
Package postgres provides the SQLC-backed durable cron Store.
Package postgres provides the SQLC-backed durable cron Store.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL