difference does a good job of communicating that the returned value is a Duration instance, but a very poor job of communicating the meaning of its sign.
// How many days are left in this hell of a year?!?
let now = Temporal.now.date();
let remaining = now.with({year: today.year + 1, month: 1, day: 1}).difference(now, {largestUnit: "days"});
// The magnitude of `remaining` must be reduced by one day, but that looks different based on its sign.
// Authors (and maintainers!) need to check if difference is "this minus that" or "that minus this".
remaining = remaining.subtract({days: 1});
// Given two Temporal instance values of unknown specific type, which is greater?
let sign = x1.difference(x2).sign;
// Authors (and maintainers!) need to check if difference is "this minus that" or "that minus this".
let x1IsLater = sign > 0;
This situation would be much better if difference were replaced with a method that clearly communicated directionality.
// How many days are left in this hell of a year?!?
let now = Temporal.now.date();
let remaining = now.with({year: today.year + 1, month: 1, day: 1}).since(now, {largestUnit: "days"});
// `remaining` is obviously nonnegative, so there's an obvious way to reduce it by one day.
remaining = remaining.subtract({days: 1});
// Given two Temporal instance values of unknown specific type, which is greater?
let sign = x1.since(x2).sign;
// `sign` is positive if and only if x1 is later than x2.
let x1IsLater = sign > 0;
I like since or durationSince, but the polarity could also be inverted (i.e., "that minus this") with something like until or durationUntil.
Personally, since is still a bit confusing when your use-case is to just check if an instance is "later" or "earlier" than another.
Mimmicking the java.time library, I'd propose two new methods for this specific usecase:
let x1IsLater = x1.after(x2) // Or maybe x1.isAfter(x2)
let x1IsEarlier = x1.before(x2) // Or maybe x1.isBefore(x2)
while these methods are very similar and somewhat redundant, they an added benefit: since().sign > 0 is hard to parse for a human reader, but .after is much easier to read.
I don't mean to remove the difference/since method, but I feel like this would be an improvement over the current API for this specific(and very common) use case. difference/ since would still be useful to know exactly _how long_ is has been from one temporal to another.
Note that, considering the redundancy of methods, Temporal.Instant.compare also serves the purpose of determining wether a temporal instance is before or after another temporal instant; that is, Temporal.Instant.compare(x1,x2) has the same value as x1.difference(x2).sign. Since we already have these two "redundant" forms of comparison, IMO new methods with the purpose of legibility are worth it.
FWIW, Java uses until for this operation on its equivalent of the PlainDate type:
https://docs.oracle.com/javase/8/docs/api/java/time/LocalDate.html#until-java.time.chrono.ChronoLocalDate-
Given that we're matching Java's terminology in other places (e.g. Instant, ZonedDateTime), if we do change the name of this method then I'd vote for until over since for this reason.
until would reverse the order of the operation: it'd start from this and go to other.
Meeting 2020-10-23:
difference => sinceuntil, but only if its implementation is not very difficultBy happy accident I found I had written this code in one of the cookbook recipes:
const duration = Temporal.Instant.from('2020-04-01T13:00-07:00[America/Los_Angeles]').difference(Temporal.now.instant());
`It's ${duration.toLocaleString()} ${duration.sign < 0 ? 'until' : 'since'} the TC39 Temporal presentation`;
OK, I think I've puzzled this out — in any case, Instant.until() is trivial to implement because there is no calendar involved.
For the other types, the implementation should be easy as well, although I've realized there are some bugs in the Duration rounding algorithm.
For earlier.until(later):
For later.since(earlier):
As an example with Temporal.Date, we'll use _earlier_ = 2019-01-01, _later_ = 2019-02-15, giving a _diff_ of 45 days. Let's say we want to round to the nearest month:
Temporal.Date.from('2019-02-15').since('2019-01-01', { smallestUnit: 'months' }):
Temporal.Duration.from({ months: 1 })Temporal.Date.from('2019-01-01').until('2019-02-15', { smallestUnit: 'months' }):
Temporal.Duration.from({ months: 2 })The deal is that these answers should be the same as Temporal.Duration.from({ days: -45 }).round({ relativeTo: '2019-02-15', smallestUnit: 'months' }) and Temporal.Duration.from({ days: 45 }).round({ relativeTo: '2019-01-01', smallestUnit: 'months' }) respectively, and that is not currently the case.
I will proceed by implementing the until() methods as described, and will fix the bugs in the rounding algorithm separately.
(In other words, the _relative_ point is not necessarily the _starting_ point. For since(), it's the _ending_ point.)
@ptomato - I believe your analysis is correct. BTW, I was starting to look at two things today:
total()calendar.daysInYear(relativeTo) (or daysinMonth, etc.) is used instead of calculating the actual number of days between relativeTo and one year earlier/later. Should I wait on these two things until your changes to the rounding algorithm are finished?
@justingrant: I think exactly that issue is one of the two problems with the rounding algorithm that need to be fixed, so no need to try to reproduce it any longer. If you have time to implement total() this week, that would be really helpful, and I don't think it needs to wait on anything. The rounding algorithm will only need to change in edge cases.
Hey @gibson042 - Kudos for pushing to make this change. In a few days of using the new names while writing test code, I'm amazed at how much easier it is to understand what each operations will do. Thanks!
Most helpful comment
By happy accident I found I had written this code in one of the cookbook recipes: