Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

moment.js - Check if two moments are from the same week, but weeks begin on Friday and end on Thursday

I am creating a Discord bot with node.js and discord.js, and there's a feature that allows users to vote thanks to a command, but I'd like them to vote only once a week.

The issue is that, on this Discord, weeks start on Friday and end on Thursday, therefore I can't simply write :

var weekNow = moment().week();
var weekLastVote = moment(dateLastVote).week();
if (weekNow == weekLastVote){
    //Prevent from voting again
} else {
    //Let the user vote
}

Therefore, I have written some code that seems to work, but I'd like your opinion on it as it seems very sloppy and I'm not sure if I have taken into account all of the possibilities (I don't know if I need to use my month variables for example):

module.exports = {
isSameWeek: function (dateLastVote) {
    // moments for today's date
    var dayNow = moment().weekday();
    var weekNow = moment().week();
    var monthNow = moment().month();
    var yearNow = moment().year();
    var dateNow = moment().format('MMDDYYYY'); // moment without hours/minutes/seconds

    // moments for last vote's date
    var dayLastVote = moment(dateLastVote).weekday();
    var weekLastVote = moment(dateLastVote).week();
    var monthLastVote = moment(dateLastVote).month();
    var yearLastVote = moment(dateLastVote).year();
    var dateLastVote = moment(dateLastVote).format('MMDDYYYY'); // moment without hours/minutes/seconds

    if ((yearNow === yearLastVote && weekNow === weekLastVote && dayLastVote < 5) || // 5 = Friday, starting day of the week (a week = Friday to thursday)
        (yearNow === yearLastVote && weekNow - 1 === weekLastVote && dayLastVote >= 5 && dayNow < 5) || 
        (dateNow === dateLastVote)
    ){
        return true;
    } else {
        return false;
    }
}

};

As I said, this seems do to the trick but I would like someone else's opinion on it to be sure there isn't a simpler way or, if there isn't, if I haven't forgotten anything.

Thank you for reading :)

like image 966
Tokipudi Avatar asked Jun 02 '17 12:06

Tokipudi


2 Answers

I do not know how our approaches compare to each other in matter of performance, but I still wanna show my approach on the problem:

function isSameWeek(firstDay, secondDay, offset) {
    var firstMoment = moment(firstDay);
    var secondMoment = moment(secondDay);

    var startOfWeek = function (_moment, _offset) {
        return _moment.add("days", _moment.weekday() * -1 + (_moment.weekday() >= 7 + _offset ? 7 + _offset : _offset));
    }

    return startOfWeek(firstMoment, offset).isSame(startOfWeek(secondMoment, offset), "day");
}

What the solution does is calculating the start of the week of each of the given dates in respect to the offset (for values >= -7 and <= 0) and returning whether both have the same start of the week. Same start of the week = same week.

All you have to do is call the function passing two date objects (or moment objects) and an offset between -7 and 0, depending on how the week is shifted in relation to a "regular" week.

like image 105
Kevkong Avatar answered Oct 22 '22 00:10

Kevkong


I think that the best way to do want you need is to tell moment that your week starts on Friday. You can simply use updateLocale method customizing dow (day of week) key of the week object and then use your first code snippet. See Customize section of the docs to get more info about locale customization.

Here a live example of setting a custom day as first day of the week and then using your code to check if a given day is in the current week:

moment.updateLocale('en', {
  week: {
    dow : 5, // Friday is the first day of the week.
  }
});

function checkWeek(dateLastVote){
  var weekNow = moment().week();
  var weekLastVote = moment(dateLastVote).week();
  if (weekNow == weekLastVote){
      //Prevent from voting again
      console.log(moment(dateLastVote).format('YYYY-MM-DD') + ' is in the current week')
  } else {
      //Let the user vote
      console.log(moment(dateLastVote).format('YYYY-MM-DD') + ' is NOT in the current week')
  }
}

checkWeek('2017-05-30'); // same week mon-sun, but previous week fri-thu
checkWeek('2017-06-01'); // same week mon-sun, but previous week fri-thu
checkWeek('2017-06-08'); // next week mon-sun, but current week fri-thu

// First day of the current week
console.log(moment().startOf('week').format('YYYY-MM-DD'));
// Last day of the current week
console.log(moment().endOf('week').format('YYYY-MM-DD'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>

EDIT An improved solution is to use moment isSame passing 'week' as second parameter. As the docs states:

Check if a moment is the same as another moment.

If you want to limit the granularity to a unit other than milliseconds, pass it as the second parameter.

Here a live sample:

moment.updateLocale('en', {
  week: {
    dow : 5, // Friday is the first day of the week.
  }
});

function isSameWeek(dateLastVote){
  var now = moment();
  var lastVote = moment(dateLastVote);
  if (now.isSame(lastVote, 'week')){
      //Prevent from voting again
      console.log(moment(dateLastVote).format('YYYY-MM-DD') + ' is in the current week')
  } else {
      //Let the user vote
      console.log(moment(dateLastVote).format('YYYY-MM-DD') + ' is NOT in the current week')
  }
}

isSameWeek('2017-06-10'); // same week mon-sun, but next week fri-thu
isSameWeek('2017-06-03'); // previous week mon-sun, but current week fri-thu
isSameWeek('2017-06-06'); // current week both mon-sun and fri-thu

// First day of the current week
console.log(moment().startOf('week').format('YYYY-MM-DD'));
// Last day of the current week
console.log(moment().endOf('week').format('YYYY-MM-DD'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>
like image 23
VincenzoC Avatar answered Oct 22 '22 01:10

VincenzoC