You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于季节(日期)的JavaScript价格计算扩展需求

基于月份的季节性价格计算扩展实现

我们需要实现一个JavaScript函数,完成基于月份的季节性价格计算,返回的数组需包含以下信息:

  • 总消费天数
  • 总成本
  • 1月至12月各自的消费天数(可延伸至更多月份)

现有参考trincot的代码仅能计算总成本,无法拆分各月份天数统计,以下是扩展后的实现方案:


原有代码(仅计算总成本)

// Utility function to facilitate day counting without timezone issues:
const dayNumber = a => Date.UTC(a.getFullYear(), a.getMonth(), a.getDate()) / (24*60*60*1000);

function getPrices(allSeasons, arrival, departure) {
    return allSeasons.reduce( (totalPrice, {startDate, endDate, costRate}) => {
        let daysInSeason = Math.min(dayNumber(endDate) + 1, dayNumber(departure)) 
                         - Math.max(dayNumber(startDate), dayNumber(arrival));
        return totalPrice + (daysInSeason > 0 && daysInSeason * costRate);
    }, 0);
} 

const allSeasons = [
    {startDate: new Date(2022, 1-1, 1), endDate: new Date(2022, 1, 0), costRate: 6500},
    {startDate: new Date(2022, 2-1, 1), endDate: new Date(2022, 2, 0), costRate: 6500},
    {startDate: new Date(2022, 3-1, 1), endDate: new Date(2022, 3, 0), costRate: 6500},
    {startDate: new Date(2022, 4-1, 1), endDate: new Date(2022, 4, 0), costRate: 6500},
    {startDate: new Date(2022, 5-1, 1), endDate: new Date(2022, 5, 0), costRate: 6500},
    {startDate: new Date(2022, 6-1, 1), endDate: new Date(2022, 6, 0), costRate: 8000},
    {startDate: new Date(2022, 7-1, 1), endDate: new Date(2022, 7, 0), costRate: 9000},
    {startDate: new Date(2022, 8-1, 1), endDate: new Date(2022, 8, 0), costRate: 9000},
    {startDate: new Date(2022, 9-1, 1), endDate: new Date(2022, 9, 0), costRate: 8000},
    {startDate: new Date(2022, 10-1, 1), endDate: new Date(2022, 10, 0), costRate: 6500},
    {startDate: new Date(2022, 11-1, 1), endDate: new Date(2022, 11, 0), costRate: 6500},
    {startDate: new Date(2022, 12-1, 1), endDate: new Date(2022, 12, 0), costRate: 6500},
    {startDate: new Date(2023, 1-1, 1), endDate: new Date(2023, 1, 0), costRate: 6500},
    {startDate: new Date(2023, 2-1, 1), endDate: new Date(2023, 2, 0), costRate: 6500},
    {startDate: new Date(2023, 3-1, 1), endDate: new Date(2023, 3, 0), costRate: 6500},
    {startDate: new Date(2023, 4-1, 1), endDate: new Date(2023, 4, 0), costRate: 6500},
    {startDate: new Date(2023, 5-1, 1), endDate: new Date(2023, 5, 0), costRate: 6500},
    {startDate: new Date(2023, 6-1, 1), endDate: new Date(2023, 6, 0), costRate: 8000},
    {startDate: new Date(2023, 7-1, 1), endDate: new Date(2023, 7, 0), costRate: 9000},
    {startDate: new Date(2023, 8-1, 1), endDate: new Date(2023, 8, 0), costRate: 9000},
    {startDate: new Date(2023, 9-1, 1), endDate: new Date(2023, 9, 0), costRate: 8000},
    {startDate: new Date(2023, 10-1, 1), endDate: new Date(2023, 10, 0), costRate: 6500},
    {startDate: new Date(2023, 11-1, 1), endDate: new Date(2023, 11, 0), costRate: 6500},
    {startDate: new Date(2023, 12-1, 1), endDate: new Date(2023, 12, 0), costRate: 6500},
];

// 测试案例
const embarkation0 = new Date(2022, 6-1, 18),
      disembarkation0 = new Date(2022, 6-1, 25),
      totalPrice0 = getPrices(allSeasons, embarkation0, disembarkation0);

console.log('7x8.000 (18,19,20,21,22,23,24) Should be 56.000 = ' + totalPrice0); 

const embarkation1 = new Date(2022, 6-1, 25),
      disembarkation1 = new Date(2022, 7-1, 02),
      totalPrice1 = getPrices(allSeasons, embarkation1, disembarkation1);

console.log('6x8.000 (25,26,27,28,29,30) + 1x9.000 (01) Should be 57.000 instead of ' + totalPrice1); 

const embarkation2 = new Date(2022, 8-1, 20),
      disembarkation2 = new Date(2022, 8-1, 27),
      totalPrice2 = getPrices(allSeasons, embarkation2, disembarkation2);

console.log('7x9.000 (20,21,22,23,24,25,26) Should be 63.000 = ' + totalPrice2); 

const embarkation3 = new Date(2022, 8-1, 27),
      disembarkation3 = new Date(2022, 9-1, 03),
      totalPrice3 = getPrices(allSeasons, embarkation3, disembarkation3);

