8000 Define time zone values and conversions by eemeli · Pull Request #1078 · unicode-org/message-format-wg · GitHub
[go: up one dir, main page]

Skip to content

Define time zone values and conversions #1078

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions
Original file line number Diff line number Diff line change
Expand Up @@ -282,11 +282,14 @@ the functions `:datetime`, `:date`, and `:time`.
- `local`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if I like the ident "local"; that word is way overloaded. I think "system" or "default" would be better.

- `UTC`

> [!NOTE]
> The value `local` permits a _message_ to convert a date/time value
> into a [floating](https://www.w3.org/TR/timezone/#floating) time value
> (sometimes called a _plain_ or _local_ time value) by removing
> the association with a specific time zone.
The value `local` corresponds to the default time zone.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The value local is meant to remove the time zone (it floats the value). This is different from the default time zone. Consider:

.local $event = {|2025-06-28T19:00:00Z| :datetime}
{{The Annual Barn Dance will be on {$event :date timeZone=local}.}}

The above formats as "June 28, 2025" regardless of where the message is viewed, vs. potentially formatting as "June 29, 2025" or "June 27, 2025" depending on the default time zone. The name local I find misleading. The Temporal synonym is plain, which I also find unhelpful.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, so local is effectively "local" to the input value, rather than being "local" to the time zone of the environment where the formatting is being done, yes? I had understood it the other way around.

We should probably use a different term that is more implicitly obvious about its meaning. Maybe input?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As expressed elsewhere, I hate the term local for this. It makes more sense in HTML forms, where presumably the user in inputting local wall time, but no sense in a context like MF. Temporal uses the term plain for this


If the _operand_ value does not include a time zone,
it 10000 is presumed to use the default time zone.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's not clear here if "the default time zone" is meant to be the result of accessing the system time zone setting, or a single default time zone (e.g. UTC).

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intent with the proposed language is to match the text from earlier in the same section:

**_<dfn>Date/time override options</dfn>_** are _options_ that allow an _expression_ to
override values set by the current locale,
or provided by the _formatting context_ (such as the default time zone),
or embedded in an implementation-defined date/time _operand_ value.

Does that context clarify the meaning here?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
it is presumed to use the default time zone.
it is presumed to use the default time zone specified by the formatting context.

Ah, I missed that. How about this, then?

If the _operand_ value does include a time zone and the `timeZone` _option_ is set,
an implementation SHOULD convert the value to the time zone indicated by the _option_.
Comment on lines +287 to +290
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this reads oddly, since the result of the first sentence would be that all operands end up with a time zone.

Lots of operands do not "include" a time zone--all classical date/time values, such as JS Date, for example. It's not helpful to think of assigning a time zone to the value so much as it is to think of assigning a time zone to the expression (so that an incremental time stamp can be converted to wall time). Consider:

When it is {$d :datetime} for you
it is {$d :datetime timeZone=|America/Los_Angeles|} in L.A.

I would say:

Suggested change
If the _operand_ value does not include a time zone,
it is presumed to use the default time zone.
If the _operand_ value does include a time zone and the `timeZone` _option_ is set,
an implementation SHOULD convert the value to the time zone indicated by the _option_.
If the _expression_ includes a valid `timeZone` _option_
and the _operand_'s value also includes a time zone,
an implementation SHOULD convert the _resolved value_ of the _operand_
to the time zone indicated by the _option_.
Otherwise, the time zone indicated by the _option_ should be used to format the value.

Note that it is unclear what "convert" means and the conversion means different things depending on the operand type. See here for examples of types. The conversion is one thing for a floating time or an incremental time/timestamp (you just add the time zone to the value) but requires calculation through a calendar for "zoned" values. The value local for timeZone exists to let the user remove the zone from a "zoned" value (so that the field values are no longer affected by the default--or any other--time zone).

Copy link
Collaborator Author
@eemeli eemeli Jun 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The conversion is one thing for a floating time or an incremental time/timestamp (you just add the time zone to the value) but requires calculation through a calendar for "zoned" values.

This does no 927D t match my understanding of what would happen if a time without an explicit timezone (such as a JS Date, which internally is handled as an incremental time/timestamp) would be formatted while explicitly setting timeZone.

With the JS Intl.DateTimeFormat, this happens:

const now = new Date()

new Intl.DateTimeFormat('en-de',{timeStyle:'long'}).format(now)
// "11:10:55 CEST"

new Intl.DateTimeFormat('en-de',{timeZone:'America/Los_Angeles',timeStyle:'long'}).format(now)
// "02:10:55 GMT-7"

So effectively the date being formatted is always considered to use the local/system time zone, and setting timeZone will format the date with conversion through a calendar.

My strong preference would be for the MF2 :datetime to behave similarly to the current JS behaviour, where the input/operand value is always considered to have some timezone (by default, the system default), and for formatting to happen in some timezone (by default, the system default). Setting the timeZone on the expression controls the timezone used for formatting, but does not change the timezone of the input.

Do the ICU datetime formatters use a similar approach, or do they do something different?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, you're right. And ICU does it the same way. It's my description that is somewhat wonky.

If you have a timestamp, you always need a calendar to format the value, but the application of a time zone (the default or some explicit value) doesn't change the timestamp itself. If you have a "zoned" value, you use a calendar to compute the instant and then modify the value by applying the explicitly provided time zone (you wouldn't use the default zone with a zoned value, presumably).

If such conversion is not supported, an implementation MAY alternatively
emit a _Bad Option_ error and use a _fallback value_ as the _resolved value_ of the _expression_.

The following _option_ is REQUIRED to be available on
the functions `:datetime` and `:time`:
Expand Down
0