Javascript calendar non-sequential months - javascript

I am new to JavaScript and created a calendar that displays all 12 months for a given year and want the ability to add random months for use in a query. I can do this part now however how do I easily check to see if the months are sequential keeping in mind they can be picked in any order. Example: JAN, FEB MAR or FEB, MAR, JAN app will display JAN 1 to MAR 30 but Jan & APR will display a message stating you selected non-sequential values.

A simple comparison would work since the date selections are already limited to a single year. In addition, you could easily presort the dates (as shown) to assist the user with the selection.
Example:
var month, dates = [new Date('2016-10-01'), new Date('2016-11-01'), new Date('2016-12-01')];
// optional date sort
// dates.sort(function(a,b){ return a < b ? -1 : a > b ? 1 : 0 });
month = dates[0].getMonth();
if (dates[1].getMonth() == (month + 1) && dates[2].getMonth() == (month + 2)) {
console.info('okay');
}
else {
console.info('fail');
}
console.info(JSON.stringify(dates, null, ' '));

Related

Is there a way to set date and month on a date object in a single statement?

I'm trying to calculate a date based on another date by adding a certain period of time. Let's say if I want to add 3 months to a date, then the new date should be one day before the date after 3 months. Taking that example, below is the code that I came closest to for achieving what I want to do:
new Date(
date
.setMonth(date.getMonth() + 3)
.setDate(date.getDate() - 1)
)
But this returns an error: TypeError: date.setMonth(...).setDate is not a function. I think the chaining of methods is not working, so I'll probably have to setDate in the next statement. Is there a way to do this in a single statement of code?
Is there a way to set date and month on a date object in a single statement?
Yes. The following sets the month to January and the day to the first on one statement:
let d = new Date();
d.setMonth(0, 1);
…if I want to add 3 months to a date, then the new date should be one day before the date after 3 months
No, you can't do that in one statement because adding a month needs to consider the number of days in the month. E.g. adding one month to 31 Jan 2020 results in
31 Feb, and since there were only 29 days in Feb 2020, the date is set to 2 Mar 2020. Similarly, adding 1 month to 31 May gives 31 Jun which rolls over to 1 Jul. So you need to add months first (see Adding months to a Date in JavaScript), then subtract one day (see Add days to JavaScript Date).
You could extend the Date Object and create your own methods which are chainable.
class MyDate extends Date {
constructor() {
super(...arguments)
}
changeMonth(amount) {
return new MyDate(this.setMonth(this.getMonth() + amount));
}
changeDate(amount) {
return new MyDate(this.setDate(this.getDate() + amount));
}
};
const date = new MyDate();
console.log("Original:", date);
console.log("Changed :", date.changeMonth(3).changeDate(-5));
You could so something like
newDate = new Date(newDate.getFullYear(), newDate.getMonth() + 3, endDate.getDate() -1)
Turns out there is a very simple solution. The setMonth() method allows us to pass an optional dayValue parameter (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/setMonth). Using which, the code simply becomes:
new Date(
date.setMonth(
date.getMonth() + 3,
date.getDate() - 1
)
)

Create custom year, different to calendar year? JS/QML

I am trying to change the 'first day' and 'last day' of a year (normally 01/01/yyyy - 31/12/yyyy), to my own defined dates, being:
First Day: 01/04/yyyy (1st April)
Last Day: 31/03/yyyy (31st March)
I have found a similar question/answer here:
Dynamically change the year when calculating date ranges using JavaScript
with the answer being:
var todayDate = new Date(); //18/05/19
var FYFirst = todayDate.getFullYear(); //2019
if (todayDate.getMonth() < 3) { // earlier than april
FYFirst -= 1;
}
var FYLast = FYFirst + 1;
But where my users are storing dates taken out of an allowance, I need this to extend forward/back at least 2 years, so the above code would be currYear (01/04/2019 - 31/03/2020).
There would also be (currently being, but needs to by dynamic):
prevPrevYear: 01/04/17 - 31/03/18
prevYear: 01/04/18 - 31/03/19
nextYear: 01/04/20 - 31/03/21
nextNextYear: 01/04/21 - 31/03/22
In which the date is taken from the allowance of currYear,nextYear of whatever year the saved date falls into.
In this example let's say a users yearly 'allowance', is 40, and each day counts as 1, so when saving a date this deducts 1 from the allowance. I have all the math working for this, but need some way for the app to know if its deducting from this years 'allowance' or next years ect, this is written and stored to a Database - all of the math for this is currently working but rolls indefinitely without any yearly end!
My question is
How would I extend the above, to take into consideration the multiple year span needed when my users are storing dates? I am building my app using Felgo, working with QML!
It seems your year range is 1 Apr to 31 Mar the following year. You might call 1 Apr 2019 to 31 Mar 2020 "2019". You can create a simple function so that any date from 1 Jan to 31 Mar belongs to the previous year, e.g.:
function getMySpecialYear(date) {
var yr = date.getFullYear();
var fyStart = new Date(yr, 3, 1);
return date < fyStart? yr - 1 : yr;
}
[new Date(2019,0,1), // 1 Jan 2019 -> 2018
new Date(2019,5,1) // 1 Jun 2019 -> 2019
].forEach(date => console.log(date + ' - ' + getMySpecialYear(date)));
You can then sum the days for a particular range/year and see if the total is less than or greater than the allowance for that range/year.
Some places have a similar issue in that the financial year runs from 1 July to 30 June, they are often called things like "FY 2019/20".

