Proposal-temporal: LocalDateTime fields in from and with

Created on 25 Jun 2020  Ā·  37Comments  Ā·  Source: tc39/proposal-temporal

In my ZonedAbsolute brainstorm, I had suggested that the LocalDateTime type be a pure wrapper over Absolute and TimeZone. Fields like "year" and "hour" would not be accessible directly from LocalDateTime, but you could get it by moving into one of the other types like DateTime.

Entirely removing the concept of DateTime fields from ZonedAbsolute/LocalDateTime eliminates the "Conflicts and Ambiguity in from and with" from #700.

If you wanted to get a DateTime field, you would do,

const ldt = Temporal.LocalDateTime.from("2020-06-25T10:50:00-05:00[America/Chicago]");
console.log(ldt.toDateTime().hour);  // 10

@justingrant

zoned-type

Most helpful comment

The explanation we came up with on Friday was: Let's think of LocalDateTime not as an "everything type" but as an "event that happened, or will happen, in a place", and ask ourselves what _not_ to have in the type, taking that into account. We'll evaluate the other open questions using this framework.

All 37 comments

Entirely removing the concept of DateTime fields from ZonedAbsolute/LocalDateTime eliminates the "Conflicts and Ambiguity in from and with" from #700.

Unfortunately I don't think this is true, at least not for string initializers. For ISO strings, AFAIK there's only one kind of conflict: DateTime+TimeZone vs. DateTime+offset. Both can be used to derive the Absolute portion of the LocalDateTime. Normally they don't conflict. But if an Extended ISO string representing a far future date is stored and later the time zone definition changes (e.g. a country permanently stops observing DST), then when the string is parsed there may be a conflict. Hence the prefer: 'offset' | 'dateTime' | 'reject' option. (FWIW, I'm not sure if "dateTime" is the right name for that option.)

IIUC, the string initializer problem isn't unique to LocalDateTime; Absolute has that problem as well.

I mainly like the clean separation between the data model and the calendar-dependent fields. Removing the calendar fields from your new type aids in the clean separation.

If you wanted to get a DateTime field, you would do,

const ldt = Temporal.LocalDateTime.from("2020-06-25T10:50:00-05:00[America/Chicago]");
console.log(ldt.toDateTime().hour);  // 10

@justingrant

Hi there. I know I wasn't invited to the party (:smile:) but, doesn't that heavily diminish the usefulness of the concept LocalDateTime tries to represent?

I've been reading several issues in this repo, including the last proposal of @justingrant. I have a lot to learn about all the date-time problem, but IMO is really useful to be able to perform date-time arithmetic (_calendar_ date and _wall-clock_ arithmetic) _under_ a particular time zone and with safe defaults for offset transitions. I know it is not really _a must_ since one can jump between date-time and absolute time as necessary, but that is very error prone, and many very good developers doesn't understand the intricacies of such calculations.

For instance, I live in Chile, and while it isn't as complicated as other places we have our issues. Due to some "natural problems", like the earthquake of 2010 or droughts in 1999 and 2008, some years the DST transition was posponed. In 2015 was an attempt to abolish DST completely but was restored in 2016. In 2017 the south regions of the country started to have its own time zone without DST, that correspond to Chilean summer time (UTC-3). And offset transitions occurs always at midnight. I've been bitten by some bugs due to a poor date-time arithmetic that doesn't takes those issues into account, and tracking those to its origins is not fun. I started to educate myself so I understand more now, but I still need to triple-check, sleep (with nightmares), and then double-check again some calculations I need to perform. So, again, a class with sane and DST-safe defaults to perform date-time arithmetic is IMHO quiet important for a date-time library.

Hi @InExtremaRes: I'm not proposing to remove timezone-safe arithmetic from LocalDateTime; indeed, that is one of the primary motivating factors for LocalDateTime. My comment is specifically limited to the getters and setters involving the datetime-specific fields.

@sffc - if we removed DateTime fields from LocalDateTime, how would developers accomplish common use cases like "change to the first day of the month" or "set time to noon" ?

const ldt = Temporal.LocalDateTime.from("2020-06-25T10:50:00-05:00[America/Chicago]");
const dt = ldt.toDateTime().with({day: 1});
// now what?  how to get back into LocalDateTime-land?

I'm sorry if my comment looks a bit off-topic, I think I couldn't express my self correctly. But my concern was exactly what @justingrant just pointed out.

