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

求优化:墨西哥工作日计算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;
}

优化建议

  1. 预缓存节假日数据,避免重复计算
    当前Holidays函数每次循环都可能重复判断年份,且内部嵌套的getHoliday函数每次调用都会重新定义。可以把节假日计算逻辑抽离,提前计算好目标年份的所有节假日,用对象按年份缓存(比如holidayCache = {2024: [...], 2025: [...]}),避免重复生成日期对象和计算。

  2. 优化节假日的存储与查询方式
    当前arrHolidays是数组,每次verificaHDays都要遍历整个数组判断月份和日期。可以把节假日按「年份-月份」整理成结构化数据,甚至将每个节假日转换为日期字符串(如'2024-01-01'),查询时直接通过键值对匹配,无需遍历数组,大幅提升判断效率。

  3. 简化跨月计算逻辑,减少嵌套循环
    逐天遍历部分可以优化:提前算出剩余需要的工作日数,直接计算从当前日期开始需要多少自然日能凑够目标数,而非逐天累加判断;也可以批量计算一段日期内的工作日数,减少循环次数。

  4. 避免频繁创建Date对象
    代码中多次创建new Date(),可以改用时间戳进行日期计算,减少对象创建开销——比如把日期转换为毫秒数,通过加减DIA_EN_SEGUNDOS跳转日期,最后再转换为Date对象。

  5. 修复逻辑冗余与错误

    • Holidays(Anio,evalua_anio)调用时传入了两个参数,但函数仅接收一个,属于冗余;
    • verificaHDays中的bus_day_check未声明,属于全局变量,会造成变量污染,应改为局部变量;
    • 逐天计算部分的三元表达式存在运算符优先级问题,需加括号修正:days_business += bus_day_check + (((i.getDay()==0)||(i.getDay()==6)) ? 0 : 1)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 21:51:48