LeetCode 1360 - Number of Days Between Two Dates
The problem gives two dates in the format YYYY-MM-DD and asks us to compute the absolute number of days between them. Each input string represents a valid calendar date. The year, month, and day are separated by hyphens.
Difficulty: 🟢 Easy
Topics: Math, String
Solution
Problem Understanding
The problem gives two dates in the format YYYY-MM-DD and asks us to compute the absolute number of days between them.
Each input string represents a valid calendar date. The year, month, and day are separated by hyphens. For example:
"2019-06-29"means June 29, 2019"2020-01-15"means January 15, 2020
The expected output is a single integer representing how many days separate the two dates. The order of the dates does not matter because the answer is the absolute difference.
The main challenge is handling the Gregorian calendar correctly, especially leap years. A leap year has 366 days instead of 365 days, which affects the number of days in February.
A year is a leap year if:
- It is divisible by 400, or
- It is divisible by 4 but not divisible by 100
Examples:
- 2020 is a leap year
- 2000 is a leap year
- 1900 is not a leap year
The constraints are relatively small, dates are only between 1971 and 2100. Even a straightforward simulation approach would work within these limits. However, the cleaner and more scalable solution is to convert each date into the number of days from a fixed reference point, then compute the absolute difference.
Several edge cases are important:
- Dates in the same month
- Dates across different months
- Dates across different years
- Leap year transitions, especially February 29
- Inputs where
date1 > date2 - Boundary years like 1971 and 2100
The problem guarantees that all dates are valid, so we do not need to validate month ranges or day ranges ourselves.
Approaches
Brute Force Approach
The brute force method simulates day-by-day movement from one date to the other.
The idea is simple:
- Determine which date is earlier.
- Repeatedly advance the earlier date by one day.
- Count how many increments are required until the dates become equal.
To advance a date correctly, we must:
- Know the number of days in each month
- Handle leap years
- Move to the next month when the day exceeds the month limit
- Move to the next year when the month exceeds December
This approach is correct because every iteration represents exactly one calendar day.
However, it is inefficient because we may need to simulate tens of thousands of days. While the constraints are small enough for this to pass, the approach is unnecessarily complicated and slower than needed.
Optimal Approach
The better approach converts each date into a single integer representing the total number of days elapsed since a fixed reference date.
For example:
- Count all days from year 0 up to the target year
- Add all days from completed months in the current year
- Add the current day
Once both dates are converted into day counts, the answer becomes:
abs(days1 - days2)
The key insight is that comparing dates becomes trivial once dates are represented as cumulative day counts.
This avoids repeated simulation and reduces the problem to arithmetic.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(D) | O(1) | Simulates every day between the two dates |
| Optimal | O(1) | O(1) | Converts each date into an absolute day count |
Here, D is the number of days between the two dates.
Algorithm Walkthrough
Optimal Algorithm
- Parse the date string into integers for year, month, and day.
The input format is fixed as YYYY-MM-DD, so we can split the string by "-" and convert each part into integers.
2. Create a helper function to determine whether a year is a leap year.
This is necessary because February has either 28 or 29 days depending on the year. 3. Create a helper function that converts a date into the total number of elapsed days.
This function computes:
- All days contributed by complete years before the current year
- All days contributed by complete months before the current month
- The current day value
- Count days from previous years.
For every year before the current year:
- Add 365 days
- Add 1 extra day if the year is a leap year
- Count days from previous months in the current year.
Use an array of month lengths:
[31,28,31,30,31,30,31,31,30,31,30,31]
If the current year is a leap year, February becomes 29 days. 6. Add the current day.
After processing completed months, add the day value itself. 7. Compute the absolute difference.
Convert both dates into total day counts and return:
abs(days1 - days2)
Why it works
The algorithm works because every date is mapped to a unique cumulative day count measured from a fixed origin. The difference between two cumulative counts is exactly the number of days separating the dates. Leap years are explicitly handled during year and month calculations, ensuring that the calendar structure is represented correctly.
Python Solution
class Solution:
def daysBetweenDates(self, date1: str, date2: str) -> int:
def is_leap(year: int) -> bool:
return (
year % 400 == 0 or
(year % 4 == 0 and year % 100 != 0)
)
def to_days(date: str) -> int:
year, month, day = map(int, date.split("-"))
month_days = [
31, 28, 31, 30, 31, 30,
31, 31, 30, 31, 30, 31
]
total_days = 0
# Add days from previous years
for y in range(year):
total_days += 365
if is_leap(y):
total_days += 1
# Adjust February for leap year
if is_leap(year):
month_days[1] = 29
# Add days from previous months
for m in range(month - 1):
total_days += month_days[m]
# Add current day
total_days += day
return total_days
return abs(to_days(date1) - to_days(date2))
The implementation begins with the is_leap helper function, which encapsulates the leap year rules. Separating this logic into its own function improves readability and avoids duplication.
The to_days helper converts a date string into a cumulative day count. First, the date is parsed into integers. Then we iterate through all years before the target year and add either 365 or 366 days depending on whether the year is a leap year.
Next, we adjust February if the current year is a leap year. After that, we add the lengths of all completed months before the current month.
Finally, we add the current day itself and return the total cumulative count.
The main function simply computes the absolute difference between the two converted values.
Go Solution
func daysBetweenDates(date1 string, date2 string) int {
isLeap := func(year int) bool {
return year%400 == 0 || (year%4 == 0 && year%100 != 0)
}
toDays := func(date string) int {
var year, month, day int
fmt.Sscanf(date, "%d-%d-%d", &year, &month, &day)
monthDays := []int{
31, 28, 31, 30, 31, 30,
31, 31, 30, 31, 30, 31,
}
totalDays := 0
// Add days from previous years
for y := 0; y < year; y++ {
totalDays += 365
if isLeap(y) {
totalDays++
}
}
// Adjust February for leap year
if isLeap(year) {
monthDays[1] = 29
}
// Add days from previous months
for m := 0; m < month-1; m++ {
totalDays += monthDays[m]
}
// Add current day
totalDays += day
return totalDays
}
result := toDays(date1) - toDays(date2)
if result < 0 {
return -result
}
return result
}
The Go solution follows the same logic as the Python version. The main difference is date parsing. Instead of split, Go uses fmt.Sscanf to extract the year, month, and day values directly from the formatted string.
Go does not provide a built-in absolute value function for integers in the standard library without conversion, so the code manually checks whether the result is negative.
Slices are used for month lengths, similar to Python lists.
Worked Examples
Example 1
Input:
date1 = "2019-06-29"
date2 = "2019-06-30"
Convert "2019-06-29"
| Step | Value |
|---|---|
| Days from years before 2019 | 736694 |
| Days from Jan through May | 151 |
| Current day | 29 |
| Total | 736874 |
Convert "2019-06-30"
| Step | Value |
|---|---|
| Days from years before 2019 | 736694 |
| Days from Jan through May | 151 |
| Current day | 30 |
| Total | 736875 |
Difference:
abs(736874 - 736875) = 1
Output:
1
Example 2
Input:
date1 = "2020-01-15"
date2 = "2019-12-31"
Convert "2020-01-15"
| Step | Value |
|---|---|
| Days from years before 2020 | 737059 |
| Days from previous months | 0 |
| Current day | 15 |
| Total | 737074 |
Convert "2019-12-31"
| Step | Value |
|---|---|
| Days from years before 2019 | 736694 |
| Days from Jan through Nov | 334 |
| Current day | 31 |
| Total | 737059 |
Difference:
abs(737074 - 737059) = 15
Output:
15
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(1) | The number of years and months processed is bounded by constants |
| Space | O(1) | Only a few variables and a fixed-size month array are used |
Although the implementation loops through years, the maximum range is fixed by the constraints, between 1971 and 2100. Therefore the runtime is effectively constant. The algorithm does not allocate memory proportional to the input size, so the space complexity is constant as well.
Test Cases
solution = Solution()
assert solution.daysBetweenDates("2019-06-29", "2019-06-30") == 1
# Basic adjacent dates
assert solution.daysBetweenDates("2020-01-15", "2019-12-31") == 15
# Crossing year boundary
assert solution.daysBetweenDates("2020-02-28", "2020-03-01") == 2
# Leap year February transition
assert solution.daysBetweenDates("2019-02-28", "2019-03-01") == 1
# Non-leap year February transition
assert solution.daysBetweenDates("2020-02-29", "2020-03-01") == 1
# Leap day handling
assert solution.daysBetweenDates("1971-01-01", "1971-01-01") == 0
# Same date
assert solution.daysBetweenDates("2100-12-31", "1971-01-01") > 0
# Large date range
assert solution.daysBetweenDates("2000-01-01", "1999-12-31") == 1
# Century leap year
assert solution.daysBetweenDates("1900-03-01", "1900-02-28") == 1
# 1900 is not a leap year
assert solution.daysBetweenDates("2021-12-31", "2022-01-01") == 1
# New year transition
| Test | Why |
|---|---|
"2019-06-29" vs "2019-06-30" |
Validates basic one-day difference |
"2020-01-15" vs "2019-12-31" |
Tests crossing into a new year |
"2020-02-28" vs "2020-03-01" |
Tests leap year February behavior |
"2019-02-28" vs "2019-03-01" |
Tests normal February behavior |
"2020-02-29" vs "2020-03-01" |
Verifies leap day correctness |
| Same date input | Ensures zero difference works |
| Very large range | Tests upper constraint range |
"2000-01-01" vs "1999-12-31" |
Validates century leap year |
"1900-03-01" vs "1900-02-28" |
Ensures non-leap century handling |
"2021-12-31" vs "2022-01-01" |
Tests year rollover |
Edge Cases
Leap Year February
Dates around February are the most common source of bugs because February changes length depending on the year. For example, 2020 has February 29 while 2019 does not. If the implementation always assumes February has 28 days, the answer becomes incorrect for leap years. The solution handles this by dynamically changing February to 29 days whenever the current year satisfies the leap year condition.
Same Input Dates
If both dates are identical, the correct answer is 0. Some implementations accidentally return 1 because they count inclusive ranges instead of differences. This implementation avoids that problem because it converts both dates into cumulative counts and subtracts them directly.
Dates Given in Reverse Order
The problem does not guarantee that date1 is earlier than date2. A naive subtraction could produce a negative result. The implementation solves this by returning the absolute value of the difference, ensuring the output is always non-negative.
Year Boundary Transitions
Transitions like December 31 to January 1 are easy to mishandle when manually simulating dates. The cumulative day count approach naturally handles year rollover because the total day count continuously increases across years.
Century Leap Year Rules
Years divisible by 100 are not leap years unless they are also divisible by 400. This rule is easy to forget. For example:
- 1900 is not a leap year
- 2000 is a leap year
The implementation explicitly encodes this rule in the is_leap function, ensuring correct calendar calculations.