Era japonesa, festivos y fechas de cierre en aplicaciones empresariales: diseño resiliente a los cambios de era, JapaneseCalendar y cálculo de días hábiles en la práctica

· · C#, .NET, Era japonesa (wareki), Festivos, Fecha de cierre, Tratamiento de fechas, Aplicaciones empresariales, Desarrollo Windows, Consultoría técnica

«La fecha del informe debe estar en la era japonesa.» «El pago vence al cerrar el día 20, pagadero a fin del mes siguiente, o el día hábil anterior si esa fecha cae en festivo.» «Los totales deben agregarse por ejercicio fiscal.» Las aplicaciones empresariales en Japón vienen de serie con requisitos de fecha que no tienen sentido en ningún otro lugar del mundo. Y este terreno esconde un tipo de trampa distinto al del tratamiento habitual de fechas y horas. Los nombres de era se multiplican con cada transición, los festivos se mueven con cada reforma legal, y «fin de mes» tiene una longitud distinta según el mes. Dicho de otro modo, todo ello son «especificaciones que cambian más adelante».

La vez pasada, en «Fecha, hora y zonas horarias en aplicaciones empresariales», establecimos el principio de almacenamiento en UTC y el tratamiento de las zonas horarias. Esta vez, como continuación, abordamos los tres grandes requisitos propios de Japón: la era japonesa, los festivos y las fechas de cierre. Repasaremos el diseño resiliente a los cambios de era, por qué nunca hay que codificar los festivos de forma fija y cómo guardarlos como datos, y el cálculo de fechas de cierre teniendo en cuenta el redondeo de AddMonths, todo con ejemplos de código y tablas de decisión.

1. Lo esencial, primero

  • El procesamiento interno y el almacenamiento permanecen en el calendario gregoriano; la era japonesa existe solo en el momento de la visualización. Guardar cadenas en era japonesa provoca una triple penalización: imposibilidad de ordenar, mezcla de notaciones tras un cambio de era y riesgo de que se cuelen fechas inválidas.
  • Delegue la conversión de era en JapaneseCalendar en lugar de una tabla de correspondencia propia. Las definiciones de era las proporciona el sistema operativo, así que basta una actualización de la plataforma para seguir un cambio de era.1 Solo quedan rezagadas las cadenas «Heisei» o «Reiwa» codificadas de forma fija que hayan quedado en la aplicación.
  • Los festivos no se pueden determinar con una fórmula. Los equinoccios de primavera y otoño se fijan el año anterior, y los propios festivos pueden desplazarse por una reforma legal ordinaria o por una ley de medidas especiales.2 Diséñelo en torno a una tabla maestra de festivos más un proceso de actualización, y como fuente de datos puede usar el CSV de festivos publicado por el Cabinet Office de Japón.
  • «Fin de mes», «día hábil» y «ejercicio fiscal» deben definirse en la especificación antes que en el código. En particular, AddMonths redondea automáticamente el fin de mes, así que el cálculo de fechas de cierre debe partir siempre de una fecha de referencia fija.3

Primero, un resumen de cómo guardar cada tipo de dato.

Dato Forma de almacenamiento Momento de conversión/evaluación
La fecha en sí (fecha de pedido, vencimiento de pago) DateOnly / DateTime en gregoriano (columna date en la BD)
Notación en era japonesa (informes, pantallas) No almacenarla Conversión con JapaneseCalendar solo en el momento de la visualización/impresión
Festivos Tabla maestra de festivos (tabla o archivo de configuración) Consulta en el momento del cálculo de días hábiles
Cierres de empresa (vacaciones de verano, aniversario de fundación) Tabla de cierres separada de la de festivos Igual que arriba (no mezclar con los festivos)
Fecha de cierre / condiciones de pago Atributo de la ficha del cliente/proveedor («cierre el día 20, pago a fin del mes siguiente») Cálculo a partir de la fecha de referencia al facturar
Ejercicio fiscal No almacenarlo (valor derivado) Cálculo a partir de la fecha en el momento de la visualización/agregación

2. La era japonesa: que sea solo una cuestión de visualización

