How to get month and its days in moment? - javascript

I'm going to create a calendar using moment.js, now I want to know how to get months and days for each month? I found this code but I didn't understand what it does:
function generateMonth() {
var weeks = [];
var done = false, date = moment().clone(), monthIndex = moment().month(), count = 0;
while (!done) {
weeks.push({ days: generateDays(moment().clone(), moment()) });
date.add(1, "w");
done = count++ > 2 && monthIndex !== date.month();
monthIndex = date.month();
}
}
function generateDays(date, month) {
var days = [];
for (var i = 0; i < 7; i++) {
days.push({
number: date.date(),
isCurrentMonth: date.month() === month.month(),
isToday: date.isSame(new Date(), "day")
});
date = date.clone();
date.add(1, "d");
}
return days;
}

Related

Get week range for given month and year in JavaScript

I need to display a range of dates in corresponding week for selected month. Suppose the selected values are month=3 (April) and year=2018. The output should show a list of ranged dates for weeks in that particular month selected.
month = 3;
year = 2018;
Output:
Week 1: 01/04/2018 - 01/04/2018
Week 2: 02/04/2018 - 08/04/2018
Week 3: 09/04/2018 - 15/04/2018
Week 4: 16/04/2018 - 22/04/2018
Week 5: 23/04/2018 - 29/04/2018
Week 6: 30/04/2018 - 30/04/2018
I tried this function but it doesn't work correctly. Can you help me ? thanks a lot.
public getWeeksInMonth(): {
let year: number = 2018;
let month: number = 3 //April;
const weeks = [];
const firstDay: Date = new Date(year, month, 1);
const lastDay: Date = new Date(year, month + 1, 0);
const daysInMonth: number = lastDay.getDate();
let dayOfWeek: number = firstDay.getDay();
let start: number;
let end: number;
for (let i = 1; i < daysInMonth + 1; i++) {
if (dayOfWeek === 0 || i === 1) {
start = i;
}
if (dayOfWeek === 6 || i === daysInMonth) {
end = i;
if ((start !== end) || (start === 30 && end === 30) || (start === 31 && end === 31)) {
weeks.push({
'start': start,
'end': end
});
}
}
dayOfWeek = new Date(year, month, i).getDay();
}
return weeks;
}
The first problem is that sunday gets 0 by .getDay() instead of monday.
Solved this issue checking for dayOfWeek === 1 instead of dayOfWeek === 0 and dayOfWeek === 0 instead of dayOfWeek === 6.
The second problem is your checking befor pushing a new week. You're checking for if start !== end but start could be equal to end
Solved this issue by setting start to null after pushing a new week and checking for start instead.
(Example below is written in JS, because TS is not runable at StackOverflow)
function getWeeksInMonth() {
let year = 2018;
let month = 3; //April;
const weeks = [];
const firstDay = new Date(year, month, 1);
const lastDay = new Date(year, month + 1, 0);
const daysInMonth = lastDay.getDate();
let dayOfWeek = firstDay.getDay();
let start;
let oldstart;
let end;
for (let i = 1; i < daysInMonth + 1; i++) {
if (dayOfWeek === 1 || i === 1) {
start = i;
}
if (dayOfWeek === 0 || i === daysInMonth) {
end = i;
if(start){
weeks.push({
'start': start,
'end': end
});
start = null;
}
}
dayOfWeek = new Date(year, month, i + 1).getDay();
}
return weeks;
}
console.log(getWeeksInMonth());
I have slightly refactored your solution and it seems to deliver expected result.
My code follows exactly the same (brute force) approach as yours:
make up an array (with Array.from()) of length equal to the number of days in a given month and fill it with the dates of each day of that month;
cycle through that array (with Array.prototype.reduce()) and throw into resulting array objects ({week,start,end}) that correspond to weeks, generating new objects as you hit Monday and closing the end date as you hit Sunday:
const getWeeksInMonth = (month, year) =>
Array
.from({length: (new Date(year, month+1, 0) - new Date(year, month, 0))/864E5},
(_,i) => new Date(year, month, i+1))
.reduce((res,date,idx,self) => {
const itsMonday = date.getDay() == 1,
itsSunday = date.getDay() == 0,
monthStart = idx == 0,
monthEnd = idx == self.length-1,
options = {dateStyle: 'short'};
if(itsMonday || monthStart)
res.push({
week:(res[res.length-1]||{week:0}).week+1,
start: date.toLocaleDateString('fr',options),
end: (monthStart ? date.toLocaleDateString('fr',options) : '')
});
if(itsSunday || monthEnd)
res[res.length-1].end = date.toLocaleDateString('fr',options);
return res;
}, []);
console.log(getWeeksInMonth(3,2018));
.as-console-wrapper {min-height:100%}
I found the solution.
Here the complete funtion working correctly:
public getWeeksInMonth(): Observable<Week[]> {
let year: number = 2018;
let month: number = 3;
const weeks: Week[] = [];
const firstDay: Date = new Date(Date.UTC(year, month, 1));
const lastDay: Date = new Date(Date.UTC(year, month + 1, 0));
const daysInMonth: number = lastDay.getDate();
let dayOfWeek: number = firstDay.getDay();
let start: number;
let end: number;
for (let i = 1; i < daysInMonth + 1; i++) {
if (dayOfWeek === 0 || i === 1) {
start = i;
}
if (dayOfWeek === 0 && start === 1 && i !== daysInMonth) {
end = 1;
weeks.push({
'start': start,
'end': end
});
}
if (dayOfWeek === 6 || i === daysInMonth) {
end = i;
if ((start !== end) || (start === 30 && end === 30) || (start === 31 && end === 31)) {
weeks.push({
'start': start,
'end': end
});
}
}
dayOfWeek = new Date(year, month, i).getDay();
}
return of(weeks);
}