I think removing the getters and setters is what would diminish the usefulness of the class. Performing date-time calculations in a zone-aware fashion implies that you start and end with instances of LocalDateTime without converting to another Temporal first. Is true that I can get the info converting to DateTime, but then I couldn't perform the math DST-safe. Also I just get and instance of DateTime, to convert to LocalDateTime I need to use the time zone again, what seems very redundant. Avoiding the round to DateTime or Absolute is how this class can help developers write better code.

@sffc - if we removed DateTime fields from LocalDateTime, how would developers accomplish common use cases like "change to the first day of the month" or "set time to noon" ?

Case 1:

const ldt = Temporal.LocalDateTime.from("2020-06-25T10:50:00-05:00[America/Chicago]");
const dt = ldt.withDate(ldt.toDate().with({day: 1}));

Since Case 1 is calendar-sensitive, I would prefer if we put the calendar in toDate():

const ldt = Temporal.LocalDateTime.from("2020-06-25T10:50:00-05:00[America/Chicago]");
const dt = ldt.withDate(ldt.toDate("gregory").with({day: 1}));

Case 2:

const ldt = Temporal.LocalDateTime.from("2020-06-25T10:50:00-05:00[America/Chicago]");
const dt = ldt.withTime(Temporal.Time.from("12:00:00"));

I see LocalDateTime as an "entry point" with all the tools you need for DST-safe arithmetic, and you go to Date, Time, or Absolute when you need something more specific.

re: ldt.withDate(ldt.toDate().with({day: 1})); vs. ldt.with({day: 1});

Channeling @littledan from his feedback about calendars, I suspect that requiring an extra .withDate(ldt.toDate(). to change a single date field will be viewed by most developers as an unnecessary tax. Same for an extra 9+ keystrokes (e.g. .toTime()) to read a date or time field, especially given how common that task is.

IMHO a big benefit of LocalDateTime is that it provides DST-safe math and same or better ergonomics for other tasks. It seems much less appealing to get DST safety but at the cost of having to pay a big ergonomics tax.

If there were ambiguity possible then I'd be more likely to agree, but there's no ambiguity in reading fields. And there's no ambiguity in writing fields in from/with except in relatively rare cases that IMHO are solveable via exceptions and options. I'm not sure those few unusual cases justify a large ergonomics hit for the vast majority of use cases.

Since Case 1 is calendar-sensitive, I would prefer if we put the calendar in toDate():

Should we do this in DateTime.toDate() too? My intent was to mirror the DateTime API to maximize compatibility so developers could leverage learning from one type to use the other. So if DateTime.prototype.toDate will accept a calendar then I think LocalDateTime.prototype.toDate should too. But if it doesn't, then I'm not sure breaking compat with DateTime is needed, especially given that there's already a DateTime-compatible LocalDateTime.prototype.withCalendar method that could be used instead.

I see LocalDateTime as an "entry point" with all the tools you need for DST-safe arithmetic, and you go to Date, Time, or Absolute when you need something more specific.

I've been thinking of LocalDateTime as more of a superset than an entry point. The idea was to remove the need to use DateTime or Absolute for most use cases if you start with a LocalDateTime. Kinda like how DateTime is a superset of Date and so you can do (almost) all the things with a DateTime that you can do with a Date, with the only exceptions being methods like toTime() (using post-#705 naming here) which necessarily behaves differently on DateTime than on Date. I'm not sure why we'd do it differently with LocalDateTime.

BTW, your comment spurred me to make sure that I'd captured all compatible Absolute methods too. Turns out I forgot to mirror the the getEpochXxx() methods. Fixed now in https://github.com/tc39/proposal-temporal/pull/700/commits/d835f3f68fc0977d69a9fcf05e901428f6d819ba.

Since Case 1 is calendar-sensitive, I would prefer if we put the calendar in toDate():

Should we do this in DateTime.toDate() too? My intent was to mirror the DateTime API to maximize compatibility so developers could leverage learning from one type to use the other.

My suggestion is ultimately leading to whether we can remove calendar from LocalDateTime, conceptualizing it as a calendar-agnostic instant in a time zone. The calendar then becomes the key used to transition to the wall clock types. This was my ZonedAbsolute proposal.

I've been thinking of LocalDateTime as more of a superset than an entry point.

If that's the direction we want to go, then the next question is naturally, do we need DateTime and Absolute if LocalDateTime solves our problems?

@ptomato asked a similar question in https://github.com/tc39/proposal-temporal/pull/700#discussion_r448590883

I guess there's a related philosophical question here, is LocalDateTime supposed to become an "everything type" where we say "when in doubt, use LocalDateTime?" Or do we recommend using it in the cases where Absolute and DateTime don't quite fit? My preference would be for the latter and I think removing the comparison method would fit with that approach. Keeping the comparison method or even the option argument would fit with the former approach.

I sense a common theme here. ;-)

