How to List All Months Between Two Dates

How to list all months between two dates

PHP 5.3

$start    = new DateTime('2010-12-02');
$start->modify('first day of this month');
$end = new DateTime('2012-05-06');
$end->modify('first day of next month');
$interval = DateInterval::createFromDateString('1 month');
$period = new DatePeriod($start, $interval, $end);

foreach ($period as $dt) {
echo $dt->format("Y-m") . "<br>\n";
}

See it in action

PHP 5.4 or newer

$start    = (new DateTime('2010-12-02'))->modify('first day of this month');
$end = (new DateTime('2012-05-06'))->modify('first day of next month');
$interval = DateInterval::createFromDateString('1 month');
$period = new DatePeriod($start, $interval, $end);

foreach ($period as $dt) {
echo $dt->format("Y-m") . "<br>\n";
}

The part where we modify the start and end dates to the first of the month is important. If we didn't, and the current day higher then the last day in February (i.e. 28 in non-leap years, 29 in leap years) this would skip February.

Generate list of months between interval in python

>>> from datetime import datetime, timedelta
>>> from collections import OrderedDict
>>> dates = ["2014-10-10", "2016-01-07"]
>>> start, end = [datetime.strptime(_, "%Y-%m-%d") for _ in dates]
>>> OrderedDict(((start + timedelta(_)).strftime(r"%b-%y"), None) for _ in xrange((end - start).days)).keys()
['Oct-14', 'Nov-14', 'Dec-14', 'Jan-15', 'Feb-15', 'Mar-15', 'Apr-15', 'May-15', 'Jun-15', 'Jul-15', 'Aug-15', 'Sep-15', 'Oct-15', 'Nov-15', 'Dec-15', 'Jan-16']

Update: a bit of explanation, as requested in one comment. There are three problems here: parsing the dates into appropriate data structures (strptime); getting the date range given the two extremes and the step (one month); formatting the output dates (strftime). The datetime type overloads the subtraction operator, so that end - start makes sense. The result is a timedelta object that represents the difference between the two dates, and the .days attribute gets this difference expressed in days. There is no .months attribute, so we iterate one day at a time and convert the dates to the desired output format. This yields a lot of duplicates, which the OrderedDict removes while keeping the items in the right order.

Now this is simple and concise because it lets the datetime module do all the work, but it's also horribly inefficient. We're calling a lot of methods for each day while we only need to output months. If performance is not an issue, the above code will be just fine. Otherwise, we'll have to work a bit more. Let's compare the above implementation with a more efficient one:

from datetime import datetime, timedelta
from collections import OrderedDict

dates = ["2014-10-10", "2016-01-07"]

def monthlist_short(dates):
start, end = [datetime.strptime(_, "%Y-%m-%d") for _ in dates]
return OrderedDict(((start + timedelta(_)).strftime(r"%b-%y"), None) for _ in xrange((end - start).days)).keys()

def monthlist_fast(dates):
start, end = [datetime.strptime(_, "%Y-%m-%d") for _ in dates]
total_months = lambda dt: dt.month + 12 * dt.year
mlist = []
for tot_m in xrange(total_months(start)-1, total_months(end)):
y, m = divmod(tot_m, 12)
mlist.append(datetime(y, m+1, 1).strftime("%b-%y"))
return mlist

assert monthlist_fast(dates) == monthlist_short(dates)

if __name__ == "__main__":
from timeit import Timer
for func in "monthlist_short", "monthlist_fast":
print func, Timer("%s(dates)" % func, "from __main__ import dates, %s" % func).timeit(1000)

On my laptop, I get the following output:

monthlist_short 2.3209939003
monthlist_fast 0.0774540901184

The concise implementation is about 30 times slower, so I would not recommend it in time-critical applications :)

How can I get a collection of months between two Dates?

Based on your code you could substract the month difference from the "to" DateTime to get DateTime difference from your input.

public static List<DateTime> GetMonthsBetween(DateTime from, DateTime to)
{
if (from > to) return GetMonthsBetween(to, from);

var monthDiff = Math.Abs((to.Year * 12 + (to.Month - 1)) - (from.Year * 12 + (from.Month - 1)));

if (from.AddMonths(monthDiff) > to || to.Day < from.Day)
{
monthDiff -= 1;
}

List<DateTime> results = new List<DateTime>();
for (int i = monthDiff; i >= 1; i--)
{
results.Add(to.AddMonths(-i));
}

return results;
}

To get the name of the month just format the DateTime to "MMM".

var dts = GetMonthsBetween(DateTime.Today, DateTime.Today.AddMonths(5));
foreach (var dateTime in dts)
{
Console.WriteLine(dateTime.ToString("MMM"));
}

How to display all the months between given two dates?

You could use the dateutil extension's relativedelta method like below -

from datetime import datetime
from dateutil.relativedelta import relativedelta

startDate = '2016-1-28'
endDate = '2017-3-26'

cur_date = start = datetime.strptime(startDate, '%Y-%m-%d').date()
end = datetime.strptime(endDate, '%Y-%m-%d').date()

while cur_date < end:
print(cur_date)
cur_date += relativedelta(months=1)

Following is the output

2016-01-28
2016-02-28
2016-03-28
2016-04-28
2016-05-28
2016-06-28
2016-07-28
2016-08-28
2016-09-28
2016-10-28
2016-11-28
2016-12-28
2017-01-28
2017-02-28

How can I get a collection of months between two Dates?

Based on your code you could substract the month difference from the "to" DateTime to get DateTime difference from your input.

public static List<DateTime> GetMonthsBetween(DateTime from, DateTime to)
{
if (from > to) return GetMonthsBetween(to, from);

var monthDiff = Math.Abs((to.Year * 12 + (to.Month - 1)) - (from.Year * 12 + (from.Month - 1)));

if (from.AddMonths(monthDiff) > to || to.Day < from.Day)
{
monthDiff -= 1;
}

List<DateTime> results = new List<DateTime>();
for (int i = monthDiff; i >= 1; i--)
{
results.Add(to.AddMonths(-i));
}

return results;
}

To get the name of the month just format the DateTime to "MMM".

var dts = GetMonthsBetween(DateTime.Today, DateTime.Today.AddMonths(5));
foreach (var dateTime in dts)
{
Console.WriteLine(dateTime.ToString("MMM"));
}

How do I get all months between two dates?

Since you aren't interested in the days, you can scrap that part of the date and deal with only the months. Do this by adding in an arbitrary day of the month (I've used the first day below, but any day will do as long as they are the same).

start <- as.Date("2015-08-14")
end <- as.Date("2015-09-03")

forced_start <- as.Date(paste0(format(start, "%Y-%m"), "-01"))
forced_end <- as.Date(paste0(format(end, "%Y-%m"), "-01"))

seq_dates <- seq.Date(forced_start, forced_end, by = "month")
format(seq_dates, "%Y-%m")


Related Topics



Leave a reply



Submit