2.1 Por qué unificar internamente en el calendario gregoriano

La era japonesa es una notación orientada a las personas, no una representación apta para el cálculo. «Heisei 9» y «Reiwa 9» no tienen un orden definido en la comparación de cadenas, y cualquier cálculo de duración que cruce una frontera de era acaba necesitando de todos modos la conversión al calendario gregoriano. Peor aún, el sistema de eras crece con cada transición, de modo que los datos que permanecen almacenados en era japonesa acumulan una mezcla de notaciones antiguas y nuevas en cada cambio. La infiltración de datos inválidos como «Showa 64, 10 de enero» (fecha inexistente, ya que a partir del 8 de enero era Heisei) es un problema clásico de los sistemas que guardan tal cual la era introducida en bruto.

Hay otra restricción que suele pasar desapercibida: JapaneseCalendar de .NET solo admite fechas a partir del 8 de septiembre de la era Meiji 1 (1868).4 Las anteriores —por ejemplo, una fecha de nacimiento antigua procedente de un registro familiar— no pueden tratarse correctamente si se mantienen en notación de era. Al unificar internamente sobre el calendario gregoriano, todos estos problemas quedan confinados a la sola capa de visualización.

2.2 Visualización y entrada con JapaneseCalendar

Las únicas clases de calendario de .NET capaces de manejar varias eras son JapaneseCalendar (y JapaneseLunisolarCalendar), y el especificador de formato g corresponde al nombre de la era.1 Este es el código mínimo para mostrar una fecha en era japonesa.

using System.Globalization;

var jaJpWareki = new CultureInfo("ja-JP");
jaJpWareki.DateTimeFormat.Calendar = new JapaneseCalendar();

var date = new DateOnly(2026, 7, 11);
Console.WriteLine(date.ToString("ggy年M月d日", jaJpWareki));  // 令和8年7月11日
Console.WriteLine(date.ToString("ggyy/MM/dd", jaJpWareki));   // 令和08/07/11

Para aceptar entrada en era japonesa, es más seguro diseñar una interfaz en la que el usuario elija la era en un desplegable y escriba el año, el mes y el día como números, en lugar de analizar una cadena de texto libre. Si es imprescindible aceptar texto libre, fije el formato con ParseExact.

var input = "令和8年7月11日";
var parsed = DateTime.ParseExact(input, "ggy年M月d日", jaJpWareki);
Console.WriteLine(parsed.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture));  // 2026-07-11

La notación del primer año de una era requiere atención. En entornos donde se han aplicado las actualizaciones de compatibilidad con eras, el primer año de cada era se formatea por defecto como «Reiwa Gan-nen» (元年, literalmente «año fundacional») en lugar de como número, y existe un conmutador de compatibilidad —Switch.System.Globalization.FormatJapaneseFirstYearAsANumber— para volver a la notación numérica «Reiwa 1».5 Confirme antes de implementar, como parte de la especificación, si el formato del informe espera «Reiwa 1» o «Reiwa Gan-nen». Otro punto: por defecto, la comprobación del rango de años de cada era está relajada, de modo que un valor inexistente como «Heisei 33» se acepta y se convierte a la fecha real correspondiente (Reiwa 3). Si necesita una validación estricta que rechace ese tipo de valores, puede forzarla con Switch.System.Globalization.EnforceJapaneseEraYearRanges.5 Dejarlo relajado o hacerlo estricto dependerá de cuánto «ruido» tengan los datos existentes, en particular de cómo quiera tratar un valor como «Heisei 32» impreso o introducido antes del cambio de era.

2.3 Código resiliente a los cambios de era, y lo que queda rezagado

