packages/period-selector-dialog/src/modules/FixedPeriodsGenerator.js

Summary

Maintainability
F
1 wk
Test Coverage
// generatePeriods config object: { boolean offset, boolean filterFuturePeriods, boolean reversePeriods }

function DailyPeriodType(formatYyyyMmDd, fnFilter) {
    this.generatePeriods = config => {
        let periods = [];
        const offset = parseInt(config.offset, 10);
        const isFilter = config.filterFuturePeriods;
        const isReverse = config.reversePeriods;
        const year = new Date(Date.now()).getFullYear() + offset;
        const date = new Date(`01 Jan ${year}`);

        while (date.getFullYear() === year) {
            const period = {};
            period.startDate = formatYyyyMmDd(date);
            period.endDate = period.startDate;
            period.name = period.startDate;
            // period['id'] = 'Daily_' + period['startDate'];
            period.iso = period.startDate.replace(/-/g, '');
            period.id = period.iso;
            periods.push(period);
            date.setDate(date.getDate() + 1);
        }

        periods = isFilter ? fnFilter(periods) : periods;
        periods = isReverse ? periods.reverse() : periods;

        return periods;
    };
}

function WeeklyPeriodType(formatYyyyMmDd, weekObj, fnFilter) {
    // Calculate the first date of an EPI year base on ISO standard  ( first week always contains 4th Jan )
    const getEpiWeekStartDay = (year, startDayOfWeek) => {
        const jan4 = new Date(year, 0, 4);
        const jan4DayOfWeek = jan4.getDay();
        const startDate = jan4;
        const dayDiff = jan4DayOfWeek - startDayOfWeek;
    
        if (dayDiff > 0) {
            startDate.setDate(jan4.getDate() - dayDiff);
        } else if (dayDiff < 0) {
            startDate.setDate(jan4.getDate() - dayDiff);
            startDate.setDate(startDate.getDate() - 7);
        }
    
        return startDate;
    };
   
    this.generatePeriods = (config) => {
        let periods = [];
        const offset = parseInt(config.offset, 10);
        const isFilter = config.filterFuturePeriods;
        const isReverse = config.reversePeriods;
        const year = new Date(Date.now()).getFullYear() + offset;
        const date = getEpiWeekStartDay(year, weekObj.startDay);
        let week = 1;

        while (date.getFullYear() <= year) {
            const period = {};
            period.startDate = formatYyyyMmDd(date);
            period.iso = `${year}${weekObj.shortName}W${week}`;
            period.id = period.iso;
            date.setDate(date.getDate() + 6);
            period.endDate = formatYyyyMmDd(date);
            period.name = `Week ${week} - ${period.startDate} - ${period.endDate}`;

            // if end date is Jan 4th or later, week belongs to next year
            if (date.getFullYear() > year && date.getDate() >= 4) {
                break;
            }

            periods.push(period);
            date.setDate(date.getDate() + 1);

            week += 1;
        }

        periods = isFilter ? fnFilter(periods) : periods;
        periods = isReverse ? periods.reverse() : periods;

        return periods;
    };
}

function BiWeeklyPeriodType(formatYyyyMmDd, fnFilter) {
    this.generatePeriods = config => {
        let periods = [];
        const offset = parseInt(config.offset, 10);
        const isFilter = config.filterFuturePeriods;
        const isReverse = config.reversePeriods;
        const year = new Date(Date.now()).getFullYear() + offset;
        const date = new Date(`01 Jan ${year}`);
        const day = date.getDay();
        let biWeek = 1;

        if (day <= 4) {
            date.setDate(date.getDate() - (day - 1));
        } else {
            date.setDate(date.getDate() + (8 - day));
        }

        while (date.getFullYear() <= year) {
            const period = {};

            period.iso = `${year}BiW${biWeek}`;
            period.id = period.iso;
            period.startDate = formatYyyyMmDd(date);
            date.setDate(date.getDate() + 13);

            period.endDate = formatYyyyMmDd(date);
            period.name = `Bi-Week ${biWeek} - ${period.startDate} - ${
                period.endDate
            }`;

            // if end date is Jan 4th or later, biweek belongs to next year
            if (date.getFullYear() > year && date.getDate() >= 4) {
                break;
            }

            periods.push(period);

            date.setDate(date.getDate() + 1);

            biWeek += 1;
        }

        periods = isFilter ? fnFilter(periods) : periods;
        periods = isReverse ? periods.reverse() : periods;

        return periods;
    };
}

