Osy#betaRun it on Osyrin: osyrin.com

Reference / Local

Moving the running app's clock

osy clock [advance <span> | set <instant> | reset] [--path <dir>] [--devname <name>] [--json]

Moves the running local app's "now" — forward a day, back an hour, or to a date you name — so you can see tomorrow's reminder fire, a deadline pass or an invitation expire without waiting for it. The dev page has the same controls. Only a local platform has this clock; nothing deployed does.

preview1 example compiled by CIlocalclitestingtime

Summary#

osy clock advance 1d moves the app on your local platform one day ahead. Everything that asks the time follows: a function's DateTime.UtcNow, the instant a query compares against, the CreatedAt a new row is stamped with, and the time a workflow arms its reminders, deadlines and expiries from. Whatever the move made due is woken at once, so a 48-hour expiry fires the moment the clock passes it.

It is the clock a test moves with TestClock.Advance, put on the app you are clicking through instead of on a test's throwaway copy.

Signature#

osy clock                          # where the app's clock stands
osy clock advance <span>           # 30m · 2h · 1d · 1w — and -1d to go back
osy clock set <instant>            # 2026-11-03T09:00, read as UTC — or with its offset, 2026-11-03T09:00+01:00
osy clock reset                    # back to the machine's time

--json prints { now, offsetSeconds, moved, description, woke }. --path and --devname pick the project and the instance as every local verb does.

Description#

What moves#

The app's own reads of the time, on the server and in the browser:

reads the moved clock
a function body, a page action, a workflow bodyDateTime.UtcNow, DateTime.Today, DateTime.Now
a querythe instant Where(o => o.Due < DateTime.UtcNow) compares against
a writeCreatedAt / ModifiedAt, and any field defaulted to DateTime.UtcNow
a workflowwhen a reminder, a deadline or an Expire is armed — and when it falls due
a schedulewhen its next occurrence is due
the pageits Now and DateTime.UtcNow, once you reload it

What does not move is the platform's own bookkeeping about the machine — the sign-in that osy and the dev page hold keeps working across a move, so you can advance two days and go on querying the app.

It runs#

The clock keeps going from wherever you put it: set 2026-11-03T09:00 and a minute later the app reads 09:01. A move is an offset from the machine's time, and osy clock shows how far ahead (or behind) it is. reset puts it back.

What a move wakes#

A move wakes the app's timer sweep and its schedule sweep straight away — the same two the platform runs when a reminder or an occurrence falls due on its own — so anything the jump made due fires within a moment. Read the outcome the way you read anything else on the app, with osy query or the page.

using Osysharp.Workflow;

enum TicketStatus { Open, TimedOut }

entity Ticket {
  [Required, MaxLength(100)] string Title;
  TicketStatus Status;
  security { allow read, create, update when IsAnonymous || IsAuthenticated; }
}

workflow TicketFlow {
  Tracks = Ticket.Status; Autostart = true; Initial = Open;
  state Open {
    Expire = TimeSpan.FromHours(48);
    on Expire { goto TimedOut; }
  }
  terminal success TimedOut { }
}

void OpenOne() { new Ticket { Title = "Leaking tap" }; UnitOfWork.Commit(); }
osy run OpenOne
osy query 'Ticket.Count(t => t.Status == TicketStatus.TimedOut)'    # 0 — it is due in two days
osy clock advance 49h
osy query 'Ticket.Count(t => t.Status == TicketStatus.TimedOut)'    # 1 — the move made it due, and it fired
osy clock reset

A move only ever goes forward through what is due: moving the clock BACK does not un-fire a reminder that already ran.

On the dev page#

osy launch --dev opens the app's dev page, whose Clock section shows the app's time and moves it — +1 hour, +1 day, +7 days, a date and time to set, and Back to the machine's time once it has moved. It is the same clock osy clock moves. Reload the app's own tab afterwards to see the new time there.

Only on a local platform#

The clock exists only on a platform osyrin dev started for you — osy launch, osy compile, osy test and the rest start one on demand. A deployed app has no such clock and no way to be given one: osy clock refuses there, and the dev page and its clock controls do not exist on any deployed host. A reminder in production falls due when its time comes, and nothing an app or a visitor sends can change that.

Examples#

osy clock advance 1d          # see tomorrow's reminders go out
osy clock set 2026-12-31T23:59 && osy clock advance 2m   # step across the year's end
osy clock --json              # { "now": "...", "offsetSeconds": 86400, "moved": true, ... }
osy clock reset

See also#

TestClock.Advance and TestClock.Set — the same clock inside a test, where TestClock.Set and TestClock.Advance move it.

Dev seeds: a fixture you can load into the running app — put the app in a known world first, then move its time.

Remind (milestone reminders) — the reminders a move makes due.

Related

TestClock.Advance and TestClock.Set

In a test, moves the run's clock FORWARD by a duration, then lets the next Workflow.Settle fire every workflow timer…

Dev seeds: a fixture you can load into the running app

Mark a [TestFixture] with [DevSeed("label")] and the local dev page (`osy launch --dev`) offers it as a button that…

Remind (milestone reminders)

A reminder scheduled off a milestone. Its SCHEDULE is config in the header parens — `After` is the first fire (once, at…

Launching your app

Opens your app in a browser, running on the local platform. It compiles the current source first, so what opens…

Reading your data

Evaluates one Osy# expression against your app and prints what it answered — the rows, the count, the projection. The…