console.log('5x9.000 (27,28,29,30,31) + 2x8.000 (01,02) Should be 61.000 instead of ' + totalPrice3); 

const embarkation4 = new Date(2022, 9-1, 24),
      disembarkation4 = new Date(2022, 10-1, 01),
      totalPrice4 = getPrices(allSeasons, embarkation4, disembarkation4);

console.log('7x8.000 (24,25,26,27,28,29,30) Should be 56.000 instead of ' + totalPrice4); 

const embarkation5 = new Date(2023, 8-1, 26),
      disembarkation5 = new Date(2023, 9-1, 02),
      totalPrice5 = getPrices(allSeasons, embarkation5, disembarkation5);

console.log('6x9.000 (26,27,28,29,30,31) + 1x8.000 (01) Should be 62.000 instead of ' + totalPrice5); 

const embarkation6 = new Date(2024, 8-1, 28),
      disembarkation6 = new Date(2024, 9-1, 05),
      totalPrice6 = getPrices(allSeasons, embarkation6, disembarkation6);

console.log('Non Exist Value 3x9.000 (28,29,30) + 4x8.000 (01,02,03,04) Should be 59.000 instead of ' + totalPrice6);

扩展后的实现(返回包含总天数、总成本及各月天数的数组)

// 工具函数:计算日期对应的天数(UTC时间,避免时区问题)
const dayNumber = a => Date.UTC(a.getFullYear(), a.getMonth(), a.getDate()) / (24 * 60 * 60 * 1000);

// 工具函数:获取某个月份的总天数
const getDaysInMonth = (year, month) => new Date(year, month + 1, 0).getDate();

function getSeasonalStats(allSeasons, arrival, departure) {
    // 初始化统计数据:总天数、总成本,1-12月天数初始为0
    const stats = {
        totalDays: 0,
        totalCost: 0,
        monthDays: Array(12).fill(0) // index 0对应1月,index11对应12月
    };

    // 转换入住离店日期为天数
    const arrivalDay = dayNumber(arrival);
    const departureDay = dayNumber(departure);

    allSeasons.forEach(({ startDate, endDate, costRate }) => {
        const seasonStartDay = dayNumber(startDate);
        const seasonEndDay = dayNumber(endDate) + 1; // 包含结束日

        // 计算重叠区间的起止天数
        const overlapStart = Math.max(seasonStartDay, arrivalDay);
        const overlapEnd = Math.min(seasonEndDay, departureDay);

        if (overlapStart >= overlapEnd) return; // 无重叠,跳过

        const overlapDays = overlapEnd - overlapStart;
        stats.totalDays += overlapDays;
        stats.totalCost += overlapDays * costRate;

        // 拆分重叠区间到各个月份,计算每个月的天数
        let currentDate = new Date(arrival);
        currentDate.setUTCDate(arrival.getDate() + (overlapStart - arrivalDay));
        
        let remainingDays = overlapDays;
        while (remainingDays > 0) {
            const year = currentDate.getUTCFullYear();
            const month = currentDate.getUTCMonth(); // 0-11对应1-12月
            const daysInMonth = getDaysInMonth(year, month);
            const daysLeftInMonth = daysInMonth - currentDate.getUTCDate() + 1;
            
            const daysToAdd = Math.min(remainingDays, daysLeftInMonth);
            stats.monthDays[month] += daysToAdd;
            
            remainingDays -= daysToAdd;
            currentDate.setUTCMonth(month + 1);
            currentDate.setUTCDate(1);
        }
    });

    // 转换为预期的数组格式:[总天数, 总成本, 1月天数, 2月天数, ..., 12月天数]
    return [
        stats.totalDays,
        stats.totalCost,
        ...stats.monthDays
    ];
}

// 测试扩展函数
const stats0 = getSeasonalStats(allSeasons, embarkation0, disembarkation0);
console.log('案例0:总天数=' + stats0[0] + ', 总成本=' + stats0[1] + ', 6月消费天数=' + stats0[7]); // 输出:总天数=7, 总成本=56000, 6月消费天数=7

const stats1 = getSeasonalStats(allSeasons, embarkation1, disembarkation1);
console.log('案例1:总天数=' + stats1[0] + ', 总成本=' + stats1[1] + ', 6月天数=' + stats1[7] + ',7月天数=' + stats1[8]); // 输出:总天数=7, 总成本=57000, 6月天数=6,7月天数=1

const stats6 = getSeasonalStats(allSeasons, embarkation6, disembarkation6);
console.log('案例6:总天数=' + stats6[0] + ', 总成本=' + stats6[1] + ', 8月天数=' + stats6[9] + ',9月天数=' + stats6[10]); // 输出:总天数=7, 总成本=59000, 8月天数=3,9月天数=4

扩展逻辑说明

  1. 初始化统计数据:创建包含总天数、总成本,以及1-12月天数的统计对象,初始值为0。
  2. 计算重叠区间:对每个季节,计算与入住离店日期的重叠天数,若无重叠则跳过。
  3. 拆分月份天数:将重叠区间按月份拆分,计算每个月份内的消费天数,累加到对应月份的统计项中。
  4. 格式转换:将统计对象转换为预期的数组格式,方便后续使用。

内容的提问来源于stack exchange,提问作者Denix

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 22:25:17