求优化:墨西哥工作日计算JavaScript代码(跨月场景效率不足)
墨西哥银行工作日转自然日代码的跨月效率优化
我写了一段代码,用来根据起始日期和所需工作日数,计算对应的自然日总数,其中包含墨西哥银行假日的判断(复活节日期参考了维基百科的计算公式)。代码在多数场景下运行正常,但跨月计算时效率偏低,希望能得到优化建议。
使用的常量
const MIN_EN_SEGUNDOS = 60000 ; /* 60*1000 --> SEG*MILSEG */ const HRS_EN_SEGUNDOS = 3600000 ; /* 60*MIN_EN_SEGUNDOS --> MIN*SEG*MILSEG */ const DIA_EN_SEGUNDOS = 86400000; /* 24*HRS_EN_SEGUNDOS --> HRS*MIN*SEG*MILSEG */
完整代码
function doBusinessNatural(start_day, business_days){ var days_business=0, evalua_anio=0, i, start_day_valid =''; var arrHolidays =[]; start_day = new Date(start_day); var start_day_valid = (start_day instanceof Date && !isNaN(start_day.valueOf())); if(!start_day_valid) start_day = new Date(); i = start_day; var evalua_mes=-1, ini_day=0, last_day=0, natural_days=0, number_of_days=0, bus_day=0, bus_day_check=0; function Holidays(Anio){ var DomingoPascua,JuevesSanto,ViernesSanto; if(Anio!=evalua_anio){ function getHoliday(Anio, Mes, sec){ var evalua_dia; evalua_dia = (new Date(Anio,Mes,1)).getDay(); evalua_dia = ((evalua_dia==0)? 2:(evalua_dia>1)? (9-evalua_dia):1)+sec; evalua_dia = (new Date(Anio,Mes,evalua_dia)); return evalua_dia; } arrHolidays = []; DomingoPascua = pascua(Anio); JuevesSanto = new Date(DomingoPascua.getFullYear(),DomingoPascua.getMonth(),DomingoPascua.getDate()-3); ViernesSanto = new Date(DomingoPascua.getFullYear(),DomingoPascua.getMonth(),DomingoPascua.getDate()-2); /* Semana santa */ arrHolidays.push(DomingoPascua) arrHolidays.push(JuevesSanto) arrHolidays.push(ViernesSanto) /* Dias que se mueven a otro día por ley */ /* 5/Febrero -> primer lunes de Febrero / Día de la Constitución Mexicana */ arrHolidays.push(getHoliday(Anio,1,0)); /* 21/Marzo -> tercer lunes / Natalicio de Benito Juárez */ arrHolidays.push(getHoliday(Anio,2,14)); /* 20/Nov -> tercer lunes / Revolución Mexicana*/ arrHolidays.push(getHoliday(Anio,10,14)); /* Dias feriados fijos */ arrHolidays.push(new Date(Anio,0,1)); /* 01/Ene - Año Nuevo */ arrHolidays.push(new Date(Anio,4,1)); /* 01/may - Dia del trabajo */ arrHolidays.push(new Date(Anio,8,16)); /* 16/Sep - Día de la Independencia */ arrHolidays.push(new Date(Anio,10,2)); /* 02/Nov - Dia de muertos */ arrHolidays.push(new Date(Anio,11,12)); /* 12/Dic - V Guadalupe / Día del empleado bancario */ arrHolidays.push(new Date(Anio,11,24)); /* 25/Dic - Navidad */ evalua_anio= Anio; } } function verificaHDays(Mes, Dia, typo){ bus_day_check = 0; for(var x=0; x<arrHolidays.length;x++){ if((arrHolidays[x]).getMonth()==Mes){ var l_v = (arrHolidays[x]).getDay(); if(typo==0){ if(((l_v!=0)&&(l_v!=6))&&(arrHolidays[x]).getDate()>Dia) bus_day_check ++ }else{ if(((l_v!=0)&&(l_v!=6))&&(arrHolidays[x]).getDate()==Dia) bus_day_check ++ } } } return bus_day_check; } var Anio, Mes, Dia; while(days_business<business_days){ Anio = i.getFullYear(); Mes = i.getMonth(); Dia = i.getDate(); Holidays(Anio,evalua_anio); if(Mes!=evalua_mes){ last_day = (new Date(Anio,Mes+1,0)); /* calcula cuando es el último día del mes */ ini_day = i; natural_days = (Math.trunc((last_day - ini_day)/DIA_EN_SEGUNDOS)); /* Diferencia de días */ bus_day = natural_days - Math.trunc((i.getDay()+natural_days)/7)*2; /* calcula los días hábiles - festivos */ /* dias festivos del mes */ bus_day_check = verificaHDays(Mes, Dia, 0); bus_day -= bus_day_check /* cubre el mes ?? */ if((days_business+bus_day)<business_days){ number_of_days += natural_days; days_business += bus_day; } else { while(ini_day<last_day){ i = new Date(Anio,Mes,Dia); /* actualiza el día a evaluar */ ini_day = i; /* actualiza ini_day*/ bus_day_check = verificaHDays(Mes, Dia, 1); days_business += bus_day_check + ((i.getDay()==0)||(i.getDay()==6))? 0:1; number_of_days ++; /* Verifica si llegó al final */ if((business_days-days_business)==0) break; Dia ++; } } /* brinco mes y busco el primer día del siguiente mes / ¿Qué pasa con el resto? */ i= new Date(Anio,Mes+1,1); /* cambia de mes */ } } // Anio = start_day.getFullYear(); Mes = start_day.getMonth(); Dia = start_day.getDate(); // ini_day = Math.trunc(((new Date(Anio,Mes,Dia,0,0,0)).getTime())/DIA_EN_SEGUNDOS)+number_of_days // ini_day = new Date(ini_day*DIA_EN_SEGUNDOS) // return ini_day; return number_of_days; }
优化建议
预缓存节假日数据,避免重复计算
当前Holidays函数每次循环都可能重复判断年份,且内部嵌套的getHoliday函数每次调用都会重新定义。可以把节假日计算逻辑抽离,提前计算好目标年份的所有节假日,用对象按年份缓存(比如holidayCache = {2024: [...], 2025: [...]}),避免重复生成日期对象和计算。优化节假日的存储与查询方式
当前arrHolidays是数组,每次verificaHDays都要遍历整个数组判断月份和日期。可以把节假日按「年份-月份」整理成结构化数据,甚至将每个节假日转换为日期字符串(如'2024-01-01'),查询时直接通过键值对匹配,无需遍历数组,大幅提升判断效率。简化跨月计算逻辑,减少嵌套循环
逐天遍历部分可以优化:提前算出剩余需要的工作日数,直接计算从当前日期开始需要多少自然日能凑够目标数,而非逐天累加判断;也可以批量计算一段日期内的工作日数,减少循环次数。避免频繁创建Date对象
代码中多次创建new Date(),可以改用时间戳进行日期计算,减少对象创建开销——比如把日期转换为毫秒数,通过加减DIA_EN_SEGUNDOS跳转日期,最后再转换为Date对象。修复逻辑冗余与错误
Holidays(Anio,evalua_anio)调用时传入了两个参数,但函数仅接收一个,属于冗余;verificaHDays中的bus_day_check未声明,属于全局变量,会造成变量污染,应改为局部变量;- 逐天计算部分的三元表达式存在运算符优先级问题,需加括号修正:
days_business += bus_day_check + (((i.getDay()==0)||(i.getDay()==6)) ? 0 : 1)。
内容的提问来源于stack exchange,提问作者ArbolNet
相关产品推荐
相关产品推荐