function MonthlyPeriodType(formatYyyyMmDd, monthNames, fnFilter) {
    const formatIso = date => {
        const y = date.getFullYear();
        let m = String(date.getMonth() + 1);

        m = m.length < 2 ? `0${m}` : m;

        return y + m;
    };

    this.generatePeriods = config => {
        let periods = [];

        const offset = parseInt(config.offset, 10);
        const isFilter = config.filterFuturePeriods;
        const isReverse = config.reversePeriods;
        const year = new Date(Date.now()).getFullYear() + offset;
        const date = new Date(`31 Dec ${year}`);

        while (date.getFullYear() === year) {
            const period = {};

            period.endDate = formatYyyyMmDd(date);
            date.setDate(1);
            period.startDate = formatYyyyMmDd(date);
            period.name = `${
                monthNames[date.getMonth()]
            } ${date.getFullYear()}`;
            period.iso = formatIso(date);
            period.id = period.iso;
            periods.push(period);
            date.setDate(0);
        }

        periods = isFilter ? fnFilter(periods) : periods;
        periods = isReverse ? periods : periods.reverse();
        // Months are collected backwards. If isReverse is true, then do nothing. Else reverse to correct order and return.

        return periods;
    };
}

function BiMonthlyPeriodType(formatYyyyMmDd, monthNames, fnFilter) {
    this.generatePeriods = config => {
        let periods = [];
        const offset = parseInt(config.offset, 10);
        const isFilter = config.filterFuturePeriods;
        const isReverse = config.reversePeriods;
        const year = new Date(Date.now()).getFullYear() + offset;
        const date = new Date(`31 Dec ${year}`);
        let index = 6;

        while (date.getFullYear() === year) {
            const period = {};

            period.endDate = formatYyyyMmDd(date);
            date.setDate(0);
            date.setDate(1);
            period.startDate = formatYyyyMmDd(date);
            period.name = `${monthNames[date.getMonth()]} - ${
                monthNames[date.getMonth() + 1]
            } ${date.getFullYear()}`;
            period.iso = `${year}0${index}B`;
            period.id = period.iso;
            periods.push(period);
            date.setDate(0);

            index--;
        }

        periods = isFilter ? fnFilter(periods) : periods;
        periods = isReverse ? periods : periods.reverse();
        // Bi-months are collected backwards. If isReverse is true, then do nothing. Else reverse to correct order and return.

        return periods;
    };
}

function QuarterlyPeriodType(formatYyyyMmDd, monthNames, fnFilter) {
    this.generatePeriods = config => {
        let periods = [];
        const offset = parseInt(config.offset, 10);
        const isFilter = config.filterFuturePeriods;
        const isReverse = config.reversePeriods;
        const year = new Date(Date.now()).getFullYear() + offset;
        const date = new Date(`31 Dec ${year}`);
        let quarter = 4;

        while (date.getFullYear() === year) {
            const period = {};
            period.endDate = formatYyyyMmDd(date);
            date.setDate(0);
            date.setDate(0);
            date.setDate(1);
            period.startDate = formatYyyyMmDd(date);
            period.name = `${monthNames[date.getMonth()]} - ${
                monthNames[date.getMonth() + 2]
            } ${date.getFullYear()}`;
            period.iso = `${year}Q${quarter}`;
            period.id = period.iso;
            periods.push(period);
            date.setDate(0);
            quarter -= 1;
        }

        periods = isFilter ? fnFilter(periods) : periods;
        periods = isReverse ? periods : periods.reverse();
        // Quarters are collected backwards. If isReverse is true, then do nothing. Else reverse to correct order and return.

        return periods;
    };
}