@sffc If that's the direction we want to go, then the next question is naturally, do we need DateTime and Absolute if LocalDateTime solves our problems?

@ptomato is LocalDateTime supposed to become an "everything type" where we say "when in doubt, use LocalDateTime?" Or do we recommend using it in the cases where Absolute and DateTime don't quite fit?

@pipobscure It seems to be more like a ZonedAbsoluteDateTime which attempts to contain and harmonise all the types Timezone, Absolute and DateTime.

The answer to those questions depends on our goals for a "Zoned" type. At a high level, I think there are three options:

1) Math Only - A zoned type is there for DST-safe math operations. Other than math, keep the type as minimalistic as possible. Don't provide ergonomic helpers, e.g. foo.hour but instead require users to know about and call into underlying types, e.g. foo.getDateTime().hour. Also doesn't include a Calendar. @sffc is in favor of this option.

2) Math + Time Zone Ergonomics - LocalDateTime is there to make DST-safe math operations easier and to improve ergonomics & DRY for use cases where both the time zone and instant are known. This option extends (1) with DateTime getters & non-ISO calendar support. This improves ergonomics and makes this type into a one-stop-shop for novice users. But this option isn't a full superset of Absolute & DateTime. Developers still must delegate down to other types to handle a few (see below) uncommon or advanced use cases. I've got work to do (see #719) to convince @ptomato that compare should be in scope of this option because it's part of "math".

3) Full Superset - Anything you can do with DateTime and Absolute, you can do with LocalDateTime. For every method like compare where Absolute or DateTime behavior is chosen by default, an options parameter enables the opposite choice. I suspect that @pipobscure prefers this option. This option makes (2) more "expert friendly" by adding support for a few uncommon use cases:

  • Parsing future ISO strings that were made invalid by a change to the time zone definition, by using an option to from and with that allows control over whether the time zone or the offset will "win" when parsing an extended ISO string into an Absolute. If it's decided in #716 to add this functionality into Absolute.from, then LocalDateTime would no longer have any code to support this use case and would simply pass options props unchanged through to Absolute.from which would handle the parsing.
  • An option for compare to use DateTime.compare instead of the default Absolute.compare. (The latter is likely the 95%-99% case, so Absolute should definitely be the default.)
  • The current PoC code accepts the timeZoneOffsetNanoseconds property in from and with, which is definitely an experts-only thing. But I'm leaning towards removing it because it adds a lot of complexity and might introduce new failure cases. See #718 if you have an opinion about this obscure case.

My opinion:

  • I don't yet understand what are advantages of Option 1 that outweigh its disadvantages:

    • Significantly harder ergonomics with extra method calls and extra typing for many common use cases, e.g. "what year is it?", "go to the first day of this month", "is this time during business hours?"
    • Very difficult (and non-DRY) for developers using non-ISO calendars, because there's no way to retain the calendar in LocalDateTime.
    • If developers are required to shell out to DateTime frequently, some percentage of them (esp. novices) will incorrectly stay in DateTime-land (and therefore lose DST safety) because they won't know that they should convert back before doing math operations.
    • It'd make it harder to port code from using DateTime to using this type, or vice-versa.
  • I'm in favor of Option 2 because of concerns about ergonomics and DST-safety of Option 1, esp. for novice users and non-ISO calendars.

  • I don't have a strong opinion about Option 3. Philosophically it's different from (2), but the implementation of both will be nearly identical. And depending on how #716 and #718 are decided, it's possible that there'd be only one difference: an options parameter on compare. The only differences between (2) and (3) deal with non-default behavior, so given that novices typically stick to defaults, I think either (2) or (3) would be fine for new users. So if I had to choose I'd probably favor (3) because it'd help experts without hurting novices. But I'd be OK with (2) too.

If that's the direction we want to go, then the next question is naturally, do we need DateTime and Absolute if LocalDateTime solves our problems?

