# The TimeZone kit — the time zone an app's days start in, picked from a list

> `Osysharp.TimeZone` is the app's one answer to "what day is it here?" — the time zone the team's days start in and the day its week starts, picked by staff from the app's declared zones on one settings page. Reports count their days, weeks and months on it, and the daily digest goes out in the team's morning by it, so a team that moves its clock moves all of them at once.

<!-- id: ui-clock-kit · area: ui · stability: preview · html: https://osysharp.com/reference/ui/clock-kit/ -->

## Summary        {#summary}
`use Osysharp.TimeZone@0;` gives the app one clock: a typed `TimeZoneInfo` and the day a week starts, picked from a list by
whoever `ManagesClock`, on the settings page the kit adds (Settings › Time and date). Code reads it with `TheClock()`:

```osy syntax
var clock = TheClock();
var today = clock.DayOf(DateTime.UtcNow);       // the date on the team's wall, not the server's
var since = clock.StartOf(today.AddDays(-6));   // midnight there, seven days ago — in summer time or not
```

Every kit that counts in days reads the same row: `Osysharp.Reports` buckets and periods, and the
`Osysharp.Notifications` digest hour.

## Signature      {#signature}
```osy syntax
use Osysharp.TimeZone@0;                       // with Osysharp.Ui

policy ManagesClock => <who moves the clock>;   // optional — the app's IsAdmin until it says otherwise

app.Clock = new ClockSetup {                // optional — the clock until staff pick one
  TimeZone = TimeZoneInfo.FindSystemTimeZoneById("Europe/Stockholm"),   // one of the app's declared zones; UTC by default
  WeekStart = DayOfWeek.Monday,             // Monday by default
};

AppClock TheClock()                         // staff's choice, else the app's ClockSetup
// on an AppClock:
Zone      TimeZone
DayOfWeek WeekStart
DateTime  Local(DateTime instant)           // the wall-clock time there
DateOnly  DayOf(DateTime instant)           // the date there
DateTime  StartOf(DateOnly day)             // the instant that day begins there
DateOnly  WeekOf(DateOnly day)              // the first day of the week holding `day`

// settings: /settings/clock (ClockSettingsPage) — a time-zone picker and a week-start dropdown
```

## Description    {#description}

### Why one clock for the whole app?   {#one-clock}
A report that says "today" and a digest that goes out "in the morning" mean the same day only if they read the same
zone. Each kit keeping its own would let them drift: the report's week would start on Monday in Stockholm while the
digest went out at 06:00 UTC. So the zone lives in one row, and the kits that count in days read it rather than keep a
copy. Changing it moves every one of them at the next read — nothing is cached.

### What can staff pick from?   {#declared}
The app's declared zones — its `zones { }` block, or the platform's default set ([[stdlib-zones#description]]). The
setting is a `TimeZoneInfo`, drawn by the settings tier as a `TimeZonePicker`, so a name that is not a time zone cannot be saved:
it cannot be typed. To offer a zone the default set does not have, declare the app's own `zones { }`; it replaces the
default wholesale, so list every zone the team may want.

### Who may change it?   {#who}
`ManagesClock`. The kit answers it with the app's `IsAdmin` until the app defines it. Everybody may read the clock,
signed in or not — it is not a secret, and a public page may say "open until 17:00" on it.

### Before anybody sets it   {#defaults}
No row means the app's `ClockSetup` — UTC and Monday unless the app says otherwise in `app.osy`. The settings page
makes the row the first time it is opened, with the same defaults.

## Examples       {#examples}

### A bakery that counts its days in Stockholm   {#example-bakery}
```osy title="the clock, a default, and today on the team's wall" test app=ui-clock-kit
app Bakery {
  use Osysharp.Identity@0;
  use Osysharp.TimeZone@0;
  use Osysharp.Ui;
  model "model/**/*.osy";
}

using Osysharp.Identity;
using Osysharp.TimeZone;

[Role] enum BakeryRole { Authenticator, Baker, Owner }
policy ManagesClock => RoleGrant.Any(g => g.Member == user && g.Role == BakeryRole.Owner);

app.Clock = new ClockSetup { TimeZone = TimeZoneInfo.FindSystemTimeZoneById("Europe/Stockholm") };

/// The date on the bakery's wall now.
public DateOnly TodayAtTheBakery() { return TheClock().DayOf(DateTime.UtcNow); }

/// When this week began there — the first day of the week, at midnight.
public DateTime ThisWeekBegan() {
  var clock = TheClock();
  return clock.StartOf(clock.WeekOf(clock.DayOf(DateTime.UtcNow)));
}
```

## See also       {#see-also}
- [Time zones — TimeZoneInfo](https://osysharp.com/reference/stdlib/zones/) — the `TimeZoneInfo` type, `TimeZoneInfo.GetSystemTimeZones()` and `TimeZonePicker`
- [[Settings] and [Setting]: a settings section declared on its row, each field drawn by its type](https://osysharp.com/reference/ui/settings-declared/) — the settings tier the kit's page is drawn by
- [The Reports kit — reports a team builds, and the overview it opens every morning](https://osysharp.com/reference/ui/reports-kit/) — periods and buckets counted on the app's clock
- [The Notifications kit — telling a person that something needs them](https://osysharp.com/reference/ui/notifications-kit/) — the daily digest, sent in the team's morning