function SixMonthlyPeriodType(monthNames, fnFilter) {
    this.generatePeriods = config => {
        let periods = [];
        const offset = parseInt(config.offset, 10);
        const isFilter = config.filterFuturePeriods;
        const isReverse = config.reversePeriods;
        const year = new Date(Date.now()).getFullYear() + offset;

        let period = {};
        period.startDate = `${year}-01-01`;
        period.endDate = `${year}-06-30`;
        period.name = `${monthNames[0]} - ${monthNames[5]} ${year}`;
        period.iso = `${year}S1`;
        period.id = period.iso;
        periods.push(period);

        period = {};
        period.startDate = `${year}-07-01`;
        period.endDate = `${year}-12-31`;
        period.name = `${monthNames[6]} - ${monthNames[11]} ${year}`;
        period.iso = `${year}S2`;
        period.id = period.iso;
        periods.push(period);

        periods = isFilter ? fnFilter(periods) : periods;
        periods = isReverse ? periods.reverse() : periods;

        return periods;
    };
}

function SixMonthlyAprilPeriodType(monthNames, fnFilter) {
    this.generatePeriods = config => {
        let periods = [];
        const offset = parseInt(config.offset, 10);
        const isFilter = config.filterFuturePeriods;
        const isReverse = config.reversePeriods;
        const year = new Date(Date.now()).getFullYear() + offset;

        let period = {};
        period.startDate = `${year}-04-01`;
        period.endDate = `${year}-09-30`;
        period.name = `${monthNames[3]} - ${monthNames[8]} ${year}`;
        period.iso = `${year}AprilS1`;
        period.id = period.iso;
        periods.push(period);

        period = {};
        period.startDate = `${year}-10-01`;
        period.endDate = `${year + 1}-03-31`;
        period.name = `${monthNames[9]} ${year} - ${monthNames[2]} ${year + 1}`;
        period.iso = `${year}AprilS2`;
        period.id = period.iso;
        periods.push(period);

        periods = isFilter ? fnFilter(periods) : periods;
        periods = isReverse ? periods.reverse() : periods;

        return periods;
    };
}

function YearlyPeriodType(formatYyyyMmDd, fnFilter) {
    this.generatePeriods = config => {
        let periods = [];
        const offset = parseInt(config.offset, 10);
        const isFilter = config.filterFuturePeriods;
        const isReverse = config.reversePeriods;
        const year = new Date(Date.now()).getFullYear() + offset;
        const date = new Date(`31 Dec ${year}`);

        while (year - date.getFullYear() < 10) {
            const period = {};
            period.endDate = formatYyyyMmDd(date);
            date.setMonth(0, 1);
            period.startDate = formatYyyyMmDd(date);
            period.name = date.getFullYear().toString();
            period.iso = date.getFullYear().toString();
            period.id = period.iso.toString();
            periods.push(period);
            date.setDate(0);
        }

        periods = isFilter ? fnFilter(periods) : periods;
        periods = isReverse ? periods : periods.reverse();
        // Years are collected backwards. If isReverse is true, then do nothing. Else reverse to correct order and return.

        return periods;
    };
}

function FinancialOctoberPeriodType(formatYyyyMmDd, monthNames, fnFilter) {
    this.generatePeriods = config => {
        let periods = [];
        const offset = parseInt(config.offset, 10);
        const isFilter = config.filterFuturePeriods;
        const isReverse = config.reversePeriods;
        const year = new Date(Date.now()).getFullYear() + offset;
        const date = new Date(`30 Sep ${year + 1}`);

        for (let i = 0; i < 10; i++) {
            const period = {};
            period.endDate = formatYyyyMmDd(date);
            date.setYear(date.getFullYear() - 1);
            date.setDate(date.getDate() + 1);
            period.startDate = formatYyyyMmDd(date);
            period.name = `${monthNames[9]} ${date.getFullYear()} - ${
                monthNames[8]
            } ${date.getFullYear() + 1}`;
            period.id = `${date.getFullYear()}Oct`;
            periods.push(period);
            date.setDate(date.getDate() - 1);
        }

        periods = isFilter ? fnFilter(periods) : periods;
        periods = isReverse ? periods : periods.reverse();
        // FinancialOctober periods are collected backwards. If isReverse is true, then do nothing. Else reverse to correct order and return.

        return periods;
    };
}

