Search code examples
javascriptmomentjsdate-fns

Getting the date of day X in week N


I have an object that looks like this:

const availability = {
        week1: {
            mon: false,
            tue: true,
            wed: true,
            thu: true,
            fri: false,
        },
        week2: {
            mon: false,
            tue: true,
            wed: true,
            thu: true,
            fri: false,
        },
        week3: {
            mon: false,
            tue: true,
            wed: true,
            thu: true,
            fri: false,
        },
        week4: {
            mon: false,
            tue: true,
            wed: true,
            thu: true,
            fri: false,
        },
    };

This shows the availability of someone on the 1st, 2nd, ... week of the current month.

What I now need is the date of each day that is true. So for example the date of tuesday on the first week of this month, the date of tuesday for the second week of the month and so on.

I have checked out date-fns and moment.js but I can not figure out how I'd tackle this issue.


Solution

  • If we assume that the requirement is that we start from the first Monday of the month then you could break the month into weeks and use your availability to map them. I am using the official moment-range plugin here but you could calculate those ranges other ways:

    const Moment = require('moment');
    const MomentRange = require('moment-range');
    const moment = MomentRange.extendMoment(Moment);
    
    const availability = { week1: { mon: false, tue: true, wed: true, thu: true, fri: false, }, week2: { mon: false, tue: true, wed: true, thu: true, fri: false, }, week3: { mon: false, tue: true, wed: true, thu: true, fri: false, }, week4: { mon: false, tue: true, wed: true, thu: true, fri: false, }, };
    
    const calcDays = (data, days) => {
      // get the assumed first Monday of the current month
      let fM = moment().startOf('month').isoWeekday('Monday')
      let cM = moment().month()
    
      // get the actual first Monday
      fM = fM.month() < cM ? fM.add(1, 'week').isoWeekday('Monday') : fM.month() > cM ? fM.subtract(1, 'week').isoWeekday('Monday') : fM
    
      // Calc the range based on first Monday and end of month
      const range = moment.range(fM, moment().endOf('month'));
      // Break into weeks
      const weeks = [...Array.from(range.by('week')), moment().endOf('month')]
    
      return Object.values(availability).reduce((r,c,i) => {
        var weekRanges = Array.from(moment.range(weeks[i], weeks[i + 1]).by('day'))
        Object.entries(c).map(([k, v], j) => v ? r.push(weekRanges[j].format()) : v)
        return r
      }, [])
    }
    
    console.log(calcDays(availability))
    

    Outputs (ran November 8th 2018):

    [ '2018-11-06T00:00:00+00:00',
      '2018-11-07T00:00:00+00:00',
      '2018-11-08T00:00:00+00:00',
      '2018-11-13T00:00:00+00:00',
      '2018-11-14T00:00:00+00:00',
      '2018-11-15T00:00:00+00:00',
      '2018-11-20T00:00:00+00:00',
      '2018-11-21T00:00:00+00:00',
      '2018-11-22T00:00:00+00:00',
      '2018-11-27T00:00:00+00:00',
      '2018-11-28T00:00:00+00:00',
      '2018-11-29T00:00:00+00:00' ]
    

    You can see it working here