Lo que se aprende de la experiencia del cambio a la era Reiwa (2019) es que la plataforma sigue el ritmo, pero la implementación propia de la aplicación no. La definición de los nombres de era no está codificada de forma fija en .NET: el mecanismo hace referencia a la información de era que mantiene el sistema operativo, de modo que, mientras se sigan aplicando las actualizaciones del sistema operativo y de .NET, la conversión y el formateo de JapaneseCalendar siguen automáticamente la nueva era.1 Lo que queda rezagado son activos del lado de la aplicación como los siguientes:

  • Tablas o ramas de conversión de era propias, como if (year >= 2019) era = "令和";
  • Nombres de era escritos directamente en plantillas de informes, formatos de celda de Excel o formularios preimpresos
  • Código que trata mediante reglas propias una abreviatura de era de un solo carácter como «R» o «H» (frecuente en formatos de intercambio con sistemas centrales)
  • Los propios datos almacenados, que siguen en notación de era japonesa

Al presupuestar una remediación por cambio de era, empiece por recorrer mecánicamente todo el código fuente y las plantillas con grep buscando «Showa», «Heisei», «Reiwa» y «Gan-nen». Las aplicaciones empresariales que llevan funcionando desde la época de VB6 o Access casi con toda seguridad tienen enterrada en algún sitio una tabla de conversión propia, por lo que este asunto suele abordarse junto con la decisión de continuidad descrita en «Prolongar la vida útil de una aplicación empresarial VB6/Access, o migrarla».

2.4 Un enemigo oculto: la configuración regional de Windows

Otra trampa fácil de pasar por alto. En la configuración regional de Windows, el usuario puede cambiar el tipo de calendario del sistema operativo a la era japonesa. Como CultureInfo.CurrentCulture refleja por defecto el cambio de configuración del usuario (una «anulación del usuario»),6 en un equipo con este ajuste, un DateTime.ToString() con el formato por defecto de la cultura empieza de pronto a devolver cadenas en era japonesa como «令和08/07/11». Si el código escribe fechas en registros, CSV o la base de datos usando el ToString() por defecto de la cultura, entonces solo en ese equipo la era japonesa se cuela en los datos almacenados, y el análisis posterior se rompe.

La solución vuelve a la misma conclusión que en el artículo anterior: cualquier cadena de fecha que atraviese una frontera —almacenamiento, comunicación, registro— debe escribirse siempre con CultureInfo.InvariantCulture y un formato explícito. El formato por defecto de la cultura está reservado a la visualización en pantalla. Para una visión más amplia del impacto de la cultura en la visualización, vea también «Localización de aplicaciones WinForms/WPF».

3. Los festivos: por qué nunca codificarlos de forma fija, y cómo gestionar una tabla maestra

3.1 Los festivos «cambian»

No escriba nunca la detección de festivos como un switch (mes, día). Los festivos están definidos por la Ley sobre los Días Festivos Nacionales, y han cambiado repetidamente por reformas legales y por la práctica administrativa.2 Solo con los ejemplos recientes:

Año Cambio
2016 Se crea el Día de la Montaña (11 de agosto)
2019 Tras el cambio de era, el cumpleaños del emperador pasa del 23 de diciembre al 23 de febrero. En 2019 no hubo ningún cumpleaños del emperador
2019 Bajo un régimen especial ligado a la sucesión imperial, el 1 de mayo (día de la entronización) y otras fechas se convierten en festivos excepcionales
2020 El Día del Deporte y la Salud pasa a llamarse Día del Deporte
2020-2021 Por una ley de medidas especiales ligada a los Juegos Olímpicos, el Día del Mar, el Día del Deporte y el Día de la Montaña se desplazan solo esos años

Además, el Día del Equinoccio de Primavera y el del Equinoccio de Otoño simplemente no tienen fecha inscrita en la ley. Se fijan y se anuncian el año anterior en función de cálculos astronómicos, así que la única forma fiable de conocer un equinoccio futuro es esperar el anuncio oficial.2 Una fórmula de cálculo que suponga que «los festivos son iguales cada año» es, en principio, inviable.

3.2 Tabla de decisión para fuentes de datos y forma de almacenamiento

Diseñe la determinación de festivos como «datos más un proceso de actualización». Comparación de las principales opciones:

Enfoque Contenido Casos adecuados Puntos a vigilar
Tabla maestra de festivos (gestión propia) Tabla con fecha y nombre, actualizada mediante una pantalla de administración Sistemas empresariales en general que quieran gestionarla junto con los cierres de la empresa Integrar la actualización en el proceso operativo (convertir la revisión anual en un recordatorio de calendario)
Importación del CSV del Cabinet Office Importar el CSV de festivos publicado por el Cabinet Office para actualizar la tabla Cuando se desea una fuente primaria autorizada para las actualizaciones de la tabla (recomendado) Solo contiene el periodo ya publicado. Cuidado con la codificación de caracteres
Biblioteca de festivos (NuGet, etc.) Determinación mediante lógica de cálculo más datos incluidos Usos a pequeña escala, como herramientas internas que no pueden sostener un proceso de actualización Las futuras reformas legales dependen de que se actualice la biblioteca. Hay que verificar las reglas incluidas
Codificación fija Fechas escritas directamente en el código fuente Ninguno Cada reforma legal obliga a redistribuir a todos los clientes

Como fuente primaria, puede usarse el CSV de fechas y nombres de festivos desde Showa 30 (1955) hasta el año siguiente, publicado por el Cabinet Office en su página «Días Festivos Nacionales».2 Hay dos precauciones prácticas al importarlo: (1) como solo se publica el periodo ya confirmado, decida como parte de la especificación cómo comportarse cuando se consulte un festivo de dentro de dos años o más (devolver un error, o rellenar con un valor provisional basado en la ley y una marca de «provisional»); y (2) la codificación de caracteres de este CSV pertenece a la familia Shift_JIS, así que hay que especificar la codificación explícitamente al leerlo, como se explica en «El CSV no es simplemente «texto plano»».

3.3 Festivo sustitutivo, festivo nacional y cálculo de días hábiles

Más allá de la lista de fechas, la ley de festivos define dos reglas derivadas: el festivo sustitutivo (cuando un festivo cae en domingo, el día siguiente más próximo que no sea a su vez festivo se convierte en día no laborable) y el festivo nacional (un día laborable atrapado entre dos festivos se convierte en día no laborable).2 El CSV del Cabinet Office los incluye también como filas, así que con el enfoque de importación por CSV basta con consultar la tabla maestra. Aunque complete esta lógica usted mismo, es relativamente estable por derivarse de la ley.

Escriba el cálculo de días hábiles con tres condiciones: que no sea sábado o domingo, que no esté en la tabla maestra de festivos y que no esté en la tabla de cierres de la empresa.

public sealed class BusinessDayCalendar
{
    private readonly HashSet<DateOnly> _publicHolidays;   // Maestro de días festivos (incluye días festivos sustitutos)
    private readonly HashSet<DateOnly> _companyHolidays;  // Maestro de días no laborables de la empresa

    public BusinessDayCalendar(IEnumerable<DateOnly> publicHolidays,
                               IEnumerable<DateOnly> companyHolidays)
    {
        _publicHolidays = [.. publicHolidays];
        _companyHolidays = [.. companyHolidays];
    }

    public bool IsBusinessDay(DateOnly d) =>
        d.DayOfWeek is not (DayOfWeek.Saturday or DayOfWeek.Sunday)
        && !_publicHolidays.Contains(d)
        && !_companyHolidays.Contains(d);

    // Si el vencimiento cae en festivo, se adelanta al día hábil anterior. Si se pospone en su lugar depende de las condiciones del contrato
    public DateOnly ToPreviousBusinessDay(DateOnly d)
    {
        while (!IsBusinessDay(d)) d = d.AddDays(-1);
        return d;
    }
}

Mantener los festivos y los cierres de la empresa en tablas separadas es deliberado. «El vencimiento de una transferencia bancaria solo mira los festivos oficiales (nuestras vacaciones de verano no cuentan)», «la fecha de envío mira ambos» — el conjunto de días no laborables que aplica depende del caso de uso. Si los mezcla en una sola tabla, separarlos más adelante se convierte en un trabajo de arqueología sobre datos ya almacenados.

4. Fecha de cierre, condiciones de pago y ejercicio fiscal: tratar correctamente el «fin de mes» en el cálculo

4.1 El comportamiento de redondeo de AddMonths

La primera trampa del cálculo de fechas de cierre es AddMonths. DateTime.AddMonths / DateOnly.AddMonths redondean al último día del mes resultante cuando el cálculo ingenuo daría un día que no existe en ese mes.3

var jan31 = new DateOnly(2026, 1, 31);
var feb = jan31.AddMonths(1);   // 2026-02-28 ── redondeado al fin de mes (correcto)
var mar = feb.AddMonths(1);     // 2026-03-28 ── lo que se pretendía como «fin de mes» se convierte silenciosamente en el día 28

El propio redondeo está correctamente especificado, pero encadenar otro AddMonths sobre un resultado ya redondeado hace perder la intención de «fin de mes». Esto se manifiesta en la práctica cuando un calendario de facturación mensual se construye como «fecha de la última ejecución más un mes»: en cuanto se atraviesa febrero, el cálculo empieza a derivar permanentemente hacia el día 28 de cada mes. Hay dos contramedidas:

  • Para fechas repetidas, calcular siempre a partir de la definición del punto de referencia («fin de cada mes», «el día 20 de cada mes»), no a partir del resultado anterior
  • Derivar «fin de mes» a partir de DateTime.DaysInMonth(y, m) en lugar de un literal de fecha

4.2 Implementación de «cierre el día 20, pago a fin del mes siguiente»

La fecha de cierre y las condiciones de pago son atributos propios de cada contraparte comercial. Codificar «cierre el día 20, pago a fin del mes siguiente» queda así:

public static class PaymentTerms
{
    // Fecha de operación → fecha de cierre (ejemplo: cierre el día 20 de cada mes)
    public static DateOnly GetClosingDate(DateOnly tradeDate, int closingDay)
    {
        var dayInMonth = Math.Min(closingDay, DateTime.DaysInMonth(tradeDate.Year, tradeDate.Month));
        var closing = new DateOnly(tradeDate.Year, tradeDate.Month, dayInMonth);
        return tradeDate <= closing ? closing : closing.AddMonths(1);
        // Nota: closingDay=31 (cierre de fin de mes) se convierte correctamente en el fin del mes siguiente gracias al redondeo de AddMonths
    }

    // Fecha de cierre → fecha de vencimiento del pago (ejemplo: pago a fin del mes siguiente). Verificar la especificación de ajuste por festivos (adelantar/posponer) antes de aplicarla
    public static DateOnly GetPaymentDueDate(DateOnly closingDate, BusinessDayCalendar calendar)
    {
        var nextMonth = closingDate.AddMonths(1);
        var endOfMonth = new DateOnly(nextMonth.Year, nextMonth.Month,
                                      DateTime.DaysInMonth(nextMonth.Year, nextMonth.Month));
        return calendar.ToPreviousBusinessDay(endOfMonth);  // Para contratos que usan la regla «si es festivo, adelantar al día hábil anterior»
    }
}

Más importante que el código es fijar de antemano la especificación. En un «cierre a fin de mes», ¿el fin de mes se cuenta en días naturales o en días hábiles? Si el vencimiento cae en festivo, ¿se adelanta o se retrasa (quien paga tiende a adelantarlo, quien recibe tiende a retrasar su fecha de cobro prevista; la práctica varía según el punto de vista)? ¿Cómo se interpreta en febrero un contrato con cierre entre el día 29 y el 31? Mientras esto no esté documentado, el código de quien lo implemente se convierte, tal cual, en «una especificación que nadie ha acordado». La mayoría de los bugs relacionados con fechas de cierre, al investigarlos, no llevan a un error de código, sino a la ausencia de una especificación.

4.3 Ejercicio fiscal y trimestre

En Japón, el «ejercicio fiscal» de una aplicación empresarial casi siempre empieza en abril. No lo almacene: trátelo como un valor derivado de la fecha.

public static int GetFiscalYear(DateOnly d) => d.Month >= 4 ? d.Year : d.Year - 1;
// 2026-03-31 → ejercicio fiscal 2025, 2026-04-01 → ejercicio fiscal 2026

public static int GetFiscalQuarter(DateOnly d) => ((d.Month + 8) % 12) / 3 + 1;
// abr-jun → T1, jul-sep → T2, oct-dic → T3, ene-mar → T4

Tenga cuidado de no almacenar de forma redundante el ejercicio fiscal en la base de datos sin necesidad. Guardar en una columna aparte un valor que siempre puede derivarse de la fecha crea el riesgo de que, al corregir una fecha y olvidar actualizarlo, la fecha y el ejercicio fiscal acaben en desacuerdo. Si por rendimiento de agregación es imprescindible mantenerlo, conviértalo en una columna calculada (generada) y prohíba la escritura directa desde la aplicación.

4.4 Pruebas: adelantarse a los bugs que solo actúan «ese día concreto»

Los bugs relacionados con la era, los festivos y las fechas de cierre solo se manifiestan en fechas concretas. Apoyándose en la inyección de la hora actual mediante TimeProvider presentada en el artículo anterior (capítulo 7 de «Fecha, hora y zonas horarias en aplicaciones empresariales»), estas son las cinco fechas que conviene probar, como mínimo, en este ámbito:

  • Fronteras de cambio de era: visualización y conversión inversa de una entrada en era japonesa en torno al 1989-01-07/08 (Showa → Heisei) y al 2019-04-30/05-01 (Heisei → Reiwa)
  • Primer año de una era: comprobación de la notación «Reiwa Gan-nen» en los informes (apartado 2.2)
  • Años bisiestos: cálculos de cierre y vencimiento de pago que abarquen el 29 de febrero (2024, 2028)
  • Encadenamiento de fines de mes: generación de doce meses de fechas de cierre a partir del 31 de enero, comprobando que se preserva el fin de mes (apartado 4.1)
  • Fronteras de ejercicio fiscal: determinación del ejercicio fiscal y del trimestre para el 31 de marzo y el 1 de abril

Además, en la revisión de código, los siguientes patrones pueden detectarse mecánicamente con grep.

Código encontrado, sospechoso Qué puede ocurrir Cómo corregirlo
Literales de era como "平成" o "令和" Queda rezagado en el próximo cambio de era Migrar a JapaneseCalendar más el formato g (apartado 2.3)
Literales de fechas festivas (como 5, 3) Determinación errónea tras una reforma legal o una ley de medidas especiales Pasar a consultar la tabla maestra de festivos (apartado 3.2)
Ruta de guardado/salida que usa ToString() sin argumentos La era japonesa se cuela en los datos almacenados en un equipo configurado para mostrar la era InvariantCulture más un formato explícito (apartado 2.4)
Repetición de fechaAnterior.AddMonths(1) El fin de mes sigue derivando tras atravesar febrero Calcular cada vez a partir de una fecha de referencia (apartado 4.1)
new DateTime(y, m, 31) Lanza una excepción en meses de 30 días o menos Derivarlo de DateTime.DaysInMonth

5. Conclusión

La era japonesa, los festivos y las fechas de cierre tienen algo en común: todos ellos tratan con «especificaciones que cambian más adelante». Por eso el principio de diseño es el mismo en los tres casos: no escribir en el código lo que puede cambiar, sino aislarlo en los datos y en una capa de conversión. Unifique el procesamiento interno y el almacenamiento en el calendario gregoriano, dejando la era japonesa como una conversión únicamente de visualización; mantenga los festivos en una tabla maestra con proceso de actualización; y documente «fin de mes», «día hábil» y «ajuste del vencimiento cuando cae en festivo» como especificación contractual antes de calcularlos a partir de una fecha de referencia. Haga todo esto y el próximo cambio de era, así como la próxima reforma de la ley de festivos, se convertirán, para su aplicación, en eventos que se resuelven con «actualizar la tabla maestra» y «aplicar la actualización del sistema operativo».

En nuestra empresa nos encargamos del diseño e implementación del tratamiento de fechas en torno a informes y facturación, del inventario y la reforma de nombres de era y festivos codificados de forma fija en aplicaciones existentes, y de revisiones de diseño que incluyen aclarar la especificación de las fechas de cierre. Podemos ayudar incluso desde la fase en la que aún no sabe cuántos puntos de su código necesitan remediación por cambio de era.

Artículos relacionados

Áreas de consultoría relacionadas

En KomuraSoft LLC (合同会社小村ソフト) nos encargamos del diseño e implementación del tratamiento de fechas para aplicaciones empresariales acordes con las costumbres comerciales japonesas (informes en era japonesa, cálculo de días hábiles, tratamiento de fechas de cierre), así como del inventario y la reforma del tratamiento de cambios de era y festivos en aplicaciones existentes.

Referencias

  1. Microsoft Learn, Work with eras - .NET. Sobre el hecho de que JapaneseCalendar y JapaneseLunisolarCalendar son las únicas clases de calendario que reconocen varias eras, el formateo de la era mediante el especificador de formato «g», y que la información de era la proporciona la plataforma en lugar de depender de codificación fija en la aplicación.  2 3

  2. Cabinet Office de Japón, «Sobre los Días Festivos Nacionales». Sobre la lista de festivos según la Ley sobre los Días Festivos Nacionales, el hecho de que los equinoccios de primavera y otoño se fijan y se anuncian el año anterior, las disposiciones sobre el festivo sustitutivo y el festivo nacional, y la disponibilidad de un CSV de fechas y nombres de festivos desde Showa 30 hasta el año siguiente.  2 3 4 5

  3. Microsoft Learn, DateTime.AddMonths(Int32) Method. Sobre el hecho de que, cuando el día resultante supera el número de días del mes resultante, el resultado se ajusta al último día de ese mes (por ejemplo, el 31 de enero más un mes se convierte en el 28/29 de febrero).  2

  4. Microsoft Learn, JapaneseCalendar Class. Sobre el rango de fechas admitido por JapaneseCalendar —a partir del 8 de septiembre de la era Meiji 1 (1868 d. C.)— y el tratamiento de las eras. 

  5. Microsoft Learn, Work with calendars - .NET. Sobre el comportamiento por defecto de formatear el primer año de una era como «Gan-nen», el conmutador Switch.System.Globalization.FormatJapaneseFirstYearAsANumber que restaura la notación numérica, y que la comprobación del rango de años de una era está relajada por defecto, pero puede hacerse estricta con Switch.System.Globalization.EnforceJapaneseEraYearRanges.  2

  6. Microsoft Learn, CultureInfo.CurrentCulture Property. Sobre el hecho de que CurrentCulture refleja por defecto los cambios de configuración regional de un usuario de Windows (una anulación de usuario), y que el formateo por defecto depende de esta cultura. 

Artículos recientes con las mismas etiquetas para profundizar en temas cercanos.

Estas páginas sitúan el tema en un contexto más amplio de servicios y decisiones.

El artículo está directamente relacionado con los siguientes servicios.

Desarrollo de aplicaciones para Windows

Implementar requisitos empresariales propios de Japón, como la visualización de fechas en la era japonesa en los informes, el cálculo de días hábiles y el tratamiento de fechas de cierre, forma parte del núcleo de la consultoría de desarrollo de aplicaciones empresariales Windows.

Preguntas frecuentes

Preguntas habituales en las consultas sobre el tema del artículo.

¿Está mal guardar las fechas directamente en formato de era japonesa en la base de datos?
No lo recomendamos, por cuatro razones. Primero, cadenas de era como «Heisei 9» y «Reiwa 9» pierden todo orden coherente al compararse como texto en cuanto se cruza una frontera de era, lo que hace imposible ordenar o hacer búsquedas por rango. Segundo, en el momento en que llega el siguiente cambio de era, las notaciones antiguas y nuevas acaban mezcladas en los datos ya almacenados. Tercero, es fácil que se cuelen datos corruptos con fechas que nunca existieron, como «Showa 64, 10 de enero» (ese día ya era Heisei 1, puesto que la era cambió el 8 de enero), convirtiéndose en una bomba de tiempo para el análisis. Cuarto, JapaneseCalendar de .NET solo admite fechas desde el 8 de septiembre de la era Meiji 1 (1868); las anteriores —por ejemplo, una fecha de nacimiento antigua procedente de un registro familiar— no pueden tratarse correctamente si se mantienen en notación de era. La norma es unificar el procesamiento interno y el almacenamiento en el calendario gregoriano, y convertir a la era japonesa únicamente en el momento de la visualización o la impresión.
¿Cómo debería prepararse una aplicación para el próximo cambio de era?
La definición de los nombres de era no está codificada de forma fija en el propio .NET: el mecanismo hace referencia a la información de era que proporciona el sistema operativo, de modo que basta con seguir aplicando las actualizaciones del sistema operativo y de .NET para que la capacidad de «mostrar la nueva era» se siga automáticamente. En la transición a Reiwa, de hecho, el grueso de la respuesta consistió simplemente en aplicar actualizaciones de la plataforma. El verdadero problema está en el código y los datos del lado de la aplicación: ramas que codifican de forma fija «Heisei» o «Reiwa» como cadenas de texto, funciones de conversión propias con su propia tabla de correspondencia entre era y año gregoriano, nombres de era escritos directamente en las plantillas de los informes, o campos «Reiwa __» en formularios preimpresos; nada de esto se actualiza automáticamente. Cuatro medidas prácticas resultan razonables: (1) unificar internamente y en el almacenamiento sobre el calendario gregoriano, de modo que la era japonesa quede reservada a la visualización; (2) dejar de mantener una lógica de conversión de era propia y delegarla en JapaneseCalendar; (3) recorrer todo el código fuente con grep buscando «Showa», «Heisei» y «Reiwa» para hacer inventario; y (4) preparar pruebas que simulen un cambio de era hipotético.
¿No se pueden determinar los festivos con una fórmula de cálculo? ¿Por qué es necesario guardarlos como datos?
Una fórmula completa es, en principio, imposible de construir, por tres razones. Primero, el Día del Equinoccio de Primavera y el del Equinoccio de Otoño se fijan el año anterior mediante decisión del gabinete basada en cálculos astronómicos; no están recogidos en la ley como fechas fijas. Segundo, los festivos se añaden, se mueven o se renombran mediante reformas legales: en 2016 se creó el Día de la Montaña, en 2019 el cumpleaños del emperador pasó del 23 de diciembre al 23 de febrero a raíz del cambio de era (**en 2019 no hubo cumpleaños del emperador**), y el Día del Deporte y la Salud pasó a llamarse Día del Deporte. Tercero, en 2020 y 2021, una ley de medidas especiales ligada a los Juegos Olímpicos desplazó el Día del Mar, el Día del Deporte y el Día de la Montaña **solo para esos años**, lo que demuestra que incluso una regla como «siempre el tercer lunes» puede quedar anulada de forma puntual. Por tanto, el diseño correcto es una «tabla maestra de festivos más un proceso de actualización», dejando la lógica de cálculo limitada a reglas auxiliares como el festivo sustitutivo.
Mi fecha de vencimiento de pago se desplazó del fin de mes después de usar AddMonths(1). ¿Por qué?
Porque DateTime.AddMonths está especificado para redondear al último día del mes resultante cuando el cálculo ingenuo daría un día que no existe en ese mes. Aplicar AddMonths(1) al 31 de enero da el 28 de febrero (o el 29 en años bisiestos); ese comportamiento en sí es correcto. Pero si después arrastra ese resultado redondeado a un nuevo AddMonths, aplicar AddMonths(1) al 28 de febrero da el 28 de marzo, y a partir de ahí el desplazamiento continúa de forma indefinida: lo que debía ser «fin de mes» se convierte silenciosa y permanentemente en «el día 28». La solución tiene dos partes: para cálculos repetidos, calcular siempre a partir de un punto de referencia fijo (la fecha del contrato, o la definición de «fin de cada mes»), en lugar de sumar al resultado anterior, y derivar «fin de mes» a partir de DateTime.DaysInMonth en lugar de un literal de fecha.

Perfil del autor

Página de presentación del autor del artículo.

Go Komura

Representante de KomuraSoft LLC

Especializado en desarrollo de software para Windows, consultoría técnica e investigación de fallos, sobre todo en proyectos con sistemas existentes y errores difíciles de reproducir.

Volver al blog