Skip to content

Why your Notion days left formula is negative

7 min read
A small white desk calendar on pale fabric

In the due today article, we used dateBetween() to build Due today, Due tomorrow and Overdue. This article is about the number underneath those labels, and why it sometimes comes back negative. πŸ’―

This article is a part of the Fantastic Formulas series. This series features helpful articles about formulas in Notion.

A days-left formula that shows -14 on a renewal that is still two weeks away is not a broken Notion. dateBetween() is a subtraction. First date minus second date. Swap the two dates and every future deadline flips sign. A blank date is the other quiet failure. dateBetween() on an empty cell does not return 0. It returns empty, and an automation that then operates on that date pauses. πŸ‘€

If you want the labels themselves, the due today article already builds them. Today we will only fix the sign, and the blank. Just like in the previous article, I will proceed with a simple database as an example. This time, let’s say we want to track client retainers. πŸ‘€

In this database shown below, you can see 3 properties. First property shows the client name, second one shows the day they signed, and the third property shows the renewal date. βœ…

Retainers database with signed dates and renewal dates Example - Simple Retainers database

I am treating 26 Sep 2026 as β€œtoday” in the screenshots, so Acme renews in 14 days, Northwind renewed 14 days ago, Harbor renews today, and Studio has no renewal date.

1. dateBetween()

dateBetween() returns the difference between two dates, in a unit you choose. The unit can be "years", "quarters", "months", "weeks", "days", "hours", or "minutes". The syntax is

dateBetween(date1, date2, "days")

dateBetween() function syntax

Read it as date1 minus date2. Notion’s own examples put the later date first when they want a positive number. Counting from an old date up to now is positive. Counting from now up to a future date is also positive. Swap either pair and the sign flips. βœ…

Say we want days left until renewal, and we put now() first because that feels like β€œstart from today”. That is the bug. People usually write it without dateStart(). The sign is already wrong before the clock gets involved. The formula below wraps both sides in dateStart() so we are comparing calendar days, which is the fix from the due today article. It does not fix the order.

dateBetween(dateStart(now()), dateStart(prop("Renewal")), "days")

dateBetween() with the dates backwards

As you can see below, the formula is perfectly working πŸ”½

Days left formula with the two dates swapped dateBetween() with the dates backwards

  • Acme β†’ -14 (the renewal is 14 days away, and the formula says it is 14 days late)
  • Northwind β†’ 14 (the renewal was 14 days ago, and the formula says there are 14 days left)
  • Harbor β†’ 0 (renews today, so the swap does not show)
  • Studio β†’ empty, because there is no renewal date

Northwind is the row that causes the real damage. A positive number looks healthy. It is not. The deadline has already passed. πŸ‘€

2. Days left, days since

Days left and days since signed are the same function. Only the order changes. Put the deadline first when you want days left. Put today first when you want days since. βœ…

dateBetween(dateStart(prop("Renewal")), dateStart(now()), "days")

Days left, renewal minus today

dateBetween(dateStart(now()), dateStart(prop("Signed")), "days")

Days since signed, today minus the signed date

As you can see below, the formula is perfectly working πŸ”½

Days left and days since signed in the same Retainers table Same function, two questions

  • Acme β†’ 14 days left, and 730 days since they signed on 26 Sep 2024
  • Northwind β†’ -14 days left, and 365 days since they signed on 26 Sep 2025
  • Harbor β†’ 0 and 0, because both dates are today
  • Studio β†’ empty days left, and 268 days since they signed on 1 Jan 2026

A negative days-left number is useful when you asked the right question. It means the renewal is in the past. You want that minus sign on Northwind. You do not want it on Acme. If the number is only off by one in the afternoon, that is the other bug. Go back to the due today article and wrap both sides in dateStart(). βœ…

3. dateEnd()

A date property can be a range, with a start and an end. dateStart() hands back the start. dateEnd() hands back the end. Length of the contract is end minus start. The syntax is

dateBetween(dateEnd(prop("Contract")), dateStart(prop("Contract")), "days")

Contract length, end minus start

Swap those two and Notion does not throw an error. It returns the same number with the sign flipped. Notion’s formula reference uses a one-year range for this exact case: end minus start is 365, start minus end is -365. βœ…

dateBetween(dateStart(prop("Contract")), dateEnd(prop("Contract")), "days")

Same range, dates swapped

As you can see below, the formula is perfectly working πŸ”½

Contract length next to the same length with the dates swapped dateEnd() minus dateStart()

  • Acme, 26 Sep 2025 to 26 Sep 2026 β†’ 365, and -365 if you swap
  • Northwind, 1 Jan 2026 to 1 Jul 2026 β†’ 181, and -181 if you swap
  • Harbor, 26 Sep 2026 to 26 Sep 2027 β†’ 365, and -365 if you swap
  • Studio has no contract range, so both columns stay empty

4. empty()

Studio is the row people forget to test. There is no renewal date. dateBetween() does not turn that into 0. The column stays empty, same as the Reading log row in the due today article. Empty is not false, and it is not zero. empty(0) is true in Notion, which is how you can tell the two apart. πŸ‘€

If this formula sits inside a button or an automation, a blank date is stricter than an empty cell. Notion pauses the automation when the formula operates on an empty date or an empty person. Check the value before you touch it. βœ…

if(
  empty(prop("Renewal")),
  empty(),
  dateBetween(dateStart(prop("Renewal")), dateStart(now()), "days")
)

empty() guard before dateBetween()

empty() on that middle branch is a blank that keeps the result a number. Do not put "" there. A pair of quotes is text, the day count is a number, and the return type becomes unknown. Notion’s own error doc shows the same mistake with a date: if(Date, Date.dateAdd(1, "day"), "") can be a date or text, so an automation will not save. A day count and a pair of quotes fails the same way. πŸ‘€

Say we want the blank row to say something, instead of sitting there looking broken. Both branches have to be text, so wrap the number in format(). βœ…

if(
  empty(prop("Renewal")),
  "No date",
  format(dateBetween(dateStart(prop("Renewal")), dateStart(now()), "days"))
)

Text label when the date is blank

As you can see below, the formula is perfectly working πŸ”½

Blank renewal guarded so Studio says No date instead of zero empty() guard, then a text label

Studio says No date. Acme says 14. Northwind says -14. Harbor says 0. Nobody gets a fake zero for a date that was never set.


Conclusion

Now you know why a days-left formula goes negative inside Notion! dateBetween() subtracts the second date from the first. Put the deadline first when you want days left. Put today first when you want days since. On a date range, put dateEnd() first. And if the date might be blank, call empty() before you call dateBetween(). πŸ”₯

I hope this article was helpful and easy to understand. At Prototion, we are on mission to make Notion easy for everyone! So we regularly post Helpful articles about Notion like this one. πŸ“„

Remember to check out the Fantastic Formulas series for more articles about Notion formulas. Doesn’t matter if you are a Notion beginner or a pro, I believe you will find this series helpful. Also, don’t forget to follow us on 🐦 Twitter for daily updates, latest Notion template launches and amazing deals & discounts on Premium Notion templates! 😍

Templates that do this for you

Built by independent makers on Prototion.