# Moving the running app's clock

> 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.

<!-- id: local-moving-the-clock · area: local · stability: preview · html: https://osysharp.com/reference/local/moving-the-clock/ -->

## Summary        {#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`](https://osysharp.com/reference/testing/clock-advance/), put on the app you are clicking
through instead of on a test's throwaway copy.

## Signature      {#signature}

```console
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    {#description}

### What moves   {#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 body | `DateTime.UtcNow`, `DateTime.Today`, `DateTime.Now` |
| a query | the instant `Where(o => o.Due < DateTime.UtcNow)` compares against |
| a write | `CreatedAt` / `ModifiedAt`, and any field defaulted to `DateTime.UtcNow` |
| a workflow | when a reminder, a deadline or an `Expire` is armed — and when it falls due |
| a schedule | when its next occurrence is due |
| the page | its `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   {#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   {#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`](https://osysharp.com/reference/local/reading-your-data/) or the page.

```osy title="a ticket that times out after two days" test app=local-moving-the-clock
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(); }
```

```console
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   {#dev-page}

[`osy launch --dev`](https://osysharp.com/reference/local/launching-your-app/) 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   {#local-only}

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       {#examples}

```console
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       {#see-also}

[TestClock.Advance and TestClock.Set](https://osysharp.com/reference/testing/clock-advance/) — 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](https://osysharp.com/reference/testing/dev-seeds/) — put the app in a known world first, then move its time.

[Remind (milestone reminders)](https://osysharp.com/reference/workflow/remind/) — the reminders a move makes due.