How to calculate duration day when in the middle date there is a holiday date

I'm trying to calculate duration when in the middle of date there is a holiday. I have tried when i choose the holiday date as the end date, the duration was 0. But if i choose next day the duration will calculate as usual without without a reduction in holidays. Whats must I add or change in my code
function getBusinessDatesCount(startDate, endDate) {
var count = 0;
var curDate = new Date(startDate);
var holiday = [];
holiday[0] = new Date('2019-08-08');
holiday[1] = new Date('2019-08-14');
while (curDate <= endDate) {
var dayOfWeek = curDate.getDay();
var isWeekend = (dayOfWeek == 6) || (dayOfWeek == 0);
if (!isWeekend && !isHoliday(endDate, holiday))
count++;
curDate.setDate(curDate.getDate() + 1);
}
function isHoliday(dt, arr) {
var bln = false;
for (var i = 0; i < arr.length; i++) {
if (compare(dt, arr[i])) { //If days are holidays
bln = true;
break;
}
}
return bln;
}
function compare(dt1, dt2) {
var equal = false;
if (dt1.getDate() == dt2.getDate() && dt1.getMonth() == dt2.getMonth() && dt1.getFullYear() == dt2.getFullYear()) {
equal = true;
}
return equal;
}
return count;
}
I expect when the date through the holiday, duration will not calculate the holiday date

How can I use moment.js to add days, excluding weekends?