function FinancialNovemberPeriodType(formatYyyyMmDd, monthNames, fnFilter) {
    this.generatePeriods = config => {
        let periods = [];
        const offset = parseInt(config.offset, 10);
        const isFilter = config.filterFuturePeriods;
        const isReverse = config.reversePeriods;
        const year = new Date(Date.now()).getFullYear() + offset;
        const date = new Date(`31 Oct ${year + 1}`);

        for (let i = 0; i < 10; i++) {
            const period = {};
            period.endDate = formatYyyyMmDd(date);
            date.setYear(date.getFullYear() - 1);
            date.setDate(date.getDate() + 1);
            period.startDate = formatYyyyMmDd(date);
            period.name = `${monthNames[10]} ${date.getFullYear()} - ${
                monthNames[9]
            } ${date.getFullYear() + 1}`;
            period.id = `${date.getFullYear()}Nov`;
            periods.push(period);
            date.setDate(date.getDate() - 1);
        }

        periods = isFilter ? fnFilter(periods) : periods;
        periods = isReverse ? periods : periods.reverse();
        // FinancialNovember periods are collected backwards. If isReverse is true, then do nothing. Else reverse to correct order and return.

        return periods;
    };
}

function FinancialJulyPeriodType(formatYyyyMmDd, monthNames, fnFilter) {
    this.generatePeriods = config => {
        let periods = [];
        const offset = parseInt(config.offset, 10);
        const isFilter = config.filterFuturePeriods;
        const isReverse = config.reversePeriods;
        const year = new Date(Date.now()).getFullYear() + offset;
        const date = new Date(`30 Jun ${year + 1}`);

        for (let i = 0; i < 10; i++) {
            const period = {};
            period.endDate = formatYyyyMmDd(date);
            date.setYear(date.getFullYear() - 1);
            date.setDate(date.getDate() + 1);
            period.startDate = formatYyyyMmDd(date);
            period.name = `${monthNames[6]} ${date.getFullYear()} - ${
                monthNames[5]
            } ${date.getFullYear() + 1}`;
            period.id = `${date.getFullYear()}July`;
            periods.push(period);
            date.setDate(date.getDate() - 1);
        }

        periods = isFilter ? fnFilter(periods) : periods;
        periods = isReverse ? periods : periods.reverse();
        // FinancialJuly periods are collected backwards. If isReverse is true, then do nothing. Else reverse to correct order and return.

        return periods;
    };
}

function FinancialAprilPeriodType(formatYyyyMmDd, monthNames, fnFilter) {
    this.generatePeriods = config => {
        let periods = [];
        const offset = parseInt(config.offset, 10);
        const isFilter = config.filterFuturePeriods;
        const isReverse = config.reversePeriods;
        const year = new Date(Date.now()).getFullYear() + offset;
        const date = new Date(`31 Mar ${year + 1}`);

        for (let i = 0; i < 10; i++) {
            const period = {};
            period.endDate = formatYyyyMmDd(date);
            date.setYear(date.getFullYear() - 1);
            date.setDate(date.getDate() + 1);
            period.startDate = formatYyyyMmDd(date);
            period.name = `${monthNames[3]} ${date.getFullYear()} - ${
                monthNames[2]
            } ${date.getFullYear() + 1}`;
            period.id = `${date.getFullYear()}April`;
            periods.push(period);
            date.setDate(date.getDate() - 1);
        }

        periods = isFilter ? fnFilter(periods) : periods;
        periods = isReverse ? periods : periods.reverse();
        // FinancialApril periods are collected backwards. If isReverse is true, then do nothing. Else reverse to correct order and return.

        return periods;
    };
}

