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

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).

Related

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".

Get previous business day with moment.js

What's the best way to get the previous business day's date with moment.js? A business day is Monday through Friday.
Some expectations:
If today is Satuday, Sunday or Monday, return last Friday's date
If today is Tuesday, return last Monday's date (yesterday)
function getPreviousWorkday(){
let workday = moment();
let day = workday.day();
let diff = 1; // returns yesterday
if (day == 0 || day == 1){ // is Sunday or Monday
diff = day + 2; // returns Friday
}
return workday.subtract(diff, 'days');
}
Updated Approach (without looping)
You could actually take advantage of the day() function that would allow you to set the current day of the week in moment.js to find the previous Friday based on certain days :
function getPreviousWorkday(){
// Based on the current day, handle accordingly
switch(moment().day())
{
// If it is Monday (1),Saturday(6), or Sunday (0), Get the previous Friday (5)
// and ensure we are on the previous week
case 0:
case 1:
case 6:
return moment().subtract(6,'days').day(5);
// If it any other weekend, just return the previous day
default:
return moment().day(today - 1);
}
}
which can be seen here and demonstrated below :
Looping Approach
You could simply subtract days from your current moment instance via the subtract() function from the current day until you reached a non-weekend day:
function getPreviousWorkday(){
// Get today
var today = new moment().subtract(-1,'days');;
// If today isn't a weekend, continue iterating back until you hit a non-weekend
while([0,6].indexOf(today.day()) !== -1){
today = today.subtract(1, 'days');
}
// Return the non-weekend day
return today;
}
You can see an example of this in action here and demonstrated below :
There is a npm module for that!
https://github.com/kalmecak/moment-business-days
From documentation:
prevBusinessDay() : Will retrieve the previous business date as moment date object:
//Previous busines day of Monday 02-02-2015
moment('02-02-2015', 'DD-MM-YYYY').prevBusinessDay()._d // Fri Jan 30 2015 00:00:00 GMT-0600 (CST)
//Previous busines day of Tuesday 03-02-2015
moment('03-02-2015', 'DD-MM-YYYY').prevBusinessDay()._d //Mon Feb 02 2015 00:00:00 GMT-0600 (CST)
P.S: Important node. Why to use dependency? If all you need is prev business day - no reason to use 3-d party lib. But for real application all that stuff will be useful for more complex operations like importing dates, formatting and calculations based on calendars, holidays, etc.
function getPreviousWorkday() {
return [1, 2, 3, 4, 5].indexOf(moment().subtract(1, 'day').day()) > -1 ?
moment().subtract(1, 'day') : moment(moment().day(-2));
}
If the previous day is a weekday, return the previous day / weekday. Otherwise return the previous Friday since, if the previous day is a Saturday or Sunday it should return the previous Friday.
If you need to check only for Saturday or Sunday, this snippet can be used. I'm getting last day of month first then checking for business day (so basically last business day of a month)
getLastDayofMonth() {
let date = moment(this.selectedDate).endOf("month").format("MM-DD-YYYY");
// console.log(moment(date).day());
// check if last day of selected month is Saturday or Sunday, assign last friday if true
if (moment(date).day() === 0 || moment(date).day() === 6)
this.selectedDate = moment(date)
.subtract(6, "days")
.day(5)
.format("MM-DD-YYYY");
else this.selectedDate = date;
},

Moment js getting next date given specified week day