I'm setting a default follow-up date two days from current date, which currently works:
const Notify = moment().add(2, 'days').toDate();
However, I would like to exclude weekends. So I installed moment WeekDay, but I can't seem to get it to work with adding days to the current date. The documentation calls for:
moment().weekday(0)
But I can't get that to work with adding in two days forward. Any ideas?
This solution is simple, easy to follow, and works well for me:
function addBusinessDays(originalDate, numDaysToAdd) {
const Sunday = 0;
const Saturday = 6;
let daysRemaining = numDaysToAdd;
const newDate = originalDate.clone();
while (daysRemaining > 0) {
newDate.add(1, 'days');
if (newDate.day() !== Sunday && newDate.day() !== Saturday) {
daysRemaining--;
}
}
return newDate;
}
Try: moment-business-days
It should help you.
Example:
var momentBusinessDays = require("moment-business-days")
momentBusinessDays('20-09-2018', 'DD-MM-YYYY').businessAdd(3)._d
Result:
Tue Sep 25 2018 00:00:00 GMT+0530 (IST)
You could also not use external lib and do a simple function like one of these two:
const WEEKEND = [moment().day("Saturday").weekday(), moment().day("Sunday").weekday()]
const addBusinessDays1 = (date, daysToAdd) => {
var daysAdded = 0,
momentDate = moment(new Date(date));
while (daysAdded < daysToAdd) {
momentDate = momentDate.add(1, 'days');
if (!WEEKEND.includes(momentDate.weekday())) {
daysAdded++
}
}
return momentDate;
}
console.log(addBusinessDays1(new Date(), 7).format('MM/DD/YYYY'))
console.log(addBusinessDays1('09-20-2018', 3).format('MM/DD/YYYY'))
// This is the somewhat faster version
const addBusinessDays2 = (date, days) => {
var d = moment(new Date(date)).add(Math.floor(days / 5) * 7, 'd');
var remaining = days % 5;
while (remaining) {
d.add(1, 'd');
if (d.day() !== 0 && d.day() !== 6)
remaining--;
}
return d;
};
console.log(addBusinessDays2(new Date(), 7).format('MM/DD/YYYY'))
console.log(addBusinessDays2('09-20-2018', 3).format('MM/DD/YYYY'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js"></script>
They are slightly modified from this post and I think are a good alternative to external library you have to carry/deal with (assuming this is the only part you need and not other features of that lib).
This will do it based on any starting date, and without a costly loop. You calculate the number of weekend days you need to skip over, then just offset by the number of weekdays and weekends, together.
function addWeekdays(year, month, day, numberOfWeekdays) {
var originalDate = year + '-' + month + '-' + day;
var futureDate = moment(originalDate);
var currentDayOfWeek = futureDate.day(); // 0 = Sunday, 1 = Monday, ..., 6 = Saturday
var numberOfWeekends = Math.floor((currentDayOfWeek + numberOfWeekdays - 1) / 5); // calculate the number of weekends to skip over
futureDate.add(numberOfWeekdays + numberOfWeekends * 2, 'days'); // account for the 2 days per weekend
return futureDate;
}
const addWorkingDays = (date: Moment, days: number) => {
let newDate = date.clone();
for (let i = 0; i < days; i++) {
if (newDate.isoWeekday() !== 6 && newDate.isoWeekday() !== 7) {
newDate = newDate.add(1, "days");
} else {
newDate = newDate.add(1, "days");
i--;
}
}
return newDate.format("YYYY/MM/DD");
};
var moment = require("moment")
function addWorkingDay(date, days){
let daysToAdd = days
const today = moment(date);
const nextWeekStart = today.clone().add(1, 'week').weekday(1);
const weekEnd = today.clone().weekday(5);
const daysTillWeekEnd = Math.max(0, weekEnd.diff(today, 'days'));
if(daysTillWeekEnd >= daysToAdd) return today.clone().add(daysToAdd, 'days');
daysToAdd = daysToAdd - daysTillWeekEnd - 1;
return nextWeekStart.add(Math.floor(daysToAdd/5), 'week').add(daysToAdd % 5, 'days')
}
I think this code will be faster:
var businessDays = 10;
var days = businessDays + Math.floor((Math.min(moment().day(),5)+businessDays)/6)*2;
moment.add(days, 'days');
// using pure JS
function addBusinessDays(originalDate, numDaysToAdd) {
const Sunday = 0;
const Saturday = 6;
let daysRemaining = numDaysToAdd;
const newDate = originalDate;
while (daysRemaining > 0) {
newDate.setDate(newDate.getDate() + 1);
if (newDate.getDay() !== 0 && newDate.getDay() !== 6) {
// skip sunday & saturday
daysRemaining--;
}
}
return newDate;
}
var dt = new Date(); // get date
var business_days = 8;
newDate = addBusinessDays(dt, business_days);
console.log(newDate.toString());

JavaScript: get all months between two dates?

I have two date strings like this:
var startDate = '2012-04-01';
var endDate = '2014-11-01';
And I want to end up with an array of strings like this:
var dates = ['2012-04-01', '2012-05-01', '2012-06-01' .... '2014-11-01',];
So far this is what I've got, but it's pretty ugly:
var startDate = '2012-04-01';
var endDate = '2014-11-01';
var start = new Date(Date.parse(startDate));
var end = new Date(Date.parse(endDate))
var dates = [];
for (var i = start.getFullYear(); i < end.getFullYear() + 1; i++) {
dates.push(i + '-' + '-01');
}
console.log(dates);
Is there a better way? JSFiddle.
This should produce the desired output:
function dateRange(startDate, endDate) {
var start = startDate.split('-');
var end = endDate.split('-');
var startYear = parseInt(start[0]);
var endYear = parseInt(end[0]);
var dates = [];
for(var i = startYear; i <= endYear; i++) {
var endMonth = i != endYear ? 11 : parseInt(end[1]) - 1;
var startMon = i === startYear ? parseInt(start[1])-1 : 0;
for(var j = startMon; j <= endMonth; j = j > 12 ? j % 12 || 11 : j+1) {
var month = j+1;
var displayMonth = month < 10 ? '0'+month : month;
dates.push([i, displayMonth, '01'].join('-'));
}
}
return dates;
}
Just call it with your existing date format:
dateRange('2013-11-01', '2014-06-01')
// ["2013-11-01", "2013-12-01", "2014-01-01", "2014-02-01", "2014-03-01", "2014-04-01", "2014-05-01", "2014-06-01", "2014-07-01", "2014-08-01", "2014-09-01", "2014-10-01", "2014-11-01", "2014-12-01"]
You can also use the excellent moment.js library:
var startDate = moment('2012-04-01');
var endDate = moment('2014-11-01');
var result = [];
if (endDate.isBefore(startDate)) {
throw "End date must be greated than start date."
}
while (startDate.isBefore(endDate)) {
result.push(startDate.format("YYYY-MM-01"));
startDate.add(1, 'month');
}
JSFiddle
If loading an extra library isn't a problem, you could always try the awesome MomentJS.
Gives for very clean and powerful date manipulation.
var startDate = moment('2012-04-01');
var endDate = moment('2014-11-01');
var dates = [];
endDate.subtract(1, "month"); //Substract one month to exclude endDate itself
var month = moment(startDate); //clone the startDate
while( month < endDate ) {
month.add(1, "month");
dates.push(month.format('YYYY-MM-DD'));
}
console.log(dates);
JSFiddle here
const getMonths = (fromDate, toDate) => {
const fromYear = fromDate.getFullYear();
const fromMonth = fromDate.getMonth();
const toYear = toDate.getFullYear();
const toMonth = toDate.getMonth();
const months = [];
for(let year = fromYear; year <= toYear; year++) {
let monthNum = year === fromYear ? fromMonth : 0;
const monthLimit = year === toYear ? toMonth : 11;
for(; monthNum <= monthLimit; monthNum++) {
let month = monthNum + 1;
months.push({ year, month });
}
}
return months;
}
const sample = getMonths(new Date('2022-07-28'), new Date('2023-03-20'));
console.log(sample);
document.write('check the console output');
https://jsfiddle.net/xfayoqvs/
You are handling "logical" jumps, so you doesn't actually need timing arthmetics. So this is a simple counting problem:
var startDate = '2012-04-01';
var endDate = '2014-11-01';
var dates = [];
var d0 = startDate.split('-');
var d1 = endDate.split('-');
for (
var y = d0[0];
y <= d1[0];
y++
) {
for (
var m = d0[1];
m <= 12;
m++
) {
dates.push(y+"-"+m+"-1");
if (y >= d1[0] && m >= d1[1]) break;
};
d0[1] = 1;
};
console.log(dates);
Here is a solution which just uses string manipulation on that specific YYYY-MM-DD format:
function monthsBetween(...args) {
let [a, b] = args.map(arg => arg.split("-").slice(0, 2)
.reduce((y, m) => m - 1 + y * 12));
return Array.from({length: b - a + 1}, _ => a++)
.map(m => ~~(m / 12) + "-" + ("0" + (m % 12 + 1)).slice(-2) + "-01");
}
console.log(monthsBetween('2012-04-01', '2014-11-01'));
Here is another solution, using Date objects:
const enumerateMonths = (from, to) => {
const current = new Date(from)
current.setUTCDate(1)
current.setUTCHours(0, 0, 0, 0)
const toDate = new Date(to)
const months = []
while (current.getTime() <= toDate.getTime()) {
months.push(current.getUTCFullYear() + "-" + `${current.getUTCMonth() + 1}`.padStart(2, "0"))
current.setUTCMonth(current.getUTCMonth() + 1)
}
return months
}
This solution presumes you provide Date objects or ISO 8601 strings. Please mind that an ISO 8601 date does not necessarily have to contain the hours-minutes-seconds part. "2012-01-14" is a valid ISO 8601 date.
An example to get all first days of months between a given date and now using moment.js.
var getMonths = function (startDate) {
var dates = [];
for (var year = startDate.year(); year <= moment().year(); year++) {
var endMonth = year != moment().year() ? 11 : moment().month();
var startMonth = year === startDate.year() ? startDate.month() : 0;
for (var currentMonth = startMonth; currentMonth <= endMonth; currentMonth = currentMonth > 12 ? currentMonth % 12 || 11 : currentMonth + 1) {
var month = currentMonth + 1;
var displayMonth = month < 10 ? '0' + month : month;
dates.push([year, displayMonth, '01'].join('-'));
}
}
return dates;
};
All solutions above run in O(n^2) time complexity, which is not very efficient.
See below solution in O(n) time complexity:
function getAllMonths(start, end){
let startDate = new Date(start);
let startYear = startDate.getFullYear();
let startMonth = startDate.getMonth()+1;
let endDate = new Date(end);
let endYear = endDate.getFullYear();
let endMonth = endDate.getMonth()+1;
let countMonth = 0;
let countYear = 0;
let finalResult = [];
for(let a=startYear; a<=endYear; a++){
if(startYear<endYear){
if(countYear==0){
countMonth += 12-startMonth;
}else
if(countYear>0){
countMonth += 12;
}
countYear+=1;
startYear++;
}else
if(startYear==endYear){
countMonth+=endMonth;
}
}
for(let i=startMonth; i<=countMonth+startMonth; i++){
finalResult.push(startDate.getFullYear()+(Math.floor(i/12)) + "-" + Math.round(i%13) + "-" + "01");
}
return finalResult;
}
getAllMonths('2016-04-01', '2018-01-01');
Might share a much more simpler code
Still not a very elegant answer, but arrives at the array of strings you want:
var startDate = '2012-04-01';
var endDate = '2014-11-01';
var start = new Date(startDate);
var end = new Date(endDate);
var dates = [];
for (var i = start.getFullYear(); i < end.getFullYear() + 1; i++) {
for (var j = 1; j <= 12; j++) {
if (i === end.getFullYear() && j === end.getMonth() + 3) {
break;
}
else if (i === 2012 && j < 4){
continue;
}
else if (j < 10) {
var dateString = [i, '-', '0' + j, '-','01'].join('');
dates.push(dateString)
}
else {
var dateString = [i, '-', j, '-','01'].join('');
dates.push(dateString);
}
}
}
console.log(dates);
jsfiddle link here: http://jsfiddle.net/8kut035a/
This is my solution, with help of math and O(n)
determineMonthInInterval(startDate, endDate) {
let startYear = startDate.getFullYear();
let endYear = endDate.getFullYear();
let startMonth = startDate.getMonth() + 1;
let endMonth = endDate.getMonth() + 1;
let monthAmount = (endMonth - startMonth) + 1 + (12 * (endYear - startYear));
let dates = [];
let currMonth = startMonth;
let currYear = startYear;
for( let i=0; i<monthAmount; i++){
let date = new Date(currYear + "/"+currMonth+"/1");
dates.push(date);
currYear = startYear + Math.floor((startMonth+i) / 12);
currMonth = (currMonth) % 12 +1;
}
return dates;
}
Here is another option:
getRangeOfMonths(startDate: Date, endDate: Date) {
const dates = new Array<string>();
const dateCounter = new Date(startDate);
// avoids edge case where last month is skipped
dateCounter.setDate(1);
while (dateCounter < endDate) {
dates.push(`${dateCounter.getFullYear()}-${dateCounter.getMonth() + 1}`);
dateCounter.setMonth(dateCounter.getMonth() + 1);
}
return dates;
}

Get a list of dates between two dates using javascript

From JavaScript is there a way to get list of days between two dates from MySQL format. I don't want to use any library for this.
This is what i did.
function generateDateList(from, to) {
var getDate = function(date) { //Mysql Format
var m = date.getMonth(), d = date.getDate();
return date.getFullYear() + '-' + (m < 10 ? '0' + m : m) + '-' + (d < 10 ? '0' + d : d);
}
var fs = from.split('-'), startDate = new Date(fs[0], fs[1], fs[2]), result = [getDate(startDate)], start = startDate.getTime(), ts, end;
if ( typeof to == 'undefined') {
end = new Date().getTime();
} else {
ts = to.split('-');
end = new Date(ts[0], ts[1], ts[2]).getTime();
}
while (start < end) {
start += 86400000;
startDate.setTime(start);
result.push(getDate(startDate));
}
return result;
}
console.log(generateDateList('2014-2-27', '2014-3-2'));
I test it from chrome and nodejs below are the result.
[ '2014-02-27',
'2014-02-28',
'2014-02-29',
'2014-02-30',
'2014-02-31',
'2014-03-01',
'2014-03-02' ]
yeh big leap year:-D..., how can i fix this? or is there any better way.?
const listDate = [];
const startDate ='2017-02-01';
const endDate = '2017-02-10';
const dateMove = new Date(startDate);
let strDate = startDate;
while (strDate < endDate) {
strDate = dateMove.toISOString().slice(0, 10);
listDate.push(strDate);
dateMove.setDate(dateMove.getDate() + 1);
};
Take the start date and increment it by one day until you reach the end date.
Note: MySQL dates are standard format, no need to parse it by hand just pass it to the Date constructor: new Date('2008-06-13').
const addDays = (date, days = 1) => {
const result = new Date(date);
result.setDate(result.getDate() + days);
return result;
};
const dateRange = (start, end, range = []) => {
if (start > end) return range;
const next = addDays(start, 1);
return dateRange(next, end, [...range, start]);
};
const range = dateRange(new Date("2014-02-27"), new Date("2014-03-02"));
console.log(range);
console.log(range.map(date => date.toISOString().slice(0, 10)))
Here I use a recursive function, but you could achieve the same thing using a while (see other answers).
I have used this one from
https://flaviocopes.com/how-to-get-days-between-dates-javascript/
const getDatesBetweenDates = (startDate, endDate) => {
let dates = []
//to avoid modifying the original date
const theDate = new Date(startDate)
while (theDate < new Date(endDate)) {
dates = [...dates, new Date(theDate)]
theDate.setDate(theDate.getDate() + 1)
}
dates = [...dates, new Date(endDate)]
return dates
}
Invoke the function as follows:
getDatesBetweenDates("2021-12-28", "2021-03-01")
Note - I just had to fix issues with the Date object creation (new Date()) in the while loop and in the dates array. Other than that the code is pretty much same as seen on the above link
dateRange(startDate, endDate) {
var start = startDate.split('-');
var end = endDate.split('-');
var startYear = parseInt(start[0]);
var endYear = parseInt(end[0]);
var dates = [];
for(var i = startYear; i <= endYear; i++) {
var endMonth = i != endYear ? 11 : parseInt(end[1]) - 1;
var startMon = i === startYear ? parseInt(start[1])-1 : 0;
for(var j = startMon; j <= endMonth; j = j > 12 ? j % 12 || 11 : j+1) {
var month = j+1;
var displayMonth = month < 10 ? '0'+month : month;
dates.push([i, displayMonth, '01'].join('-'));
}
}
return dates;
}
var oDate1 = oEvent.getParameter("from"),
oDate2 = oEvent.getParameter("to");
var aDates = [];
var currentDate = oDate1;
while (currentDate <= oDate2) {
aDates.push(new Date(currentDate));
currentDate.setDate(currentDate.getDate() + 1);
}
I expanded Công Thắng's great answer to return {years, months, days}, thought it was worth sharing:
function getDates(startDate, endDate) {
const days = [],
months = new Set(),
years = new Set()
const dateMove = new Date(startDate)
let date = startDate
while (date < endDate){
date = dateMove.toISOString().slice(0,10)
months.add(date.slice(0, 7))
years.add(date.slice(0, 4))
days.push(date)
dateMove.setDate(dateMove.getDate()+1) // increment day
}
return {years: [...years], months: [...months], days} // return arrays
}
console.log(getDates('2016-02-28', '2016-03-01')) // leap year
/* =>
{
years: [ '2016' ],
months: [ '2016-02', '2016-03' ],
days: [ '2016-02-28', '2016-02-29', '2016-03-01' ]
}
*/
const {months} = getDates('2016-02-28', '2016-03-01') // get only months
Basically the function just increments the built-in Date object by one day from start to end, while the Sets capture unique months and years.

Categories

Resources