function PeriodType() {
    const monthNames = [
        'January',
        'February',
        'March',
        'April',
        'May',
        'June',
        'July',
        'August',
        'September',
        'October',
        'November',
        'December',
    ];

    const formatYyyyMmDd = date => {
        const y = date.getFullYear();
        let m = String(date.getMonth() + 1);
        let d = String(date.getDate());

        m = m.length < 2 ? `0${m}` : m;
        d = d.length < 2 ? `0${d}` : d;

        return `${y}-${m}-${d}`;
    };

    const filterFuturePeriods = periods => {
        const array = [];
        const now = new Date(Date.now());

        for (let i = 0; i < periods.length; i++) {
            if (new Date(periods[i].startDate) <= now) {
                array.push(periods[i]);
            }
        }

        return array;
    };

    const periodTypes = [];

    periodTypes.Daily = new DailyPeriodType(
        formatYyyyMmDd,
        filterFuturePeriods
    );
    periodTypes.Weekly = new WeeklyPeriodType(
        formatYyyyMmDd,
        { shortName: '', startDay: 1 },
        filterFuturePeriods
    );
    periodTypes['Bi-weekly'] = new BiWeeklyPeriodType(
        formatYyyyMmDd,
        filterFuturePeriods
    );
    periodTypes['Weekly (Start Wednesday)'] = new WeeklyPeriodType(
        formatYyyyMmDd,
        { shortName: 'Wed', startDay: 3 },
        filterFuturePeriods
    );
    periodTypes['Weekly (Start Thursday)'] = new WeeklyPeriodType(
        formatYyyyMmDd,
        { shortName: 'Thu', startDay: 4 },
        filterFuturePeriods
    );
    periodTypes['Weekly (Start Saturday)'] = new WeeklyPeriodType(
        formatYyyyMmDd,
        { shortName: 'Sat', startDay: 6 },
        filterFuturePeriods
    );
    periodTypes['Weekly (Start Sunday)'] = new WeeklyPeriodType(
        formatYyyyMmDd,
        { shortName: 'Sun', startDay: 7 },
        filterFuturePeriods
    );
    periodTypes.Monthly = new MonthlyPeriodType(
        formatYyyyMmDd,
        monthNames,
        filterFuturePeriods
    );
    periodTypes['Bi-monthly'] = new BiMonthlyPeriodType(
        formatYyyyMmDd,
        monthNames,
        filterFuturePeriods
    );
    periodTypes.Quarterly = new QuarterlyPeriodType(
        formatYyyyMmDd,
        monthNames,
        filterFuturePeriods
    );
    periodTypes['Six-monthly'] = new SixMonthlyPeriodType(
        monthNames,
        filterFuturePeriods
    );
    periodTypes['Six-monthly April'] = new SixMonthlyAprilPeriodType(
        monthNames,
        filterFuturePeriods
    );
    periodTypes.Yearly = new YearlyPeriodType(
        formatYyyyMmDd,
        filterFuturePeriods
    );
    periodTypes[
        'Financial year (Start November)'
    ] = new FinancialNovemberPeriodType(
        formatYyyyMmDd,
        monthNames,
        filterFuturePeriods
    );
    periodTypes[
        'Financial year (Start October)'
    ] = new FinancialOctoberPeriodType(
        formatYyyyMmDd,
        monthNames,
        filterFuturePeriods
    );
    periodTypes['Financial year (Start July)'] = new FinancialJulyPeriodType(
        formatYyyyMmDd,
        monthNames,
        filterFuturePeriods
    );
    periodTypes['Financial year (Start April)'] = new FinancialAprilPeriodType(
        formatYyyyMmDd,
        monthNames,
        filterFuturePeriods
    );

    this.get = key => periodTypes[key];
    this.getOptions = () => Object.keys(periodTypes);
}

export default PeriodType;