I seem to have a bit of a problem getting the previous Monday given a particular date. I'm trying to use Moment js for the task. Obviously, I can do it by hand, but found it curious that I couldn't get it to work using the example in the moment.js documentation on their website: http://momentjs.com/docs/#/get-set/day/.
I was trying something like:
moment([2013, 08, 15, 15, 20]).day(-1).format('ddd, MMM DD')
which results in the 'two days ago' date, that being September 13 instead of the expected September 9th.
Does anybody have a clue here? Thanks.
Here is how it works:
moment().day(1) // this monday
moment().day(-6) // last monday, think of it as this monday - 7 days = 1 - 7 = -6
Same applies in other direction:
moment().day(8) // next monday, or this monday + 7 days = 1 + 7 = 8
Your code moment().day(-1) can be explained as this Sunday - 1 day = 0 - 1 = -1
or this Saturday - 7 days = 6 - 7 = -1
The accepted answer only works if you already know whether the day in question is in this week or next week. What if you don't know? You simply need the next available Thursday following some arbitrary date?
First, you want to know if the day in question is smaller or bigger than the day you want. If it's bigger, you want to use the next week. If it's smaller, you can use the same week's Monday or Thursday.
const dayINeed = 4; // for Thursday
if (moment().isoWeekday() <= dayINeed) {
return moment().isoWeekday(dayINeed);
} else...
If we're past the day we want already (if for instance, our Moment is a Friday, and we want the next available Thursday), then you want a solution that will give you "the Thursday of the week following our moment", regardless of what day our moment is, without any imperative adding/subtracting. In a nutshell, you want to first go into the next week, using moment().add(1, 'weeks'). Once you're in the following week, you can select any day of that week you want, using moment().day(1).
Together, this will give you the next available day that meets your requirements, regardless of where your initial moment sits in its week:
const dayINeed = 4; // for Thursday
// if we haven't yet passed the day of the week that I need:
if (moment().isoWeekday() <= dayINeed) {
// then just give me this week's instance of that day
return moment().isoWeekday(dayINeed);
} else {
// otherwise, give me next week's instance of that day
return moment().add(1, 'weeks').isoWeekday(dayINeed);
}
See also: https://stackoverflow.com/a/27305748/800457
function nextWeekday (day, weekday) {
const current = day.day()
const days = (7 + weekday - current) % 7
return day.clone().add(days, 'd')
}
// example: get next Friday starting from 7 Oct 2019
nextWeekday(moment('2019-10-07'), 5)) // 2019-10-11
I think the point is that using day() or isoWeekday() you get a date in the current week, no matter which day of the week is today. As a consequence, the date you get can be past, or still to come.
Example:
if today is Wednesday, moment().isoWeekday(5).format() would return the date of the upcoming Friday.
While
moment().isoWeekday(1).format() would return the previous Monday.
So when you say you want the date of, let's say, "last Tuesday", this date could belong to the current week or to the previous week, depending on which day is today.
A possible function to get the date of the last dayOfTheWeek is
function getDateOfPreviousDay(dayOfTheWeek) {
currentDayOfTheWeek = moment().isoWeekday();
if ( currentDayOfTheWeek >= dayOfTheWeek ) {
return moment().isoWeekday(dayOfTheWeek).format(); // a date in the current week
}
else {
return moment().add(-1,'weeks').isoWeekday(dayOfTheWeek).format(); // a date in the previous week
}
}
const upcomingDay = (dayIndex, format = "DD MMMM YYYY") => {
if (
Number(moment().format("D")) >= Number(moment().day(dayIndex).format("D"))
) {
return moment()
.day(7 + dayIndex)
.format(format);
}
return moment().day(dayIndex).format(format);
};

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

Javascript getDay returning incorrect values for April, June, September, November

I'm using this script located here: http://www.javascriptkit.com/script/script2/dyndateselector.shtml
If you try it, and go to any of April, June, September or November, you will notice that the day of the week columns are incorrect. Here's a list of incorrect data (the x starts y stuff is showing the following month.)
Bugged months:
4/April (starts Sunday instead of Friday)
May starts Sunday
6/June (starts Friday instead of Wednesday)
July starts Friday
9/September (starts Saturday instead of Thursday)
October starts Saturday
11/November (starts Thursday instead of Tuesday)
December starts Thursday
You'll notice that every bugged month is starting with the day of the following month, yet all the other months seem to be correct.
I can't find anything on this problem. Anyone able to help? The actual Javascript alone can be found here, and the getDay() method occurs on line 125: http://pastebin.com/0zuBYrzv
I've tested in both Firefox and Chrome.
Here's some very simple code to demonstrate the issue:
<script>
var d = new Date();
d.setMonth(5);
d.setFullYear(2011);
d.setDate(1);
alert(d.getDay());
</script>
This will create an alert with the message "5", meaning Friday (5+1 = 6, Friday is the 6th day of the week,) when in fact Wednesday is the start of the week.
This is actually pretty interesting as i am guessing that tomorrow your original code will work as you want again.
What i think is happening is you are creating a new Date and that will automaticly initialize to today (31th of may).
Then you set the Month to June by which you basically say make it 31th of June. This date doesn't exist so javascript will turn it into 1th of July.
Finally you set the Date but since your month is not anymore what you want it to be the results will be wrong.
Looks like 0 is january and 11 is december.
Apparently JavaScript doesn't like it if I set the month, full year, then day. What I must do is set them all in one function, like so:
<script>
var d = new Date();
d.setFullYear(2011, 5, 1);
alert(d.getDay());
</script>
I think this is a bug in Javascript's Date Object.
Please take a look at the following code.
(I'm using w3school's JSref tool online to see it quickly.)
Please note, the ways used below are told by w3 schools themselves.
Some examples of instantiating a date:
var today = new Date()
var d1 = new Date("October 13, 1975 11:13:00")
var d2 = new Date(79,5,24) (//THIS IS WRONG - unexpected results!!!)
var d3 = new Date(79,5,24,11,33,0)
So please be careful when using this Date object, looks like certain ways of instantiating dates are better than others.
<script type="text/javascript">
function whatDayIsToday(date)
{
var weekday=new Array(7);
weekday[0]="Sunday";
weekday[1]="Monday";
weekday[2]="Tuesday";
weekday[3]="Wednesday";
weekday[4]="Thursday";
weekday[5]="Friday";
weekday[6]="Saturday";
document.write("Today is " + weekday[date.getDay()] + ", <br />");
document.write("the " + date.getDay() + getSuffix(date.getDay()) + " day of the week. <br /><br />")
}
function getSuffix(num)
{
return (num>3)?"th":(num==3)?"rd":(num==2)?"nd":(num==1)?"st":"";
}
//CORRECT
var d3 = new Date("01/01/2011");
whatDayIsToday(d3);
//CORRECT
var d2 = new Date("01/01/2011");
whatDayIsToday(d2);
//DOESN'T WORK
var d5 = new Date("1-1-2011");
whatDayIsToday(d5);
//WRONG
var d4 = new Date("2011", "01", "01");
whatDayIsToday(d4);
//DAY OF WEEK IS WRONG
var d = new Date(2011, 1, 1);
whatDayIsToday(d);
//DAY OF WEEK IS ALSO WRONG
var d0 = new Date(11, 1, 1);
whatDayIsToday(d0);
</script>
outputs (all using some format of 1/1/2011) are:
Today is Saturday,
the 6th day of the week. (CORRECT, January first this year was a saturday)
Today is Saturday,
the 6nd day of the week. (CORRECT)
Today is undefined,
the NaNnd day of the week. (WRONG FORMATTING, DOESN'T WORK - EXPECTED)
Today is Tuesday,
the 2nd day of the week. (WRONG - UNEXPECTED)
Today is Tuesday,
the 2nd day of the week. (WRONG - UNEXPECTED)
Today is Wednesday,
the 3nd day of the week. (WRONG - UNEXPECTED)
Based on the other answers to this question, I'm guessing it has to do with the day I'm on currently (8/26/2011) - this would be my starting new Date() and the days and/or years getting applied in the wrong order.
However, it sure would be nice if this thing worked!
This is the typical wrong approach:
var days = ["Söndag", "Måndag", "Tisdag", "Onsdag", "Torsdag", "Fredag", "Lördag"];
var d = new Date();
d.setFullYear(selectYear.options[selectYear.selectedIndex].value);
d.setMonth(selectMonth.options[selectMonth.selectedIndex].value-1);
d.setDate(selectDay.options[selectDay.selectedIndex].value);
alert(days[d.getDay()]);
It will work most of the time. But it will have problems with when current date is 31 and when trying to set date to 31 for when set at a month with less than 31 days.
This is a good way:
var days = ["Söndag", "Måndag", "Tisdag", "Onsdag", "Torsdag", "Fredag", "Lördag"];
var d = new Date(0); // solves all problems for existing dates
d.setFullYear(selectYear.options[selectYear.selectedIndex].value);
d.setMonth(selectMonth.options[selectMonth.selectedIndex].value-1);
d.setDate(selectDay.options[selectDay.selectedIndex].value);
if( d.getDate() == selectDay.options[selectDay.selectedIndex].value ) {
alert(days[d.getDay()]);
}
else {
alert("this date doesn't exist"); // date was changed cuz date didn't exist
}
Constructing the date with new Date(0) sets the date to 1 jan, this is good because the date is not 31st and january has 31 days which give us the space we need. It wont fail on the 31st and we don't need to set the datas in any special order. It will always works.
If the user sets month to september (30 days) and date to 31 the data object will hold either 1 oct or 1 sep (depending on which order you set them in). Which is irrelevant because they are both wrong. A simple check if the date is what we set it to will tell us if the date is a existing date, if not we can tell the user this.

Categories