Skip to content
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

Emphasize best practices for creating custom time zones and calendars #1040

Closed
ptomato opened this issue Oct 22, 2020 · 2 comments · Fixed by #1043
Closed

Emphasize best practices for creating custom time zones and calendars #1040

ptomato opened this issue Oct 22, 2020 · 2 comments · Fixed by #1043
Assignees
Labels
documentation Additions to documentation

Comments

@ptomato
Copy link
Collaborator

ptomato commented Oct 22, 2020

From #1037/#810, we need to document that the best practice for creating a custom time zone is to inherit from the built-in Temporal.TimeZone and warn what happens if you don't implement all the methods.

Justin suggested the following text, which I think is a good starting point but should be edited to be more concise, given how rare the use case is that it's documenting.

Temporal's internal implementation only requires the following four methods from a custom time zone:

getOffsetNanosecondsFor(instant: Temporal.Instant): number;
getDateTimeFor(instant: Temporal.Instant, calendar?: CalendarProtocol | string): Temporal.DateTime;
getPossibleInstantsFor(dateTime: Temporal.DateTime): Temporal.Instant[];
toString(): string;

Any custom time zone implementation that implements those four methods will return the correct output from any Temporal property or method. If you're writing a custom time zone and you're only calling Temporal code (not anyone else's code that also makes Temporal calls) then only the four methods above are sufficient.

However, most userland code will assume that custom time zones act like built-in Temporal.TimeZone objects, which implement additional properties and methods. If you want to interoperate with userland libraries or other code that you didn't write, then you should also implement the properties and methods below:

id: string;
getOffsetStringFor(instant: Temporal.Instant): string;
getZonedDateTimeFor(instant: Temporal.Instant, calendar?: CalendarProtocol | string): Temporal.ZonedDateTime;
getInstantFor(dateTime: Temporal.DateTime, options?: ToInstantOptions): Temporal.Instant;
getNextTransition(startingPoint: Temporal.Instant): Temporal.Instant | null;
getPreviousTransition(startingPoint: Temporal.Instant): Temporal.Instant | null;
toJSON(): string;

The easiest way to implement these properties and methods is to subclass the built-in Temporal.TimeZone which contains the following default implementations:

Members Default Implementation
id, toJSON Returns the result of toString()
getNextTransition, getPreviousTransition Returns null
getOffsetStringFor Creates an offset string using the result of getOffsetNanosecondsFor
getZonedDateTimeFor Creates a Temporal.ZonedDateTime instance using this and the provided Temporal.Instant and Temporal.Calendar inputs.
getInstantFor Creates a Temporal.Instant instance by calling getPossibleInstantsFor and, if there's more than one, using the provided disambiguation option to pick the correct result.

Here's an example of how this works, using a fictional time zone whose offset is always 3:14:15.926 behind UTC before UNIX epoch and 3:14:15.926 ahead of UTC after UNIX epoch.

const beforeEpochZone = new Temporal.TimeZone('-03:14:15.926');
const afterEpochZone = new Temporal.TimeZone('+03:14:15.926');
const OFFSET_NS = Temporal.Duration.from('PT3H14M15.926S').total({ unit: 'nanoseconds' });
class PiZone extends Temporal.TimeZone {
  constructor() {
    super('π');
  }
  getOffsetNanosecondsFor(instant) {
    return instant.offsetNanoseconds > 0 ? OFFSET_NS : -OFFSET_NS;
  }
  getDateTimeFor(instant, calendar) {
    return instant.offsetNanoseconds < 0 ? beforeEpochZone : afterEpochZone.getDateTimeFor(instant, calendar);
  }
  getPossibleInstantsFor(dateTime) {
    return (dateTime.year < 1970 ? beforeEpochZone : afterEpochZone).getPossibleInstantsFor(dateTime);
  }
  toString() {
    return 'π';
  }
}
@ptomato
Copy link
Collaborator Author

ptomato commented Oct 22, 2020

We'll have plenty of examples of custom time zones and calendars in #900, #603, #604, and #605, so I didn't add the example custom time zone, and instead of the table I've added details about the methods' default implementations directly to the documentation of each method.

ptomato added a commit that referenced this issue Oct 22, 2020
Emphasize that inheriting from the built-in objects is the recommended way
to create custom time zones and calendars, and give details about what the
default implementations of each of the optional methods do.

Closes: #1040
@justingrant
Copy link
Collaborator

Sounds good. The PR content in #1043 looks spot-on.

ptomato added a commit that referenced this issue Oct 23, 2020
Emphasize that inheriting from the built-in objects is the recommended way
to create custom time zones and calendars, and give details about what the
default implementations of each of the optional methods do.

Closes: #1040
Ms2ger pushed a commit that referenced this issue Oct 26, 2020
Emphasize that inheriting from the built-in objects is the recommended way
to create custom time zones and calendars, and give details about what the
default implementations of each of the optional methods do.

Closes: #1040
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
documentation Additions to documentation
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants