Osy#betaRun it on Osyrin: osyrin.com

Reference / I18n

Declaring the time zones and cultures an app uses — zones { } and cultures { }

zones { "UTC", "Europe/Madrid", … } · cultures { "en-US", "es-ES", … }

The time zones and cultures an app names are a declared, closed set: a `zones { }` and a `cultures { }` block in any model file (not in `app.osy`). The platform ships a default set of each; an app's own block REPLACES it, so it lists every zone or culture the app uses. A zone or culture outside the set is a compile error that prints the line to add.

stable2 examples compiled by CIzonetimezoneculturelocale

Summary#

A zones { } block lists the IANA time zones the app names; a cultures { } block lists the cultures it formats and parses in. Each is a top-level declaration in any model file — model/zones.osy is a good home — and never an entry in app.osy, which declares only where the source lives.

zones { "UTC", "Europe/London", "Europe/Lisbon", "Europe/Madrid", "Europe/Paris", "Europe/Stockholm" }

string ZoneFor(bool iberian) {
  return iberian ? "Europe/Madrid" : "Europe/Stockholm";
}

string TimeThere(bool iberian) {
  return TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, TimeZoneInfo.FindSystemTimeZoneById(ZoneFor(iberian))).ToString("HH:mm");
}

Signature#

zones    { "<IANA zone id>", … }       // e.g. "Europe/Madrid", "Asia/Kolkata", "UTC"
cultures { "<BCP-47 culture id>", … }  // e.g. "en-US", "sv-SE", "ar-AE"

Description#

Why the set is declared. A zone's daylight-saving rules and a culture's number and date patterns run in the browser as well as on the server, and the browser is handed them for exactly the declared set — so a page can show "18:40 in Madrid" or "1 290,00 €" with no round trip. A zone or culture outside the set has nothing to run with there, which is why naming one is refused when the app COMPILES rather than discovered when a page renders.

Your block replaces the default — it does not add to it. With no block of its own an app gets the platform's default packs (osy docs stdlib-zones lists the zones; osy docs stdlib-culture-formatting the cultures). The moment the app writes zones { … }, that list IS the app's set, so keep the default zones you still use. Blocks in several model files of one app add up; a capability's (kit's) own block is used only when the app declares none.

What is checked, and where.

you writerefused when the zone/culture is undeclared
TimeZoneInfo.FindSystemTimeZoneById("Europe/Madrid")at that literal
TimeZoneInfo.FindSystemTimeZoneById(ZoneFor(c)), where ZoneFor returns "Europe/Madrid"at the literal in ZoneFor — the compile follows the value through functions, fields, ?:, ?? and switch
amount.ToString("C", "es-ES")at that literal
TimeZoneInfo.FindSystemTimeZoneById(shop.TimeZone) — a zone a PERSON storedcannot be decided at compile time: a stored value is checked to be a real zone when it is written, and a page that meets one outside the set names it. Let people pick it with TimeZonePicker, which only offers declared zones

The refusal carries the whole remedy — where the block goes and the exact line, the zones you already have plus the missing one:

model/people.osy:12  ERROR  zone 'Europe/Madrid' reaches `TimeZoneInfo.FindSystemTimeZoneById(…)` at model/people.osy:18 through `ZoneFor`, but
  it is not one of this app's declared time zones, so the browser is given no daylight-saving plan for it and the
  page would fail there. Declare the zones the app uses in a `zones { }` block in any model file (`model/zones.osy`,
  say — not in `app.osy`). Your block REPLACES the platform's default set, so it lists every zone the app uses:

  zones { "UTC", "Europe/London", …, "Europe/Madrid" }

Each id is checked against the time-zone and culture data the platform ships, so a misspelling ("Europe/Madird") is refused with a "did you mean"; an id listed twice is refused too.

Listing them as data. TimeZoneInfo.GetSystemTimeZones() is the declared zones as a Zone[], in block order — what a picker offers. With use Osysharp.Locale; they are also rows (Osysharp.Locale.Zone, Osysharp.Locale.Culture) a page can query.

Examples#

cultures { "en-US", "en-GB", "es-ES", "sv-SE" }

string PriceInSpain(decimal amount) {
  return amount.ToString("C", "es-ES");
}

See also#

Related

Time zones — TimeZoneInfo

Store and use civil time zones with C#'s `TimeZoneInfo`. A zone holds an IANA id (`"Europe/Stockholm"`), and C#'s…

Culture formatting — ToString(format, culture)

Format numbers, currency, percentages and dates for a declared culture — `total.ToString("C", "sv-SE")` → `1 234,56…

app.osy

The manifest at the root of every project. It names the app, says which files are model, seed, migrations and tests…