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():
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 notEvery kit that counts in days reads the same row: Osysharp.Reports buckets and periods, and the
Osysharp.Notifications digest hour.
Signature#
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 dropdownDescription#
Why one clock for the whole app?#
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?#
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?#
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#
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#
A bakery that counts its days in Stockholm#
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#
- Time zones — TimeZoneInfo — the
TimeZoneInfotype,TimeZoneInfo.GetSystemTimeZones()andTimeZonePicker - [Settings] and [Setting]: a settings section declared on its row, each field drawn by its type — the settings tier the kit's page is drawn by
- The Reports kit — reports a team builds, and the overview it opens every morning — periods and buckets counted on the app's clock
- The Notifications kit — telling a person that something needs them — the daily digest, sent in the team's morning