2777. Date Range Generator
Problem Description
The problem requires us to create a range of dates that starts with a given "start" date and ends at an "end" date, including both start and end dates. The range should be generated using a given "step" value, which specifies the interval between consecutive dates in terms of days. We want to yield each date in this range as a string in the format "YYYY-MM-DD".
Intuition
To solve this problem, we use the JavaScript Date object, which enables us to work easily with dates. The procedure includes:
-
Parsing the provided "start" and "end" string dates into JavaScript Date objects, which allows us to manipulate dates with built-in methods.
-
Iterating from the start date to the end date, increasing the date by the step value every iteration. We achieve this by using the
getDate
andsetDate
methods that the Date object provides. ThegetDate
method gets the day of the month from a Date object andsetDate
sets the day of the month to a specified number. By adding the step to the current day, we move our date forward by that many days. -
During each iteration, we convert the current date to an ISO string using
toISOString
. ISO strings are in the format "YYYY-MM-DDTHH:MM:SS.ZZZZ". However, since we only need the date part without the time, we useslice
to obtain the first 10 characters of the ISO string which represent the date in the required "YYYY-MM-DD" format. -
Yielding the formatted date string using
yield
, which is part of creating a generator. Generators are special functions in JavaScript that can be exited and re-entered later with their context (variable bindings) being saved between re-entries. -
Continuing this process until the current date exceeds the end date, at which point the generator finishes.
This solution effectively gives us a custom range of dates, and by using a generator, we can efficiently go through the range one date at a time without needing to precompute the entire range at once.
Solution Approach
The solution to this problem involves a step-by-step approach using a generator function to yield the desired range of dates one by one. Here's how the solution is implemented:
-
Initialize Dates: First, we need to convert the input start and end strings into
Date
objects using JavaScript'snew Date()
constructor. This allows us to perform date arithmetic and make use of Date object methods. -
Create Generator Function: We then declare a generator function called
dateRangeGenerator
that takesstart
,end
, andstep
as arguments. A generator function is defined withfunction*
syntax and is capable of yielding values one at a time with theyield
keyword. -
Iterate Through Dates: Inside the generator function, we initiate a
while
loop that will continue as long as thecurrentDate
is less than or equal to theendDate
. -
Yield Current Date: For each iteration within the loop, the current date (formatted as "YYYY-MM-DD" using
toISOString().slice(0, 10)
) isyield
ed. This means that every time the generator'snext()
method is called, it will return an object with a value of the current date string and a done status indicating whether the generator has finished iterating. -
Increment Date: To move to the next date in the range, we use
currentDate.getDate()
to get the day of the month forcurrentDate
, add thestep
value to it, and updatecurrentDate
with this new value usingcurrentDate.setDate()
. This effectively incrementscurrentDate
by thestep
number of days. -
Finish Iteration: As soon as
currentDate
exceedsendDate
, the condition in thewhile
loop becomes false, and the generator finishes its execution. Any further calls tonext()
after this point will return an object with a done status of true, indicating there are no more values to yield.
By utilizing a generator function and the JavaScript Date object, the solution elegantly traverses a range of dates and provides them on-demand, handling date arithmetic internally without the caller needing to manage the date range state. This allows for an efficient, on-the-fly generation of date strings that can be iterated over using the generator's next()
method.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's consider a small example to illustrate the solution approach. Suppose we want to create a range of dates from "2021-11-01" to "2021-11-05" with a step of 2 days. We will use the outlined solution approach to generate the desired date strings.
-
Initialize Dates: We convert the input strings "2021-11-01" and "2021-11-05" to JavaScript Date objects:
const startDate = new Date("2021-11-01"); const endDate = new Date("2021-11-05");
-
Create Generator Function: We create a generator function
dateRangeGenerator
which takesstartDate
,endDate
, andstep
as parameters:function* dateRangeGenerator(start, end, step) { // Generator function body will be implemented here }
-
Iterate Through Dates: We initiate a loop inside the generator function, where we will generate dates from
startDate
toendDate
. We use a while loop for this purpose:let currentDate = new Date(start.getTime()); // avoid modifying the original start date while (currentDate <= end) { // The body of the loop will yield dates and increment `currentDate` }
-
Yield Current Date: During each iteration, we format the
currentDate
as a string and yield it:yield currentDate.toISOString().slice(0, 10);
-
Increment Date: After yielding the date, we increment
currentDate
by thestep
value. In this example, we add 2 days:const nextDay = currentDate.getDate() + step; currentDate.setDate(nextDay); // This will increment `currentDate` by 2 days.
-
Finish Iteration: The loop continues until
currentDate
is greater thanendDate
. When that condition is met, the loop terminates, and the generator finishes:// Loop has ended, so the generator is complete.
Putting it all together, our generator function will be used as follows:
const dateRange = dateRangeGenerator(startDate, endDate, 2);
console.log(dateRange.next().value); // "2021-11-01"
console.log(dateRange.next().value); // "2021-11-03"
console.log(dateRange.next().value); // "2021-11-05"
console.log(dateRange.next().done); // true, as no more dates are left
The output of each console.log
illustrates how the generator yields each date string when .next()
is called. After the last date, calling .next()
indicates that the generator is finished by returning {done: true}
.
Solution Implementation
1from datetime import datetime, timedelta
2
3# This generator function yields a range of dates, incrementing by a given step in days.
4# Parameters:
5# - start_date_str: The start date in ISO format (YYYY-MM-DD).
6# - end_date_str: The end date in ISO format (YYYY-MM-DD).
7# - step_days: The number of days to increment each time.
8# It yields a string representing the current date in ISO format (YYYY-MM-DD) at each iteration.
9def date_range_generator(start_date_str, end_date_str, step_days):
10 # Convert the start and end date strings into datetime objects.
11 start_date = datetime.fromisoformat(start_date_str)
12 end_date = datetime.fromisoformat(end_date_str)
13 # Initialize the current date to the start date.
14 current_date = start_date
15
16 # Continue yielding dates until the current date exceeds the end date.
17 while current_date <= end_date:
18 # Yield the current date as a string in ISO date format.
19 yield current_date.strftime('%Y-%m-%d')
20 # Increment the current date by the step value in days.
21 current_date += timedelta(days=step_days)
22
23# Usage example:
24date_generator = date_range_generator('2023-04-01', '2023-04-04', 1)
25for date in date_generator:
26 print(date) # Prints '2023-04-01', '2023-04-02', '2023-04-03', '2023-04-04'
27
1import java.time.LocalDate;
2import java.time.temporal.ChronoUnit;
3import java.util.Iterator;
4import java.util.NoSuchElementException;
5
6/**
7 * This Iterable class provides a range of dates, incrementing by a given step in days.
8 * Parameters:
9 * - start: The start date in ISO format (YYYY-MM-DD).
10 * - end: The end date in ISO format (YYYY-MM-DD).
11 * - step: The number of days to increment each time.
12 * It yields a string representing the current date in ISO format (YYYY-MM-DD) on each iteration.
13 */
14public class DateRange implements Iterable<String> {
15
16 private LocalDate startDate;
17 private LocalDate endDate;
18 private int step;
19
20 public DateRange(String start, String end, int step) {
21 this.startDate = LocalDate.parse(start);
22 this.endDate = LocalDate.parse(end);
23 this.step = step;
24 }
25
26 @Override
27 public Iterator<String> iterator() {
28 return new Iterator<String>() {
29
30 private LocalDate currentDate = startDate;
31
32 @Override
33 public boolean hasNext() {
34 // Check if the current date has not passed the end date
35 return !currentDate.isAfter(endDate);
36 }
37
38 @Override
39 public String next() {
40 if (!hasNext()) {
41 throw new NoSuchElementException("No more dates to generate.");
42 }
43 // Store the current date as it should be returned
44 LocalDate current = currentDate;
45 // Increment the current date by the step value in days
46 currentDate = currentDate.plusDays(step);
47 // Return the current date as a string in ISO date format
48 return current.toString();
49 }
50 };
51 }
52
53}
54
55// Usage example:
56
57public class Main {
58 public static void main(String[] args) {
59 DateRange dateRange = new DateRange("2023-04-01", "2023-04-04", 1);
60 for (String date : dateRange) {
61 System.out.println(date); // Outputs each date in the range
62 }
63 }
64}
65
1#include <iostream>
2#include <ctime>
3#include <string>
4
5// This class represents a generator that yields a range of dates, incrementing by a given step in days.
6class DateRangeGenerator {
7public:
8 // Constructs the generator with start, end dates and step value.
9 // - startDate: The start date in ISO format (YYYY-MM-DD).
10 // - endDate: The end date in ISO format (YYYY-MM-DD).
11 // - stepDays: The number of days to increment each time.
12 DateRangeGenerator(const std::string& startDate, const std::string& endDate, int stepDays)
13 : endDate(ConvertToDate(endDate)), step(stepDays) {
14 currentDate = ConvertToDate(startDate);
15 }
16
17 // Checks if there are more dates to generate.
18 bool HasNext() const {
19 return currentDate <= endDate;
20 }
21
22 // Returns the next date in the range, moving forward by the step value.
23 std::string Next() {
24 if (!HasNext()) {
25 return ""; // End of the range, no more dates to generate.
26 }
27
28 std::string currentDateString = ConvertToString(currentDate);
29 IncrementDate(currentDate, step); // Move to the next date.
30 return currentDateString;
31 }
32
33private:
34 // Converts ISO date string to tm structure.
35 tm ConvertToDate(const std::string& isoDate) const {
36 tm date = {};
37 sscanf(isoDate.c_str(), "%d-%d-%d", &date.tm_year, &date.tm_mon, &date.tm_mday);
38 date.tm_year -= 1900; // tm_year is years since 1900.
39 date.tm_mon -= 1; // tm_mon is 0-based.
40 return date;
41 }
42
43 // Converts tm structure to ISO date string.
44 std::string ConvertToString(const tm& date) const {
45 char buffer[11];
46 // Add 1900 to tm_year to get the full year and 1 to tm_mon as it is 0-based.
47 snprintf(buffer, sizeof(buffer), "%04d-%02d-%02d", date.tm_year + 1900, date.tm_mon + 1, date.tm_mday);
48 return std::string(buffer);
49 }
50
51 // Increments the date by given step value in days.
52 void IncrementDate(tm& date, int days) const {
53 const time_t ONE_DAY = 24 * 60 * 60; // One day in seconds.
54 // Convert tm structure to time_t.
55 time_t date_time = mktime(&date);
56 // Increment the date by the number of days converted to seconds.
57 date_time += days * ONE_DAY;
58 // Convert back to tm structure.
59 date = *localtime(&date_time);
60 }
61
62 tm currentDate; // Current date to yield.
63 tm endDate; // End date for the range.
64 int step; // Increment step in days.
65};
66
67/*
68// Usage example:
69int main() {
70 // Create a date range generator from April 1st to April 4th with a step of 1 day.
71 DateRangeGenerator dateGenerator("2023-04-01", "2023-04-04", 1);
72
73 // Iterate through the generated dates and print them.
74 while (dateGenerator.HasNext()) {
75 std::cout << dateGenerator.Next() << std::endl;
76 }
77
78 return 0;
79}
80*/
81
1// This generator function yields a range of dates, incrementing by a given step in days.
2// Parameters:
3// - start: The start date in ISO format (YYYY-MM-DD).
4// - end: The end date in ISO format (YYYY-MM-DD).
5// - step: The number of days to increment each time.
6// It yields a string representing the current date in ISO format (YYYY-MM-DD) at each iteration.
7function* dateRangeGenerator(start: string, end: string, step: number): Generator<string> {
8 // Convert the start and end strings into Date objects.
9 const startDate = new Date(start);
10 const endDate = new Date(end);
11 // Initialize the current date to the start date.
12 let currentDate = startDate;
13
14 // Continue yielding dates until the current date exceeds the end date.
15 while (currentDate <= endDate) {
16 // Yield the current date as a string in ISO date format.
17 yield currentDate.toISOString().slice(0, 10);
18 // Increment the current date by the step value in days.
19 currentDate.setDate(currentDate.getDate() + step);
20 }
21}
22
23/*
24// Usage example:
25const dateGenerator = dateRangeGenerator('2023-04-01', '2023-04-04', 1);
26console.log(dateGenerator.next().value); // '2023-04-01'
27console.log(dateGenerator.next().value); // '2023-04-02'
28console.log(dateGenerator.next().value); // '2023-04-03'
29console.log(dateGenerator.next().value); // '2023-04-04'
30console.log(dateGenerator.next().done); // true (indicates that the generator is finished)
31*/
32
Time and Space Complexity
The time complexity of the dateRangeGenerator
function is O(N) where N is the number of dates generated between the start and end dates with the specified step. This is because the function generates each date in the range one by one in a loop, and the number of iterations of the loop equals the number of dates.
The space complexity of the function is O(1) since it yields the dates one by one and does not store all the dates in memory. The storage used does not grow with the size of the date range; the function only keeps track of the current date and the parameters provided.
How would you design a stack which has a function min
that returns the minimum element in the stack, in addition to push
and pop
? All push
, pop
, min
should have running time O(1)
.
Recommended Readings
LeetCode Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way we
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https algomonster s3 us east 2 amazonaws com recursion jpg You first
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!