-
Notifications
You must be signed in to change notification settings - Fork 157
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
Should built-in calendars be subclasses? #847
Comments
What would be the way to hide the implementation from JS? I think that is better (though I don't really have any justification for that, and I would be fine to go the other way too.) |
Absolutely they should be subclasses of a common base class - Temporal needs to establish a convention for userland calendar objects to follow. |
Thinking about this some more — there is an inconsistency in the way we do time zones and calendars which has bothered me for a while. In pseudocode, Calendars: class Calendar {
constructor(id) {
this.[[Identifier]] = id;
}
method() {
throw new Error('not implemented');
}
}
class ISO8601 extends Calendar {
constructor() { super('iso8601'); }
method() {
/* ISO calendar implementation of method */
}
}
class Japanese extends Calendar {
constructor() { super('japanese'); }
method() {
/* Japanese calendar implementation of method */
}
} But TimeZones: class TimeZone {
constructor(id) {
if (new.target === TimeZone)
throwIfIdentifierIsNotBuiltin(id);
this.[[Identifier]] = id;
}
method() {
if (isOffsetTimeZoneIdentifier(this.[[Identifier]]))
/* offset time zone implementation of method */;
else
/* named time zone implementation of method */;
}
} So, Calendar does expose subclasses to JS, and TimeZone does not. (I guess I answered my own question from #847 (comment) with that.) It seems to me that we should go with either one or the other (either hide the Calendar subclasses from JS, or expose OffsetTimeZone and NamedTimeZone to JS.) I think I prefer the latter, as Jordan pointed out it establishes the proper convention. I'm not too concerned about adding more types to Temporal in this way, as I otherwise would be, since these subclasses will mostly be invisible and normal user code doesn't have to care about them. |
Adding them as, eg, |
How many calendars are we specifying, in that case? |
I think we are only specifying ISO8601, at least in ECMA-262; see also #541 |
We previously concluded that there would be just one built-in calendar class in #300 . Did anything change to motivate this additional complexity? |
That is right, we did, and I forgot about that. One reason for the additional complexity is that some calendars will inherit from the ISO calendar (e.g. This is probably not something that we have to resolve in order to call all design decisions complete, as it's more of an implementation detail, but it needs to be resolved before we can call the proposal stable. |
I have no opinion about the subclass-vs-not topic in this issue, but a very strong opinion about how built-in calendars should be exposed into the Temporal public API namespace. If we're putting built-in calendars into the Temporal namespace, then seems much better to make them static properties on the Calendar class, e.g. I think this is similar feedback that @ljharb gave in #847 (comment). I disagree with Jordan about time zones because there's no Regardless of how this is decided, remember to update the TS types so the new API will be discoverable! |
I don't see why we'd include any of these as properties; I thought |
@littledan Would you find the reason I gave above in #847 (comment) (many userland calendars would likely inherit from a specific built-in calendar) sufficient for including them as properties? |
I thought the idea was that all of the built-in calendars, including japanese, hebrew, islamic, buddhist, etc would be instances of Temporal.Calendar, not subclasses. |
That was originally the idea, but that seems inconvenient for userland calendars who want to inherit most of the behaviour from one built-in calendar. (For example, it's my understanding from interviews that it's common to have a Hijri calendar that allows a correction of one or two days in either direction for when the moon sighting doesn't agree with the tabular data.) |
@ptomato Couldn't those just call |
That would give you the wrong value in your internal [[Identifier]] slot and then you'd have to override |
@ptomato How would this be different if we created different subclasses? |
Presumably you'd do something like class MyHijiriCalendar extends Temporal.HijiriCalendar {
constructor() {
super('my-hijiri')
}
} Once you're subclassing, implementing |
I think maybe the question will be clearer if I provide full code examples of doing it one way and doing it the other way, that we can compare side-by-side; I'll do that within the next few days. |
OK, as promised, the example use case of implementing a Hijri calendar with date correction: // Subclassing way:
const HijriCalendar = Temporal.Calendar.from('islamic').constructor;
// or there is some object called e.g. Temporal.Calendar['islamic']
class AdjustedHijriCalendar extends HijriCalendar {
#deltaDays;
constructor(deltaDays) {
this.#deltaDays = deltaDays;
super('adjusted-hijri');
}
#adjustDate(date) {
return date.subtract(this.#deltaDays);
}
day(date) {
return super.day(this.#adjustDate(date));
}
// etc.
} Here, // Switching on identifier way:
class AdjustedHijriCalendar2 extends Temporal.Calendar {
#deltaDays;
constructor(deltaDays) {
this.#deltaDays = deltaDays;
super('islamic');
}
#adjustDate(date) {
return date.subtract(this.#deltaDays);
}
day(date) {
return super.day(this.#adjustDate(date));
}
// etc.
toString() {
return 'adjusted-hijri';
}
} Here. day(date) {
const id = this.[[Identifier]];
switch (id) {
case 'iso8601':
case 'gregory':
return date.[[ISODay]];
case 'islamic':
return calculateIslamicDay(date.[[ISOYear]], date.[[ISOMonth]], date.[[ISODay]]);
// etc.
}
} Since the [[Identifier]] slot is used for determining the internal behaviour, the Note that you also have to override |
We had originally decided to go with a scheme where the value of the [[Identifier]] internal slot determines the behaviour of the calendar methods, and there are not individual parent classes for each built-in calendar. This implements that scheme. Merges most of the spec text of ISO8601Calendar into Calendar. Closes: #847 See: #300
What implications (if any) does this change have on time zone subclassing? Does a custom time zone subclass require calling super with a valid ID for a built-in time zone? Or is any ID ok? I'm asking specifically in regards to my NYSE Time Zone cookbook sample PR #1177. |
Oh, glad you reminded me that it should have implications for time zones as well, I forgot all about that. I'll update the PR. For the NYSE time zone, the only change you would need to make is to add |
But the behavior of that time zone is not consistent with America/New_York when the market is closed. Is that ok? What about a time zone that doesn't behave like any built-in time zone? |
It seems like there should be a base class that accepts any arbitrary ID, or none at all, otherwise you couldn’t make a reasonable subclass that was anything beyond a slight deviation from a builtin calendar. |
If a subclass's behavior deviates from the base class, then should it use the parent's ID or some other arbitrary ID when initializing For a specific example, in the NYSE time zone in #1177, @ptomato you recommended that I shouldn't implement The general version of this question is which methods can a subclass safely not implement if it passes a built-in ID to A second issue: is the built-in calendar is observable to users of a subclass? If it is, then that seems like a problem because a caller might have code like this: if (date.calendar.id==='hijri') doSomething(); And assuming that your subclass deviates from the built-in calendar's behavior (because if it didn't deviate, why bother subclassing?) then that code above might be wrong because the calendar isn't really acting like the built-in one in all cases. Certainly in the NYSE custom calendar case, you'd not want the subclass to expose its underlying time zone ID because most of the time (because the market is open 22% of the hours in a week) the results won't match the builtin America/New_York zone. |
We had originally decided to go with a scheme where the value of the [[Identifier]] internal slot determines the behaviour of the calendar methods, and there are not individual parent classes for each built-in calendar. This implements that scheme. Merges most of the spec text of ISO8601Calendar into Calendar. Closes: #847 See: #300
We had originally decided to go with a scheme where the value of the [[Identifier]] internal slot determines the behaviour of the calendar methods, and there are not individual parent classes for each built-in calendar. This implements that scheme. Merges most of the spec text of ISO8601Calendar into Calendar. Closes: #847 See: #300
We had originally decided to go with a scheme where the value of the [[Identifier]] internal slot determines the behaviour of the calendar methods, and there are not individual parent classes for each built-in calendar. This implements that scheme. Merges most of the spec text of ISO8601Calendar into Calendar. Closes: #847 See: #300
We should spec those or make them invisible to JS.
The text was updated successfully, but these errors were encountered: