commercial-calendar-ts
v1.0.0
Published
Professional TypeScript library for 30/360 commercial calendar calculations. Ideal for fintech, banking, payroll systems, and financial applications.
Maintainers
Readme
Calendario Comercial TypeScript 30/360
Librería TypeScript profesional para cálculos financieros usando la convención de conteo de días 30/360 (también conocida como Bond Basis o 30/360 US).
Ideal para: Sistemas bancarios, aplicaciones de nómina, cálculo de intereses, liquidaciones laborales, y aplicaciones fintech que requieren precisión en cálculos de días.
Instalación • ¿Qué es 30/360? • API • Ejemplos • Casos de Uso
📋 Tabla de Contenidos
- ¿Qué es el Calendario Comercial 30/360?
- ¿Por qué es Importante en Finanzas?
- Instalación
- Inicio Rápido
- API Reference
- Ejemplos
- Comparación de Convenciones
- Casos de Uso
- Tests
- Roadmap
📘 ¿Qué es el Calendario Comercial 30/360?
El método 30/360 es una convención de conteo de días ampliamente utilizada en finanzas donde:
- Cada mes tiene exactamente 30 días
- Cada año tiene exactamente 360 días
- Los días 31 se normalizan a 30
Fórmula Matemática
La fórmula básica para calcular días entre dos fechas es:
$$ \text{Días} = (Y_2 - Y_1) \times 360 + (M_2 - M_1) \times 30 + (D_2 - D_1) $$
Donde:
- $Y_1, Y_2$ = Años (inicio, fin)
- $M_1, M_2$ = Meses (inicio, fin)
- $D_1, D_2$ = Días (inicio, fin)
Ajustes:
- Si $D_1 = 31$, entonces $D_1 = 30$
- Si $D_2 = 31$ y $D_1 \geq 30$, entonces $D_2 = 30$
Variantes del Método 30/360
Existen varias variantes del método 30/360:
| Convención | También Conocida Como | Uso Principal | |------------|----------------------|---------------| | 30/360 US | Bond Basis, NASD | Bonos corporativos y municipales en EE.UU. | | 30E/360 | Eurobond Basis | Mercados de eurobonos | | 30E/360 ISDA | 30E/360 ISDA | Derivados de tasas de interés | | 30/360 German | 30E/360 ICMA | Bonos alemanes |
Esta librería implementa 30/360 US (Bond Basis), la más utilizada en Latinoamérica para cálculos laborales y financieros.
💼 ¿Por qué es Importante en Finanzas?
1. Simplicidad en Cálculos
En lugar de contar días calendario reales (que varían entre 28-31 días por mes), el método 30/360 simplifica dramáticamente los cálculos financieros:
// ❌ Calendario Real: Complejo
// Enero: 31 días, Febrero: 28/29 días, Marzo: 31 días...
// ✅ Calendario 30/360: Uniforme
// Todos los meses: 30 días, Todos los años: 360 días2. Estándar en la Industria
- Bonos Corporativos: El 90% de bonos corporativos en EE.UU. usan 30/360
- Swaps de Tasas: Mercados de derivados OTC
- Cálculos Laborales: Legislación laboral en varios países latinoamericanos
- Software Bancario: SAP, Oracle Financials, sistemas de core bancario
3. Consistencia Legal
Muchas jurisdicciones requieren por ley el uso de calendario comercial para:
- Cálculo de cesantías / severance pay
- Liquidaciones laborales
- Intereses de préstamos específicos
- Bonificaciones prorrateadas
4. Predictibilidad
Los cálculos son reproducibles y auditables:
// Mismo input = Mismo output, siempre
calculateWorkedDaysInYear('2025-01-15', '2025-06-15', 2025);
// Resultado: 150 días (5 meses × 30)
// No importa si febrero tiene 28 o 29 días📦 Instalación
npm install commercial-calendar-tsO clona el repositorio:
git clone https://github.com/YamiCueto/commercial-calendar-ts.git
cd commercial-calendar-ts
npm install🚀 Inicio Rápido
import { calculateWorkedDaysInYear, getWorkedYears } from 'commercial-calendar-ts';
// Ejemplo 1: Calcular días trabajados en un año específico
const days = calculateWorkedDaysInYear('2025-01-15', '2025-06-15', 2025);
console.log(days); // 150 días (5 meses × 30)
// Ejemplo 2: Período multi-año
const startDate = '2023-11-01';
const endDate = '2024-08-15';
const years = getWorkedYears(startDate, endDate);
years.forEach(year => {
const daysInYear = calculateWorkedDaysInYear(startDate, endDate, year);
console.log(`${year}: ${daysInYear} días`);
});
// Output:
// 2023: 60 días
// 2024: 225 días📚 API Reference
calculateWorkedDaysInYear(startDate, endDate, targetYear)
Calcula el número de días trabajados en un año específico usando la convención 30/360.
Parámetros:
| Parámetro | Tipo | Descripción |
|-----------|------|-------------|
| startDate | string | Fecha de inicio en formato YYYY-MM-DD |
| endDate | string | Fecha de fin en formato YYYY-MM-DD |
| targetYear | number | Año objetivo para el cálculo |
Retorno:
| Tipo | Descripción |
|------|-------------|
| number | Número de días trabajados en el año objetivo (0-360) |
Reglas de Cálculo:
- ✅ Ambos días incluidos: La fecha de inicio y fin se cuentan
- ✅ Ajuste día 31: Los días 31 se convierten automáticamente en día 30
- ✅ Intersección: Solo cuenta días dentro del año objetivo
- ✅ Regla especial: Cuando los días del mes son iguales y hay diferencia de meses, no se suma +1 adicional
Ejemplos:
// Mismo día
calculateWorkedDaysInYear('2025-01-01', '2025-01-01', 2025);
// → 1 día
// Días consecutivos
calculateWorkedDaysInYear('2025-01-01', '2025-01-02', 2025);
// → 2 días
// Un mes exacto
calculateWorkedDaysInYear('2025-01-15', '2025-02-15', 2025);
// → 30 días
// Múltiples meses
calculateWorkedDaysInYear('2025-01-01', '2025-06-30', 2025);
// → 180 días (6 meses × 30)
// Año completo
calculateWorkedDaysInYear('2025-01-01', '2025-12-31', 2025);
// → 360 días
// Período multi-año (solo cuenta días de 2024)
calculateWorkedDaysInYear('2023-11-15', '2024-03-20', 2024);
// → 80 días (solo del 1 de enero al 20 de marzo de 2024)
// Día 31 ajustado
calculateWorkedDaysInYear('2025-01-31', '2025-02-28', 2025);
// → 30 días (31 de enero se trata como 30)Edge Cases Cubiertos:
// Período fuera del año objetivo
calculateWorkedDaysInYear('2023-01-01', '2023-12-31', 2025);
// → 0 días
// Período que no intersecta
calculateWorkedDaysInYear('2025-06-01', '2025-08-31', 2024);
// → 0 días
// Año bisiesto (no afecta, todos los meses son 30 días)
calculateWorkedDaysInYear('2024-02-01', '2024-02-29', 2024);
// → 30 días (febrero también tiene 30 días en 30/360)getWorkedYears(startDate, endDate)
Extrae todos los años que intersectan con el período de trabajo.
Parámetros:
| Parámetro | Tipo | Descripción |
|-----------|------|-------------|
| startDate | string | Fecha de inicio en formato YYYY-MM-DD |
| endDate | string | Fecha de fin en formato YYYY-MM-DD |
Retorno:
| Tipo | Descripción |
|------|-------------|
| number[] | Array de años en orden ascendente |
Ejemplo:
getWorkedYears('2022-11-15', '2024-03-20');
// → [2022, 2023, 2024]
getWorkedYears('2025-01-01', '2025-12-31');
// → [2025]parseLocalYMD(dateString)
Función auxiliar para parsear fechas en formato YYYY-MM-DD sin problemas de zona horaria.
Parámetros:
| Parámetro | Tipo | Descripción |
|-----------|------|-------------|
| dateString | string | Fecha en formato YYYY-MM-DD |
Retorno:
| Tipo | Descripción |
|------|-------------|
| Date | Objeto Date con hora 00:00:00 local |
Ejemplo:
import { parseLocalYMD } from 'commercial-calendar-ts/utils/formatter/localYMD';
const date = parseLocalYMD('2025-06-15');
// → Date object: 2025-06-15T00:00:00 (hora local)💡 Ejemplos
Ejemplo 1: Cálculo de Cesantías / Severance Pay
interface ResultadoCesantias {
year: number;
baseSalary: number;
daysWorked: number;
amount: number;
}
function calcularCesantias(
fechaIngreso: string,
fechaRetiro: string,
salarioMensual: number
): ResultadoCesantias[] {
const years = getWorkedYears(fechaIngreso, fechaRetiro);
const resultados: ResultadoCesantias[] = [];
years.forEach(year => {
const daysWorked = calculateWorkedDaysInYear(fechaIngreso, fechaRetiro, year);
if (daysWorked > 0) {
// Fórmula legal: (Salario × Días Trabajados) / 360
const amount = (salarioMensual * daysWorked) / 360;
resultados.push({
year,
baseSalary: salarioMensual,
daysWorked,
amount: Math.round(amount)
});
}
});
return resultados;
}
// Uso
const cesantias = calcularCesantias(
'2020-03-15', // Fecha de ingreso
'2024-11-30', // Fecha de retiro
3500000 // Salario mensual
);
console.log(cesantias);
/* Output:
[
{ year: 2020, baseSalary: 3500000, daysWorked: 285, amount: 2771250 },
{ year: 2021, baseSalary: 3500000, daysWorked: 360, amount: 3500000 },
{ year: 2022, baseSalary: 3500000, daysWorked: 360, amount: 3500000 },
{ year: 2023, baseSalary: 3500000, daysWorked: 360, amount: 3500000 },
{ year: 2024, baseSalary: 3500000, daysWorked: 330, amount: 3212500 }
]
*/
// Total cesantías
const total = cesantias.reduce((sum, item) => sum + item.amount, 0);
console.log(`Total cesantías: $${total.toLocaleString('es-CO')}`);
// → Total cesantías: $16,483,750Ejemplo 2: Cálculo de Intereses con 30/360
interface InterestCalculation {
principal: number;
rate: number;
startDate: string;
endDate: string;
days: number;
interest: number;
}
function calcularInteres(
principal: number,
tasaAnual: number, // Tasa anual (ej: 0.12 = 12%)
fechaInicio: string,
fechaFin: string
): InterestCalculation {
// Usar solo el año de inicio para el cálculo
const year = new Date(fechaInicio).getFullYear();
const days = calculateWorkedDaysInYear(fechaInicio, fechaFin, year);
// Fórmula: I = P × r × (d/360)
const interest = principal * tasaAnual * (days / 360);
return {
principal,
rate: tasaAnual,
startDate: fechaInicio,
endDate: fechaFin,
days,
interest: Math.round(interest * 100) / 100
};
}
// Uso: Préstamo de $10,000,000 al 12% anual
const calculo = calcularInteres(
10000000, // Principal
0.12, // 12% anual
'2025-01-15',
'2025-04-15'
);
console.log(calculo);
/* Output:
{
principal: 10000000,
rate: 0.12,
startDate: '2025-01-15',
endDate: '2025-04-15',
days: 90,
interest: 300000
}
*/
console.log(`Interés a pagar: $${calculo.interest.toLocaleString('es-CO')}`);
// → Interés a pagar: $300,000Ejemplo 3: Bonificaciones Prorrateadas
function calcularBonificacionProrrateada(
bonusAnual: number,
fechaIngreso: string,
fechaCorte: string
): number {
const year = new Date(fechaCorte).getFullYear();
const diasTrabajados = calculateWorkedDaysInYear(fechaIngreso, fechaCorte, year);
// Bonificación prorrateada: (Bonus Anual × Días Trabajados) / 360
const bonusProrateado = (bonusAnual * diasTrabajados) / 360;
return Math.round(bonusProrateado);
}
// Uso
const bonus = calcularBonificacionProrrateada(
6000000, // Bonus anual
'2025-04-01', // Fecha de ingreso
'2025-12-31' // Fecha de corte
);
console.log(`Bonus prorrateado: $${bonus.toLocaleString('es-CO')}`);
// → Bonus prorrateado: $4,500,000
// (270 días trabajados de 360 posibles)Más ejemplos en: /examples
🔄 Comparación de Convenciones de Calendario
Tabla Comparativa
| Convención | Días/Mes | Días/Año | Ajuste Día 31 | Uso Principal | |------------|----------|----------|---------------|---------------| | 30/360 US (Esta librería) | 30 | 360 | Sí | Bonos corporativos, cálculos laborales | | Actual/Actual | Real | Real | No | Bonos del tesoro, mercados gubernamentales | | Actual/360 | Real | 360 | No | Préstamos bancarios, líneas de crédito | | Actual/365 | Real | 365 | No | UK gilts, algunos mercados europeos | | 30E/360 | 30 | 360 | Sí (diferente) | Eurobonos |
Ejemplo de Diferencias
// Período: 15 de enero a 15 de marzo de 2024
// 30/360 US (esta librería)
calculateWorkedDaysInYear('2024-01-15', '2024-03-15', 2024);
// → 60 días (2 meses × 30)
// Actual/Actual (calendario real)
// Enero: 17 días (15-31)
// Febrero: 29 días (año bisiesto)
// Marzo: 15 días
// → 61 días
// Diferencia: 1 día¿Cuándo Usar Cada Convención?
Usa 30/360 cuando:
- ✅ Calcules cesantías o liquidaciones laborales
- ✅ Trabajes con bonos corporativos o municipales
- ✅ Necesites simplicidad y reproducibilidad
- ✅ La legislación local lo requiera
- ✅ Implementes sistemas de nómina
Usa Actual/Actual cuando:
- ✅ Trabajes con bonos del tesoro
- ✅ Necesites máxima precisión con días reales
- ✅ Calcules intereses de mercados gubernamentales
Usa Actual/360 cuando:
- ✅ Calcules intereses de préstamos bancarios
- ✅ Trabajes con líneas de crédito comercial
- ✅ Implementes sistemas de tesorería
🎯 Casos de Uso
1. Sistemas de Nómina (Payroll Systems)
// Calcular días trabajados para liquidación
const diasLiquidacion = calculateWorkedDaysInYear(
empleado.fechaIngreso,
empleado.fechaRetiro,
añoActual
);
const liquidacion = {
cesantias: (salario * diasLiquidacion) / 360,
primaServicios: (salario * diasLiquidacion) / 360,
vacaciones: (salario * diasLiquidacion) / 720
};2. Plataformas de Inversión (Investment Platforms)
// Calcular rendimientos de bonos
const diasInversion = calculateWorkedDaysInYear(
fechaCompra,
fechaVencimiento,
año
);
const rendimiento = principal * (tasaCupon / 100) * (diasInversion / 360);3. Core Bancario (Banking Core Systems)
// Cálculo de intereses moratorios
const diasMora = calculateWorkedDaysInYear(
fechaVencimiento,
fechaPagoReal,
año
);
const interesMoratorio = saldoPendiente * tasaMora * (diasMora / 360);4. Aplicaciones Fintech
// Robo-advisors, préstamos P2P, crowdfunding
const diasPrestamo = calculateWorkedDaysInYear(
fechaDesembolso,
fechaPago,
año
);
const interesDevengado = montoDesembolsado * tasa * (diasPrestamo / 360);5. Software de RRHH (HR Software)
// Cálculo de beneficios prorrateados
const diasTrabajados = calculateWorkedDaysInYear(
fechaIngreso,
fechaCorte,
año
);
const beneficioProrrateado = beneficioAnual * (diasTrabajados / 360);Documentación completa de casos de uso: USE-CASES.md
🧪 Tests
La librería incluye 18 tests exhaustivos que cubren:
Edge Cases Cubiertos
- Mismo día: Fecha inicio = Fecha fin
- Días consecutivos: Diferencia de 1 día
- Meses exactos: Del día 15 al día 15 del siguiente mes
- Años completos: 360 días
- Períodos multi-año: Distribución correcta por año
- Día 31: Ajuste automático a día 30
- Febrero: Tratado como mes de 30 días
- Períodos fuera del año objetivo: Retorna 0
- Intersecciones parciales: Solo días dentro del año
Ejecutar Tests
# Ejecutar todos los tests
npm test
# Tests con coverage
npm run test:coverage
# Tests en modo watch
npm run test:watchResultados
Test Suites: 1 passed, 1 total
Tests: 18 passed, 18 total
Snapshots: 0 total
Time: 1.1 s
Coverage:
--------------------|---------|----------|---------|---------|
File | % Stmts | % Branch | % Funcs | % Lines |
--------------------|---------|----------|---------|---------|
All files | 100 | 100 | 100 | 100 |
calculateWorked... | 100 | 100 | 100 | 100 |
getWorkedYears.ts | 100 | 100 | 100 | 100 |
localYMD.ts | 100 | 100 | 100 | 100 |
--------------------|---------|----------|---------|---------|📁 Estructura del Proyecto
commercial-calendar-ts/
├── calculateWorkedDaysInYear.ts # Función principal
├── calculateWorkedDaysInYear.test.ts # Suite de tests
├── getWorkedYears.ts # Extractor de años
├── utils/
│ └── formatter/
│ └── localYMD.ts # Date parser
├── examples/
│ ├── severance-pay.ts # Ejemplo: Cesantías
│ ├── interest-calculation.ts # Ejemplo: Intereses
│ └── worked-days.ts # Ejemplo: Días trabajados
├── docs/
│ └── USE-CASES.md # Casos de uso detallados
├── package.json
├── tsconfig.json
├── jest.config.js
├── .npmignore
├── LICENSE
└── README.md🗺️ Roadmap
v1.1.0 (Próximo Release)
- [ ] Soporte para 30E/360 (Eurobond Basis)
- [ ] Soporte para Actual/360
- [ ] Soporte para Actual/365
- [ ] CLI tool para cálculos rápidos
- [ ] Publicación en NPM
v1.2.0
- [ ] Plugin para Excel/Google Sheets
- [ ] API REST opcional
- [ ] Soporte para múltiples monedas
- [ ] Calculadora web interactiva
v2.0.0
- [ ] Soporte para 30/360 ISDA
- [ ] Cálculo de intereses compuestos
- [ ] Amortización de préstamos
- [ ] Integración con sistemas ERP
🤝 Contribuciones
¡Las contribuciones son bienvenidas! Por favor:
- Fork el repositorio
- Crea una rama (
git checkout -b feature/nueva-funcionalidad) - Commit tus cambios (
git commit -am 'Add: nueva funcionalidad') - Push a la rama (
git push origin feature/nueva-funcionalidad) - Abre un Pull Request
Guías de contribución: Asegúrate de que todos los tests pasen y la cobertura se mantenga en 100%.
👤 Autor
Yami Cueto
- GitHub: @YamiCueto
- LinkedIn: yamicueto
📄 Licencia
MIT License - Ver LICENSE para más detalles.
🙏 Agradecimientos
- Basado en estándares ISDA y NASD para convenciones de conteo de días
- Inspirado en librerías financieras como QuantLib y Apache Commons Math
- Especificaciones de Bond Market Association
📚 Referencias
- ISDA Day Count Conventions
- Bond Market Association Standards
- 30/360 Day Count Convention - Wikipedia
- Colombian Labor Code - Article 127 (Cálculo de cesantías)
🔒 Seguridad
Si encuentras vulnerabilidades de seguridad, por favor envía un email a [email protected] en lugar de abrir un issue público.
⚡ Performance
Esta librería es extremadamente eficiente:
- 0 dependencias externas (solo devDependencies para testing)
- < 5KB minificado
- O(1) complejidad para cálculos individuales
- Type-safe con TypeScript
- Tree-shakeable para bundlers modernos
¿Preguntas? ¿Sugerencias?
Abre un issue o un discussion
Hecho con ❤️ para desarrolladores fintech y sistemas bancarios
