基于季节(日期)的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-12月天数的统计对象,初始值为0。
- 计算重叠区间:对每个季节,计算与入住离店日期的重叠天数,若无重叠则跳过。
- 拆分月份天数:将重叠区间按月份拆分,计算每个月份内的消费天数,累加到对应月份的统计项中。
- 格式转换:将统计对象转换为预期的数组格式,方便后续使用。
内容的提问来源于stack exchange,提问作者Denix
相关产品推荐
相关产品推荐

