patterncsharpMinor
Method to return date ranges of 1 year
Viewed 0 times
yearmethodreturndateranges
Problem
I made a method that takes 2
The routine should return a list of
Can this method be made better or even recursive?
DateTimes as a parameter, a startDate and a stopDate.The routine should return a list of
DateTime ranges by checking if the range in the parameters is above one year TimeSpan. If it is, it should separate the range into smaller ranges of one year, starting form startDate and adding a year until reaching the limit, and then adjusting the last range accordingly (as it might only contain a few months).Can this method be made better or even recursive?
// startDate = 2016-01-01
// endDate = 2020-06-01
List> list = GetYearsBetweenDates(startDate, endDate);
//list = 2016-01-01 2016-12-31
// 2017-01-01 2017-12-31
// 2018-01-01 2018-12-31
// 2019-01-01 2019-12-31
// 2020-01-01 2020-06-01
private List> GetYearsBetweenDates(DateTime startDate, DateTime stopDate)
{
var list = new List>();
var oneYear = TimeSpan.FromDays(365);
var tempStopDate = startDate + oneYear;
var tempStartDate = startDate;
bool first = true;
if (stopDate - startDate (startDate, stopDate));
return list;
}
while (tempStopDate != stopDate)
{
list.Add(new Tuple(tempStartDate, tempStopDate));
if (first)
{
tempStartDate += oneYear.Add(TimeSpan.FromDays(1)); // We should not have the same date twice
first = false;
}
else
tempStartDate += oneYear;
tempStopDate += oneYear;
if (tempStopDate > stopDate)
{
tempStopDate = stopDate;
list.Add(new Tuple(tempStartDate, stopDate));
}
}
return list;
}Solution
If you perform all calcuclations dynamically your method becomes a simple few lines long loop:
So what I have I changed?
The method returns an
The
To get all dates you call it with
or you put it in a
You can also exagerate and make it pure LINQ method without any manual loop but personaly I think the
What else?
You should always choose meaningful names for all variables. You did it well for the others. This could have been
It's safer to use the
private IEnumerable GetYearsBetweenDates(DateTime startDate, DateTime stopDate)
{
for (int i = startDate.Year; i <= stopDate.Year; i++)
{
yield return new DateTimeRange
(
start: new DateTime(i, startDate.Month, startDate.Day),
end:
i == stopDate.Year
? new DateTime(i, stopDate.Month, stopDate.Day).AddYears(1)
: new DateTime(i, startDate.Month, startDate.Day).AddYears(1)
);
}
}So what I have I changed?
The method returns an
IEnumerable and uses the yield return which makes it deferred (this means it is executed only if enumerated).The
T is a new struct that is easier to understand than a Tuple:struct DateTimeRange
{
public DateTimeRange(DateTime start, DateTime end)
{
Start = start;
End = end;
}
public DateTime Start { get; }
public DateTime End { get; }
}To get all dates you call it with
ToList or ToArrayvar dates = GetYearsBetweenDates(startDate, endDate).ToList();or you put it in a
foreach loopforeach(var range in GetYearsBetweenDates(startDate, endDate))
{
// do something
}You can also exagerate and make it pure LINQ method without any manual loop but personaly I think the
for loop looks better:private IEnumerable GetYearsBetweenDates3(DateTime startDate, DateTime stopDate)
{
return Enumerable.Range(startDate.Year, stopDate.Year - startDate.Year + 1)
.Select(x => new DateTimeRange
(
start: new DateTime(x, startDate.Month, startDate.Day),
end:
x == stopDate.Year
? new DateTime(x, stopDate.Month, stopDate.Day).AddYears(1)
: new DateTime(x, startDate.Month, startDate.Day).AddYears(1)
));
}What else?
var list = new List>();You should always choose meaningful names for all variables. You did it well for the others. This could have been
results or dates.var oneYear = TimeSpan.FromDays(365);It's safer to use the
AddYear(1) method rather then hardcoding the number. Especially if you don't want to care about leapyears.Code Snippets
private IEnumerable<DateTimeRange> GetYearsBetweenDates(DateTime startDate, DateTime stopDate)
{
for (int i = startDate.Year; i <= stopDate.Year; i++)
{
yield return new DateTimeRange
(
start: new DateTime(i, startDate.Month, startDate.Day),
end:
i == stopDate.Year
? new DateTime(i, stopDate.Month, stopDate.Day).AddYears(1)
: new DateTime(i, startDate.Month, startDate.Day).AddYears(1)
);
}
}struct DateTimeRange
{
public DateTimeRange(DateTime start, DateTime end)
{
Start = start;
End = end;
}
public DateTime Start { get; }
public DateTime End { get; }
}var dates = GetYearsBetweenDates(startDate, endDate).ToList();foreach(var range in GetYearsBetweenDates(startDate, endDate))
{
// do something
}private IEnumerable<DateTimeRange> GetYearsBetweenDates3(DateTime startDate, DateTime stopDate)
{
return Enumerable.Range(startDate.Year, stopDate.Year - startDate.Year + 1)
.Select(x => new DateTimeRange
(
start: new DateTime(x, startDate.Month, startDate.Day),
end:
x == stopDate.Year
? new DateTime(x, stopDate.Month, stopDate.Day).AddYears(1)
: new DateTime(x, startDate.Month, startDate.Day).AddYears(1)
));
}Context
StackExchange Code Review Q#146422, answer score: 4
Revisions (0)
No revisions yet.