This is a good question. Could we ask the same question about Date, Time, MonthDay, and YearMonth? Those types exist because it's helpful to have strongly-typed support for use cases that only require a subset of date/time data. For the same reason, DateTime and Absolute types handle useful subsets of date/time use cases, so it seems reasonable to leave them as separate types.

That's a good summary; thanks.

I don't yet understand what are advantages of Option 1 that outweigh its disadvantages

My previous research (#596) suggests that majority of call sites don't actually need date/time field getters. A type that correctly performs calendar-agnostic operations like time zone conversion, DST math with units of days and smaller, and toLocaleString() (where the calendar comes from the locale) covers most of the cookbook examples. That's the type I've been advocating for.

Most of the time the calendar is not needed, but when it is needed, the calendar should be explicit. I understand that this is a minority opinion, but it is consistent with overwhelming advice I've received from other colleagues in the i18n space. Moving the calendar to the wall clock types (Date, DateTime, MonthDay, and YearMonth), and requiring that transitions between LocalDateTime and DateTime require a calendar parameter, would make calendars explicit throughout Temporal, which solves #292 and would make Temporal the gold standard for i18n support in date time libraries.

If we were to choose one of the other options in #292, like Option 2 or Option 6, then my concerns about having an implicitly defined calendar field in ZonedDateTime would be put to rest.

Very difficult (and non-DRY) for developers using non-ISO calendars, because there's no way to retain the calendar in LocalDateTime.

I don't think developers building non-ISO apps would be affected any more than those building ISO apps. If you never have to leave LocalDateTime, as is the majority of cases, then there's no problem. If you do need to go to DateTime, then you need to pass the calendar argument, which you should have readily available, since, as a non-ISO developer, you already need to compute it explicitly somewhere in your code.

@ptomato is in favor of this option (correct?)

FWIW, I don't have a good enough grasp of the problem space yet to say that I am in favour of any particular one.

@ptomato is in favor of this option (correct?)

FWIW, I don't have a good enough grasp of the problem space yet to say that I am in favour of any particular one.

Whoops, sorry for misunderstanding your feedback! I edited the comment above accordingly.

So, I am growing increasingly happy with Option 3 ("Full Superset"), with the following caveats:

  1. If we adopt Option 3, we should throw away both Absolute and DateTime.
  2. The calendar and the time zone default to null unless otherwise specified.

Caveat 2 makes it possible to represent what we currently call DateTime: a LocalDateTime with a null time zone slot. An Absolute would be a LocalDateTime in UTC. The string/data correspondence would be:

  1. "2020-07-09T21:40:40" => a LocalDateTime with a null time zone and null calendar.

    • Mental model: An instant anywhere on Earth.

    • Data model: Internally, it is represented as epoch nanos in UTC, but we do not expose this information to the user in an easy way.

    • Getters & Conversion: Time fields only.

    • Arithmetic: Either forbidden, or allowed with only days.

  2. "2020-07-09T21:40:40[ca=gregory]" => a LocalDateTime with a null time zone and explicit calendar.

    • Mental model: A wall clock date and time anywhere on Earth.

    • Most similar to: Temporal.DateTime

    • Data model: Same as above.

    • Getters & Conversion: Date and time fields.

    • Arithmetic: Days and larger.

  3. "2020-07-09T21:40:40Z" => a LocalDateTime with an explicit time zone and null calendar.

    • Mental model: An instant at a particular place on Earth.

    • Most similar to: Temporal.Absolute

    • Data model: Epoch nanos of the instant in UTC.

    • Getters & Conversion: Time fields and timestamp.

    • Arithmetic: Days and smaller.

  4. "2020-07-09T21:40:40Z[ca=gregory]" => a LocalDateTime with an explicit time zone and calendar.

    • Mental model: A date and time at a particular place on Earth.

    • Most similar to: @justingrant's Temporal.LocalDateTime

    • Data model: Same as above.

    • Getters & Conversion: All.

    • Arithmetic: All.

Behavior is enforced with exceptions. Example error messages:

  • "To perform arithmetic with weeks, months, or years, please set a calendar system."
  • "To perform arithmetic with hours or smaller, please set a time zone."
  • "To get the date, please set a calendar system."
  • "To get the timestamp, please set a time zone."

^ I edited my post to fix some typos and add more examples ^

I don't think it makes sense to drop Absolute. There are lots of real-world use cases that only need absolute times, and having a constrained type for those use cases seems like a real benefit for developers with few downsides. Also, by having a separate Absolute type, it makes it much harder for developers to accidentally mix up absolute and local times, which historically was a problem with legacy Date.

That said, depending on what we decide in #759, dropping or renaming DateTime might make sense. Specifically, if we remove DateTime's math methods (#759 Option 1b) then it seems worth discussing whether to drop DateTime or to rename it to something obscure like ZonelessDateTime.

I'd suggest we try to close on #759 and then come back to this question.

Behavior is enforced with exceptions. Example error messages:

For really common use cases (e.g. absolute timestamp math) I'd prefer strong types that could leverage IDE intellisense and TS to prevent bugs like these. I agree that clear error messages are great, but preventing errors before runtime is also helpful.

The calendar and the time zone default to null unless otherwise specified.

Per earlier discussions and decision to make ISO calendar the default, very few developers will use non-ISO calendars and many (most?) of those will also use the ISO calendar, often in the same project. Therefore, I don't see the value in making null the default instead of ISO. We'd just be adding work without benefit for almost all developers.

Re: null time zone, it's an interesting idea as an alternative to setting the time zone to 'utc'. I assume you mean .from({timeZone: null, year: 2020, month: 1, day: 1}) not .from({year: 2020, month: 1, day: 1}). Right? It'd be bad if users didn't get an exception if they forgot to include the timeZone prop. I'm not wild about the string parsing behavior though-- seems like we'd want some way to force opting into the null beyond parsing a zoneless string. Also, how would a user output a null-time-zone value using toString()?

_(I edited this post to add more notes)_

The calendar and the time zone default to null unless otherwise specified.

Per earlier discussions and decision to make ISO calendar the default, very few developers will use non-ISO calendars and many (most?) of those will also use the ISO calendar, often in the same project. Therefore, I don't see the value in making null the default instead of ISO. We'd just be adding work without benefit for almost all developers.

It's only tentative right now, we agreed to revisit it based on feedback interviews. But on the other hand I agree that the default calendar here should be equal to the default calendar elsewhere, so we should make it ISO for now and revisit it at the same time as everywhere else a default calendar appears.

That said, depending on what we decide in #759, dropping or renaming DateTime might make sense. Specifically, if we remove DateTime's math methods (#759 Option 1b) then it seems worth discussing whether to drop DateTime or to rename it to something obscure like ZonelessDateTime.

I realized another reason that merging LocalDateTime and DateTime may make sense: many countries are considering getting rid of DST changes permanently. The EU is set to do this in 2021. So it's possible that very few countries may still use DST in 5-10 years. If this happens, then DateTime and LocalDateTime would always return the same results. Having two types that always return the same results in almost all countries (at least for non-historic times) would probably seem like an unfortunate legacy mistake. Having only one type might future-proof the API in case this happens.

That’s actually the worst possible reason and actually a good reason to definitely keep them separate. Merging them in that case would make the cases where it’s not the same would be even more confusing.

Aside from that, your information is wrong: the EU is only planning to address the issue (i.e. have more discussions) in 2021. So whether DST is ever going to abolished is entirely up in the air. So your ā€œunfortunate legacy mistakeā€ is actually highly unlikely to be that within at least my life-time.

Haha, OK. I trust your instincts on what the EU will do. Certainly more than I trust my cross-the-pond guesses based on news articles like https://www.nytimes.com/2019/03/27/world/europe/daylight-savings-time-european-union.html.

I don't have a strong opinion either way re: merging these types. My priority at this point is resolving #759 first and then to see how everything else shakes out. If you have a chance to review that issue, it'd be very helpful.

Calendars are a struggle because 90% or more of developers haven't encountered non-Gregorian in their daily life, so they don't think about it when writing date code. If time zones are on that same trajectory, it's yet another reason to consider them as conceptually identical to calendars. Whatever we do to one, we should do to the other.

I disagree with that; time zones are definitely never going away. Maybe DST will be gone in the future, but there will always be a difference in local time as you move to different longitudes on the planet, even if they are always simple offsets from UTC and geographical areas never switch time zones.

See my comment here: https://github.com/tc39/proposal-temporal/issues/716#issuecomment-659348139 because I think we’re getting a bit confused sometimes (or maybe it’s just me šŸ˜€ )

I didn't like it at first but the more I think of LocalDateTime as an 'everything type' the more I'm warming to the idea. I do not think that having it means that we can remove either Absolute or DateTime, which as I said in https://github.com/tc39/proposal-temporal/issues/759#issuecomment-659719725 represent two other kinds of arithmetic, but I think that LocalDateTime arithmetic is probably the least surprising and most often needed so it makes sense for that to be the type that you reach for first.

I don't like the idea of null time zones and calendars. I agree with @pipobscure, Absolute and DateTime don't have time zones, LocalDateTime does.

LocalDateTime arithmetic is probably the least surprising and most often needed so it makes sense for that to be the type that you reach for first.

Yep, this matches what I had in mind for LocalDateTime: a comprehensive type that represents the superset of Temporal data and use cases. Every other Temporal type is a subset of LocalDateTime that's focused on a particular subset of use cases. I actually think that this model may be helpful in documenting Temporal and teaching it to newbies. Instead of having to teach or learn three types (Absolute, DateTime, and TimeZone) for most common use cases, you can start with only learning one type initially... and then you can branch out from there to learn about every other Temporal type. From an education standpoint I suspect this gradual approach may be helpful.

If we make LocalDateTime the main entrypoint type (I think we should use a different name, but that's a different thread), then it should have a calendar slot and all the calendar fields, and we return back to our #292 dilemma.

The types then become:

| Type | Time Zone? | Calendar? |
|---|---|---|
| Absolute | āŒ | āŒ |
| LocalDateTime | āœ”ļø | āœ”ļø |
| DateTime | āŒ | āœ”ļø |

That table lacks consistency. If LocalDateTime is the "main" type, why do you lose 1 field when transitioning to DateTime, but 2 fields when transitioning to Absolute? And unless we make calendar a required argument, transitioning to and from Absolute will lose the calendar information.

Should we add a calendar slot to Absolute and complete the table? The slot wouldn't do much, except it would be represented in string output and would be retained when transitioning between types.

Should we add a calendar slot to Absolute and complete the table? The slot wouldn't do much, except it would be represented in string output and would be retained when transitioning between types.

To be quite honest, I could not care less about tables and their visual layout. I care about what these types represent and that needs to be the only thing that triggers inclusion of a piece of data.

So for DateTime which represents a calendar date & time independent of where that is, the calendar makes sense, for Absolute that represents a unique point in time independent on where on (or off) earth and with no idea what even a day is, the calendar makes no sense.

So no we should not be adding fields to types just to make a table look prettier; and no this does not relate to #292 because absolutes do not have a concept of what a calendar even is.


Plus nobody is suggesting that LocalDateTime is the main entry point into Temporal. I don't even know what that means to be honest. (I mean I can guess, but...) it's simply a convenience type that let's you combine different concepts/types.

So the way I see it:

DateTime - contains a Calendar and some date/time fields
TimeZone - contains rules for how much offset from UTC to use to represent a time
Absolute - contains an absolute point in time

and

LocalDateTime contains a TimeZone & DateTime

So I don't even think of LocalDateTime having a calendar, because it only has one by dint of including a DateTime or even having an Absolute because it conceptionaly only has one by dint of having a DateTime and TimeZone (and possibly a disambiguation for offset).


So in short this is just not data that lends itself to tabular representation without loosing important context. Rather it is a set of relationships that exceeds 2-dimensions.

I've been thinking about this notion of an entry-point to Temporal a bit more. If anything, Absolute would be that entry-point because:

  • It's what most things will spit out information as (logging, timestamps, etc...)
  • It's what's most like the current legacy-Date minus all the cruft that was bad.
  • It's the conversion point for legacy-Date since Absolute.fromEpochMilliseconds(new Date())

And once you enter Temporal (rather than just using it peripherally) then you add concepts.

  • Use a TimeZone to get a DateTime
  • Add a TimeZone to get a LocalDateTime

And from there you can keep on exploring into Calendars and Partial-Dates and Date-Math and Durations and...

I realized another reason that merging LocalDateTime and DateTime may make sense: many countries are considering getting rid of DST changes permanently. The EU is set to do this in 2021. So it's possible that very few countries may still use DST in 5-10 years. If this happens, then DateTime and LocalDateTime would always return the same results. Having two types that always return the same results in almost all countries (at least for non-historic times) would probably seem like an unfortunate legacy mistake. Having only one type might future-proof the API in case this happens.

@justingrant I think that's rather shortsighted for several reasons:

  1. "very few countries" using DST is still not "zero countries" using it, which means the separation is still needed. Indeed, the U.S. and Australia haven't made any noises about removing DST.
  2. Just because the EU is considering ditching DST at the moment, that doesn't mean that they won't try to re-introduce it again many years later. Looking outside the EU, the DST history of countries like Morocco and Egypt (with on-again, off-again whims of governments) show that you can't take a current scenario for granted.
  3. You briefly mentioned "at least for non-historic times" as if dates/times in the past aren't important. If Temporal is expected to handle dates in the past (which is very, very common), it must keep DST distinctions.

@pipobscure - LocalDateTime's internal data model is (Absolute, TimeZone, Calendar), because DateTime->Absolute is potentially ambiguous but Absolute->DateTime is not.

So the chart above, if it's only considering the internal data model, has DateTime and Absolute reversed. That said, the way I've been thinking about this is from the API consumer's perspective, in which it's the _external_ data model that matters most. The external data model of LocalDateTime is (Absolute, TimeZone, DateTime). The fact that the DateTime part is derived from (Absolute, TimeZone, Calendar) is an implementation detail that won't be important to most API consumers.

I think of Calendar in a similar way. Regardless of where the calendar internal slot lives, for the purposes of the external usage, a Calendar is logically part of a Date because it has no impact on times.

I think this general approach-- to not require the internal slots to determine how we document Temporal types-- is probably a good idea, because most developers care more about what they can do with a type and less about what we do behind the scenes.

So from the API user's standpoint, I think the model looks like this:

image

@justingrant I think that's rather shortsighted for several reasons:

OK I'm convinced. ;-)

@pipobscure - LocalDateTime's internal data model is (Absolute, TimeZone, Calendar), because DateTime->Absolute is potentially ambiguous but Absolute->DateTime is not.

you’re of course right. I was thinking in terms of the mental model. And in those terms it’s conceptionaly / didactically important to be clear on the main characteristics of the objects.

  • DateTime / Date / Time - are abstract ideas that that humans use and that may not necessarily correspond to real time (think a slow watch or a desk calendar that hasn’t flipped the day)
  • Absolute - is directly tied to the absolute timeline and is monotonically increasing
  • TimeZone - is a way to connect the two
  • LocalDateTime - is the combination (conceptionaly) between TimeZone and DateTime that let’s you connect your watch to actual times

These broad strokes make it much easier to explain, because it makes clear why there is a DateTime & LocalDateTime & Absolute and what the differences are. It also let’s one reason about which one is the right choice for any use-case.


Why I’m such a stickler for these concepts is that I think they are what will make the API succeed. Because the conceptual clarity is what in the end allows you to choose the right solution without being an expert in edge-cases.

I think we’re putting a lot of faith in education for temporal and that’s only going to be successful if we keep the concepts clear.

On that note, I think I’ve been convinced by @ptomato arguing against adding y/m/d/h/m/s/... to Absolute I think it’s probably better if we don’t.

  • DateTime / Date / Time - are abstract ideas that that humans use and that may not necessarily correspond to real time (think a slow watch or a desk calendar that hasn’t flipped the day)
  • Absolute - is directly tied to the absolute timeline and is monotonically increasing
  • TimeZone - is a way to connect the two

I think this is a great summary of these types!

  • LocalDateTime - is the combination (conceptionaly) between TimeZone and DateTime that let’s you connect your watch to actual times

I see it as more like this:

  • LocalDateTime - combines all three types to represent a real-world event: something that happens at a specific instant (an Absolute time) in a specific place (therefore in a specific TimeZone) with a human-friendly representation (a DateTime in a specific Calendar).

I think it's OK if different people think of it differently. It's not "wrong" if a LocalDateTime feels more like a DateTime+TimeZone to you but more like Absolute+TimeZone+DateTime to me.

BTW, another thing I like about your "one sentence to describe each Temporal type" list is that we can come up with N different possible wordings and then send early adopters a short survey about which explanation for each Temporal type makes the most sense. This could help us evade the problem that documentation is often hard to get feedback on.

The explanation we came up with on Friday was: Let's think of LocalDateTime not as an "everything type" but as an "event that happened, or will happen, in a place", and ask ourselves what _not_ to have in the type, taking that into account. We'll evaluate the other open questions using this framework.

My comments in the OP are addressed. I'm convinced that it is useful for LocalDateTime to carry a calendar slot. I don't know if there is anything remaining in the conversation between @justingrant and @pipobscure regarding the data model.

This seems to be ready to be closed.

Was this page helpful?
0 / 5 - 0 ratings