Moment.js : Get first day of first week by given year

Im trying to get the first day of first week by given day
Correct results :
2017 = 2 Jan (Monday)
2016 = 4 Jan (Monday)
2015 = 1 Jan (Thursday)
2014 = 1 Jan (Wednesday)
2013 = 1 Jan (Tuesday)
I can get the first day of the year by :
moment().year(year).startOf('year') // All result is 1. jan XXXX
Then I tried : (correct)
moment().day(1).year(2017).week(1) // 2 Jan (Monday) (correct)
But when changed to 2016 : (wrong)
moment().day(1).year(2016).week(1) // 10 Jan (Sunday) (wrong)
Any know how to get correct results ? (Im open for pure JS date() also)
PS: Week number is based in Norway
Playground : https://jsfiddle.net/6sar7eb4/
There is no need to use loop, you can simply test if the week number is not 1 using week() that gives localized output
The output of moment#week will depend on the locale for that moment.
You have to set the locale to moment using locale() method to get start of week locale aware. If you have to show result in english you can change locale locally.
Here a working sample:
function getFirstDayOfFirstWeekByYear( year ) {
// Create moment object for the first day of the given year
let func = moment({year:year})
// Check if 1st of January is in the first week of the year
if( func.week() !== 1 ){
// If not, add a week to the first day of the current week
func.startOf('week').add(1, 'week');
}
// Return result using english locale
return func.locale('en').format('D. MMMM YYYY (dddd)')
}
// Set Norwegian Bokmål locale globally
moment.locale('nb');
// Tester
[2017, 2016, 2015, 2014, 2013].forEach(function ( year ) {
let s = '<li>' + getFirstDayOfFirstWeekByYear( year ) + '</li>'
document.getElementsByTagName('BODY')[0].innerHTML += s
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment-with-locales.min.js"></script>
Anyway if your happy to do it within a loop this works
function getFirstDayOfFirstWeekByYear( year ){
let D = 1;
x = 0;
while(x !== 1){
x = moment(`${year}-01-${D}`).isoWeek();
if(x === 1 ){
return moment(`${year}-01-${D}`).format('D. MMMM YYYY (dddd)')
}
D++;
};
}
results
2. January 2017 (Monday)
4. January 2016 (Monday)
1. January 2015 (Thursday)
1. January 2014 (Wednesday)
1. January 2013 (Tuesday)
Im trying to get the first day of first week by given day
Using moment.js to get the first day of the first week of the year based on the ISO 8601 scheme, you can just use the first day of the first week of the year since the ISO week starts on Monday:
// Return a moment.js object for the first weekday of the first week
// of the year using ISO 8601 scheme
function firstDay(year) {
return moment(year + '011', 'GGGGWWE');
}
// Examples
['2015', '2016', '2017', '2018', '2019', '2020']
.forEach(function(year) {
console.log('ISO First day of ' + year + ': ' +
firstDay(year).format('dddd D MMMM, YYYY'));
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>
However you seem to not want to use the ISO scheme, so if you just want to get the first day of the year that isn't a Saturday or Sunday (i.e. ignore the ISO 8601 scheme), you can do (without moment.js):
// Return Date for first day of year that is one of
// Monday to Friday
function firstDay(year) {
var d = new Date(year, 0, 1);
var day = d.getDay();
d.setDate(d.getDate() + (day%6? 0 : (++day%4)));
return d;
}
// Examples
['2015', '2016', '2017', '2018', '2019', '2020']
.forEach(function(year) {
console.log('First weekday of ' + year + ': ' + firstDay(year).toString());
});
Discussion
There are many different schemes for first day of the week and first day of the year. A commonly used scheme is ISO 8601, where the first day of the week is Monday and the first week of the year is the one in which the first Thursday appears (or the first that contains at least 4 days of the week, it's the same result).
According to ISO 8601 rules, the first day of the first week of 2019 is Monday 31 December, 2018.
If you decide to support different schemes (e.g. in Saudi Arabia the week starts on Saturday and ends on Friday, with work days Saturday to Wednesday and in the US a week is typically Sunday to Saturday with work days Monday to Friday), then you should clearly state which scheme you support and not simply assume a scheme based on the language the user has chosen. Schemes may also be based on other cultural factors such as religion, so are not necessarily region or language-based.
Which scheme should be used for an English person in Saudi Arabia? An Islamic person in Bhutan? In Saudi the official language for commerce is French, but most international discussion is in English, while the national language is Arabic. But they use the Hijri calendar, which does not align with the Georgian calendar commonly used elsewhere and is not defined by language but by religion.
There may also be differences even in the same place, with departments of the same government choosing different schemes so even locale (in the correct meaning of the term) is not a definitive indicator of which scheme to use.
If you decide to support different schemes, far better to adopt a standard scheme (like ISO 8601) and let the user change it to the one they prefer if they wish (and which the preferences in my computer and calendar application allow me to do).

Why this datetime funtion setMonth result is wrong? getMonth() + 1

I have this code:
var nextDate = new Date("2016 01 31");
nextDate.setMonth(nextDate.getMonth() + 1);
I'm expecting the result to be Feb 28 2016, but it shows Mar 02 2016 instead.
Why? Is there any solution for it?
There is only 29 day in February, therefore, February 31 February will translate to Mars 2.
You need to update the days in your date object to the last day of that month. You can get the last day of the month by specifying a function that sets the date to 0:
function daysInMonth(month,year) {
return new Date(year, month, 0).getDate();
}
This is because February has 29 days, and when you set new month from January, which has 31 day, to February then the difference of the days are transferred to another month.
Easy way to do it is just create new Date instance.
You might need to implement some logic to get corresponding dates right
Possible work around with a helper function: after setMonth, check if the results doesn't contain a month equal to the expected month and if so, use setDate(0), which sets the date to last day of the previous month. e.g.
Date.prototype.addMonths = function(months){
var m = this.getMonth() + (months || 1);
this.setMonth(m);
if(this.getMonth() !== (m % 11)) //11: month is 0 based
this.setDate(0);
}
var nextDate = new Date("2016 01 31");
nextDate.addMonths(1);
document.writeln(nextDate);
months || 1 only is meant to have a default value if no month was submitted. m % 11 is needed in case of year transitions. 11 and not 12 because javascripts month (and thus getMonth) is 0 based.

Get date by day of week based from that date

Problem
I'm using AngularJS and in my view I have 7 days in a row like the pic below. The red, grey, and blue are based on a date (9/1/2013 Sunday). When I click Friday or Monday I want that date to be returned so I can reload the 0/3 with the stats for that date.
I don't need anything fancy for AngularJS I can't figure out the logic to take a base date and then switch the day out for the day that was clicked.
How do I get this to return a date?
Current base date: 9/1/2013 - Sunday
I click: Thursday
I receive: 8/29/2013 - Thursday
I click: Sunday
I receive: 9/1/2013
What it looks like
I'm currently trying to convert this function from:
JavaScript - get the first day of the week from current date
function getMonday(d) {
d = new Date(d);
var day = d.getDay(),
diff = d.getDate() - day + (day == 0 ? -6:1); // adjust when day is sunday
return new Date(d.setDate(diff));
}
getMonday(new Date()); // Mon Nov 08 2010
Solved!
I render the dates server side when I render my stats.
Using AngularJS:
My directives: http://paste.laravel.com/Nz9
My HTML template: http://paste.laravel.com/Nza
My PHP: http://paste.laravel.com/Nzc
Forget about what it looks like, let's focus on what data you have.
If I'm understanding you correctly, you have an associative array of something like:
[{'M',0},{'T',1},{'W',2},{'T',3},{'F',4},{'S',5},{'S',6}]
And you also have a base date
var base = moment('2013-09-01');
And the base is associated with the last value - the 6.
So then what you could do is something like this:
var x = 3; // I clicked on Thursday and got a 3
var target = base.subtract('days', 6-x); // move back 6-x days
That would work, but wouldn't it be much easier just to precalculate your associative array in the first place?
[{'M','2013-08-26'},
{'T','2013-08-27'},
{'W','2013-08-28'},
{'T','2013-08-29'},
{'F','2013-08-30'},
{'S','2013-08-31'},
{'S','2013-09-01'}]
Then you would already know what value to use when it was clicked.
The problem with moment's day() is that Sunday == 0, not Monday, so you have to jump one week back and use the range 1..7 for Monday..Sunday:
base = '9/1/2013'
console.log(moment(base).day(-7).day(4))
> Thu Aug 29 2013 00:00:00 GMT+0100
console.log(moment(base).day(-7).day(7))
> Sun Sep 01 2013 00:00:00 GMT+0100

Categories