Ère japonaise, jours fériés et dates de clôture dans les applications métier — conception résiliente aux changements d'ère, JapaneseCalendar et calcul des jours ouvrés en pratique

· · C#, .NET, Ère japonaise (wareki), Jours fériés, Date de clôture, Traitement des dates, Applications métier, Développement Windows, Conseil technique

« La date du bordereau doit être en ère japonaise. » « Le paiement est dû le 20 du mois, réglé fin du mois suivant, ou le jour ouvré précédent si cette date tombe un jour férié. » « Les totaux doivent être agrégés par exercice fiscal. » Les applications métier au Japon embarquent en standard des exigences de date qui n’ont de sens nulle part ailleurs dans le monde. Et ce territoire recèle un tout autre type de piège que le traitement classique des dates et heures. Les noms d’ère se multiplient à chaque changement, les jours fériés bougent au gré des révisions légales, et « la fin du mois » n’a pas la même longueur selon le mois. Autrement dit, tout cela relève de « spécifications qui changent plus tard ».

La dernière fois, dans « Date, heure et fuseaux horaires dans les applications métier », nous avons posé le principe du stockage en UTC et la gestion des fuseaux horaires. Cette fois, en guise de suite, nous abordons les trois grandes exigences propres au Japon : l’ère japonaise, les jours fériés et les dates de clôture. Nous verrons la conception résiliente aux changements d’ère, les raisons de ne jamais coder en dur les jours fériés et comment les stocker sous forme de données, ainsi que le calcul des dates de clôture en tenant compte de l’arrondi de AddMonths — le tout avec exemples de code et tableaux de décision.

1. L’essentiel d’abord

  • Le traitement interne et le stockage restent en calendrier grégorien ; l’ère japonaise n’existe qu’au moment de l’affichage. Stocker des chaînes en ère japonaise entraîne une triple peine : impossibilité de trier, mélange des notations après un changement d’ère, et risque d’infiltration de dates invalides.
  • Déléguez la conversion d’ère à JapaneseCalendar plutôt qu’à une table de correspondance maison. Les définitions d’ère sont fournies par le système d’exploitation, si bien qu’une mise à jour de la plateforme suffit à suivre un changement d’ère.1 Seules les chaînes codées en dur « Heisei » ou « Reiwa » laissées dans l’application restent à la traîne.
  • Les jours fériés ne peuvent pas être déterminés par une formule. Les équinoxes de printemps et d’automne sont fixés l’année précédente, et les jours fériés eux-mêmes peuvent être déplacés par une révision légale ordinaire ou une loi de mesures spéciales.2 Concevez le système autour d’une table de référence des jours fériés + un processus de mise à jour, en vous appuyant si possible sur le CSV des jours fériés publié par le Cabinet Office japonais comme source de données.
  • « Fin de mois », « jour ouvré » et « exercice fiscal » doivent être définis dans le cahier des charges avant de l’être dans le code. AddMonths, en particulier, arrondit automatiquement la fin de mois : le calcul des dates de clôture doit donc toujours partir d’une date de référence fixe.3

Voici d’abord un résumé de la manière de stocker chaque type de donnée.

Donnée Mode de stockage Moment de conversion/évaluation
La date elle-même (date de commande, échéance de paiement) DateOnly / DateTime en grégorien (colonne date en base)
Notation en ère japonaise (bordereaux, écrans) Ne pas la stocker Conversion via JapaneseCalendar uniquement au moment de l’affichage/de l’impression
Jours fériés Table de référence des jours fériés (table ou fichier de configuration) Consultée lors du calcul des jours ouvrés
Jours de fermeture de l’entreprise (congés d’été, anniversaire de fondation) Table de référence distincte de celle des jours fériés Idem (ne pas la mélanger avec les jours fériés)
Date de clôture / conditions de paiement Attribut de la fiche client/fournisseur (« clôture le 20, paiement fin du mois suivant ») Calculée à partir d’une date de référence lors de la facturation
Exercice fiscal Ne pas la stocker (valeur dérivée) Calculée à partir de la date au moment de l’affichage/de l’agrégation

2. L’ère japonaise — n’en faire qu’un affichage

2.1 Pourquoi unifier le traitement interne sur le calendrier grégorien

L’ère japonaise est une notation destinée aux humains, pas une représentation adaptée au calcul. « Heisei 9 » et « Reiwa 9 » n’ont pas d’ordre défini en comparaison de chaînes, et tout calcul de durée franchissant une frontière d’ère finit de toute façon par nécessiter une conversion vers le calendrier grégorien. Pire, le système d’ères s’enrichit à chaque changement, si bien que des données restées stockées en ère japonaise accumulent un mélange d’anciennes et de nouvelles notations à chaque transition. L’infiltration de données invalides comme « Showa 64, 10 janvier » (une date qui n’a jamais existé, puisqu’on était déjà en Heisei à partir du 8 janvier) est un problème classique des systèmes qui stockent tel quel l’ère saisie brute.

Une autre contrainte souvent négligée : JapaneseCalendar de .NET ne prend en charge que les dates à partir du 8 septembre de l’ère Meiji 1 (1868).4 Les dates antérieures — par exemple une date de naissance ancienne issue d’un registre d’état civil — ne peuvent pas être traitées correctement tant qu’elles restent en notation d’ère. En unifiant le traitement interne sur le calendrier grégorien, tous ces problèmes se retrouvent confinés à la seule couche d’affichage.

2.2 Affichage et saisie avec JapaneseCalendar

Les seules classes de calendrier de .NET capables de gérer plusieurs ères sont JapaneseCalendar (et JapaneseLunisolarCalendar), et le spécificateur de format g correspond au nom de l’ère.1 Voici le code minimal pour afficher une date en ère japonaise.

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

Pour accepter une saisie en ère japonaise, il est plus sûr de concevoir une interface où l’utilisateur choisit l’ère dans une liste déroulante et saisit l’année, le mois et le jour sous forme numérique, plutôt que d’analyser une chaîne libre. Si vous devez absolument accepter du texte libre, fixez le format avec 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 notation de la première année d’une ère demande de l’attention. Sur les environnements où les mises à jour liées à la gestion des ères ont été appliquées, la première année de chaque ère est par défaut formatée comme « Reiwa Gan-nen » (元年, littéralement « année de fondation ») plutôt que sous forme numérique, et un commutateur de compatibilité — Switch.System.Globalization.FormatJapaneseFirstYearAsANumber — permet de revenir à la notation numérique « Reiwa 1 ».5 Vérifiez avant toute implémentation, en tant qu’élément du cahier des charges, si le modèle de bordereau attend « Reiwa 1 » ou « Reiwa Gan-nen ». Autre point : par défaut, la vérification de la plage d’années de chaque ère est assouplie, si bien qu’une valeur inexistante comme « Heisei 33 » est acceptée et convertie vers la date réelle correspondante (Reiwa 3). Si vous avez besoin d’une validation stricte qui rejette ce type de valeur, vous pouvez l’imposer avec Switch.System.Globalization.EnforceJapaneseEraYearRanges.5 Le choix entre assouplissement et rigueur dépendra du degré de « saleté » des données existantes — en particulier, de la manière dont vous voulez traiter une valeur comme « Heisei 32 » imprimée ou saisie avant le changement d’ère.

2.3 Un code résilient aux changements d’ère, et ce qui reste à la traîne

Ce qu’on retient de l’expérience du changement d’ère vers Reiwa (2019), c’est que la plateforme suit, mais l’implémentation maison de l’application ne suit pas. La définition des noms d’ère n’est pas codée en dur dans .NET : le mécanisme référence les informations d’ère détenues par le système d’exploitation, si bien qu’en continuant à appliquer les mises à jour de l’OS et de .NET, la conversion et le formatage de JapaneseCalendar suivent automatiquement la nouvelle ère.1 Ce qui reste à la traîne, ce sont des éléments côté application comme les suivants :

  • Des tables ou branches de conversion d’ère maison, du type if (year >= 2019) era = "令和";
  • Des noms d’ère écrits directement dans des modèles de bordereaux, des formats de cellules Excel ou des formulaires préimprimés
  • Du code qui traite selon des règles maison une abréviation d’ère sur un caractère comme « R » ou « H » (fréquent dans les formats d’échange avec les systèmes centraux)
  • Les données elles-mêmes, restées stockées en ère japonaise

Lors du chiffrage d’une remise en conformité pour un changement d’ère, commencez par passer mécaniquement au crible l’ensemble du code source et des modèles avec un grep sur « Showa », « Heisei », « Reiwa » et « Gan-nen ». Les applications métier fonctionnant depuis l’époque de VB6 ou d’Access contiennent presque toujours une table de conversion maison enfouie quelque part, ce qui rejoint souvent la décision de maintien en vie évoquée dans « Prolonger la durée de vie d’une application métier VB6 / Access, ou la migrer ».

2.4 Un piège discret : les paramètres régionaux de Windows

Un dernier piège facilement négligé. Dans les paramètres régionaux de Windows, l’utilisateur peut changer le type de calendrier de l’OS pour l’ère japonaise. Comme CultureInfo.CurrentCulture reflète par défaut le changement de paramètre de l’utilisateur (une « surcharge utilisateur »),6 sur un poste ayant ce réglage, un DateTime.ToString() par défaut de la culture se met soudain à renvoyer une chaîne en ère japonaise comme « 令和08/07/11 ». Si du code écrit des dates dans des journaux, des CSV ou une base de données via un ToString() par défaut de la culture, alors sur ce seul poste, l’ère japonaise s’infiltre dans les données stockées, ce qui casse l’analyse en aval.

La parade rejoint la même conclusion que dans l’article précédent : toute chaîne de date qui franchit une frontière (stockage, communication, journalisation) doit impérativement être écrite avec CultureInfo.InvariantCulture et un format explicite. Le format par défaut de la culture est réservé à l’affichage à l’écran. Pour une vue plus large de l’impact de la culture sur l’affichage, voir aussi « Localisation d’applications WinForms/WPF ».

3. Les jours fériés — pourquoi ne jamais les coder en dur, et comment gérer une table de référence

3.1 Les jours fériés « changent »

N’écrivez jamais la détection des jours fériés sous forme d’un switch (mois, jour). Les jours fériés sont définis par la loi sur les jours fériés nationaux, et ils ont changé à de nombreuses reprises au fil des révisions légales et des dispositions opérationnelles.2 Rien qu’avec les exemples récents :

Année Changement
2016 Création du Jour de la montagne (11 août)
2019 Suite au changement d’ère, l’anniversaire de l’Empereur passe du 23 décembre au 23 février. Il n’y a eu aucun anniversaire de l’Empereur en 2019
2019 Sous un régime spécial lié à la succession impériale, le 1er mai (jour de l’intronisation) et d’autres dates deviennent des jours fériés ou chômés exceptionnels
2020 Le Jour du sport et de la santé est renommé Jour du sport
2020-2021 En vertu d’une loi de mesures spéciales liée aux Jeux olympiques, le Jour de la mer, le Jour du sport et le Jour de la montagne sont déplacés pour ces seules années-là

De plus, le jour de l’équinoxe de printemps et celui de l’équinoxe d’automne n’ont tout simplement aucune date inscrite dans la loi. Ils sont fixés et publiés l’année précédente sur la base d’un calcul astronomique, si bien que le seul moyen fiable de connaître un futur équinoxe est d’attendre la publication officielle.2 Une formule de calcul qui postule que « les jours fériés sont les mêmes chaque année » est par principe irréalisable.

3.2 Tableau de décision pour les sources de données et le mode de stockage

Concevez la détermination des jours fériés comme « des données + un processus de mise à jour ». Voici une comparaison des principales options.

Approche Contenu Cas adaptés Points de vigilance
Table de référence des jours fériés (gestion maison) Une table avec date et libellé, mise à jour via un écran d’administration Systèmes métier en général souhaitant la gérer conjointement avec les jours de fermeture de l’entreprise Intégrer la mise à jour dans le processus opérationnel (transformer la vérification annuelle en rappel calendaire)
Import du CSV du Cabinet Office Importer le CSV des jours fériés publié par le Cabinet Office pour mettre à jour la table de référence Quand on souhaite une source primaire fiable pour les mises à jour de la table (recommandé) Ne contient que la période déjà publiée. Attention à l’encodage des caractères
Bibliothèque de jours fériés (NuGet, etc.) Détermination par logique de calcul + données embarquées Usages à petite échelle, comme des outils internes ne pouvant pas soutenir un processus de mise à jour Les futures révisions légales dépendent de la mise à jour de la bibliothèque. Il faut vérifier les règles embarquées
Codage en dur Dates écrites directement dans le code source Aucun Chaque révision légale impose une redistribution à tous les postes clients

Comme source primaire, vous pouvez utiliser le CSV des dates et libellés des jours fériés depuis Showa 30 (1955) jusqu’à l’année suivante, publié par le Cabinet Office sur sa page « Jours fériés nationaux ».2 Deux précautions pratiques s’imposent lors de l’import : (1) seule la période déjà confirmée étant publiée, décidez dans le cahier des charges du comportement à adopter lorsqu’on interroge un jour férié au-delà de l’année suivante (renvoyer une erreur, ou combler avec une valeur provisoire fondée sur la loi et un indicateur « provisoire ») ; (2) l’encodage de ce CSV est de la famille Shift_JIS, il faut donc spécifier explicitement l’encodage à la lecture, comme expliqué dans « Le CSV n’est pas « juste du texte » ».

3.3 Jour férié de substitution, jour férié national et calcul des jours ouvrés

Au-delà de la liste des dates, la loi sur les jours fériés définit deux règles dérivées : le jour férié de substitution (lorsqu’un jour férié tombe un dimanche, le jour non férié le plus proche qui suit devient chômé) et le jour férié national (un jour de semaine coincé entre deux jours fériés devient chômé).2 Le CSV du Cabinet Office les inclut également sous forme de lignes, si bien qu’avec l’approche d’import CSV, la simple consultation de la table de référence suffit. Même si vous complétez cette logique vous-même, elle reste relativement stable puisqu’elle découle de la loi.

Écrivez le calcul des jours ouvrés avec trois conditions : ne pas être un samedi ou un dimanche, ne pas figurer dans la table des jours fériés, ne pas figurer dans la table des jours de fermeture de l’entreprise.

public sealed class BusinessDayCalendar
{
    private readonly HashSet<DateOnly> _publicHolidays;   // Table des jours fériés (y compris les jours de report)
    private readonly HashSet<DateOnly> _companyHolidays;  // Table des jours de fermeture de l'entreprise

    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 l'échéance tombe un jour férié, la ramener au jour ouvré précédent. Reporter en avant dépend des conditions contractuelles
    public DateOnly ToPreviousBusinessDay(DateOnly d)
    {
        while (!IsBusinessDay(d)) d = d.AddDays(-1);
        return d;
    }
}

Séparer les jours fériés et les jours de fermeture de l’entreprise en deux tables distinctes est un choix délibéré. « Le calcul de l’échéance d’un virement bancaire ne regarde que les jours fériés (notre fermeture estivale n’entre pas en ligne de compte) », « le calcul de la date d’expédition regarde les deux » — l’ensemble de jours chômés à consulter dépend de l’usage. Si vous les fusionnez dans une seule table, les séparer plus tard devient un travail d’archéologie sur les données déjà stockées.

4. Date de clôture, conditions de paiement, exercice fiscal — traiter correctement « la fin du mois » par le calcul

4.1 Le comportement d’arrondi d’AddMonths

Le premier piège du calcul des dates de clôture est AddMonths. DateTime.AddMonths / DateOnly.AddMonths arrondit au dernier jour du mois résultant lorsque le calcul naïf donnerait un jour qui n’existe pas dans ce mois.3

var jan31 = new DateOnly(2026, 1, 31);
var feb = jan31.AddMonths(1);   // 2026-02-28 ── arrondi à la fin du mois (correct)
var mar = feb.AddMonths(1);     // 2026-03-28 ── ce qui devait être « fin de mois » se transforme silencieusement en le 28

L’arrondi en lui-même est correctement spécifié, mais enchaîner un nouvel AddMonths sur un résultat déjà arrondi fait perdre l’intention de « fin de mois ». Cela se manifeste concrètement lorsqu’un calendrier de facturation mensuel est construit comme « date de la dernière exécution + un mois » : dès que février est franchi, le calcul se met à dériver définitivement vers une exécution le 28 de chaque mois. Deux parades s’imposent :

  • Pour une date répétée, toujours calculer à partir de la définition du point de référence (« fin de chaque mois », « le 20 de chaque mois ») plutôt qu’à partir du résultat précédent
  • Dériver « la fin du mois » à partir de DateTime.DaysInMonth(y, m) plutôt que d’un littéral de date

4.2 Implémentation de « clôture le 20, paiement fin du mois suivant »

La date de clôture et les conditions de paiement sont des attributs propres à chaque partenaire commercial. Coder « clôture le 20, paiement fin du mois suivant » donne ceci :

public static class PaymentTerms
{
    // Date de transaction → date de clôture (exemple : clôture le 20 de chaque mois)
    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);
        // Remarque : closingDay=31 (clôture en fin de mois) devient correctement la fin du mois suivant grâce à l'arrondi d'AddMonths
    }

    // Date de clôture → date d'échéance de paiement (exemple : paiement à la fin du mois suivant). Vérifier la spécification d'ajustement des jours fériés (avancer/reporter) avant de l'appliquer
    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);  // Pour les contrats utilisant la règle « si férié, ramener au jour ouvré précédent »
    }
}

Plus important que le code, il y a la définition préalable du cahier des charges. Pour une « clôture fin de mois », la fin de mois se compte-t-elle en jours calendaires ou en jours ouvrés ? Si l’échéance tombe un jour férié, avance-t-on la date ou la recule-t-on (le payeur a tendance à avancer, le bénéficiaire à reculer sa date d’encaissement prévue — les usages diffèrent selon le point de vue) ? Comment interpréter, en février, un contrat dont la clôture se situe entre le 29e et le 31e jour ? Tant que ces points ne sont pas documentés, le code de l’implémenteur devient tel quel « une spécification que personne n’a jamais validée ». La plupart des enquêtes sur des bugs liés aux dates de clôture n’aboutissent pas à une erreur de code, mais à une spécification absente.

4.3 Exercice fiscal et trimestre

Au Japon, l’« exercice fiscal » d’une application métier commence presque toujours en avril. Ne le stockez pas : traitez-le comme une valeur dérivée de la date.

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

public static int GetFiscalQuarter(DateOnly d) => ((d.Month + 8) % 12) / 3 + 1;
// avr-juin → T1, juil-sept → T2, oct-déc → T3, janv-mars → T4

Attention à ne pas stocker inutilement en redondance l’exercice fiscal en base de données. Conserver dans une colonne séparée une valeur qui peut toujours être dérivée de la date crée un risque : un oubli de mise à jour lors d’une correction de date, et l’on se retrouve avec des données où la date et l’exercice fiscal ne correspondent plus. Si vous devez absolument le conserver pour des raisons de performance d’agrégation, faites-en une colonne calculée (générée) et interdisez toute écriture directe depuis l’application.

4.4 Tests — anticiper les bugs qui ne se manifestent « qu’à cette date-là »

Les bugs liés à l’ère japonaise, aux jours fériés et aux dates de clôture ne se déclarent que pour des dates précises. En vous appuyant sur l’injection de l’heure actuelle via TimeProvider présentée dans l’article précédent (chapitre 7 de « Date, heure et fuseaux horaires dans les applications métier »), voici les cinq dates à tester au minimum dans ce domaine :

  • Frontières de changement d’ère : affichage et conversion inverse d’une saisie en ère japonaise autour du 1989-01-07/08 (Showa → Heisei) et du 2019-04-30/05-01 (Heisei → Reiwa)
  • Première année d’une ère : vérification de la notation « Reiwa Gan-nen » sur les bordereaux (section 2.2)
  • Années bissextiles : calculs de clôture et d’échéance de paiement englobant le 29 février (2024, 2028)
  • Enchaînement de fins de mois : génération de douze mois de dates de clôture à partir du 31 janvier, en vérifiant que la fin de mois est préservée (section 4.1)
  • Frontières d’exercice fiscal : détermination de l’exercice fiscal et du trimestre pour le 31 mars et le 1er avril

En complément, lors de la revue de code, les motifs suivants peuvent être repérés mécaniquement par grep.

Code trouvé, à suspecter Ce qui peut se produire Comment corriger
Littéraux d’ère comme "平成" ou "令和" Reste à la traîne au prochain changement d’ère Migrer vers JapaneseCalendar + le format g (section 2.3)
Littéraux de dates fériées (comme 5, 3) Mauvaise détermination après une révision légale ou une loi de mesures spéciales Passer par la table de référence des jours fériés (section 3.2)
Chemin de sauvegarde/sortie utilisant ToString() sans argument L’ère japonaise s’infiltre dans les données stockées sur un poste réglé sur l’affichage en ère InvariantCulture + format explicite (section 2.4)
Répétition de dateFrécédente.AddMonths(1) La fin de mois continue de dériver après le passage de février Calculer à partir d’une date de référence à chaque fois (section 4.1)
new DateTime(y, m, 31) Exception pour tout mois de 30 jours ou moins Le dériver à partir de DateTime.DaysInMonth

5. Conclusion

L’ère japonaise, les jours fériés et les dates de clôture ont un point commun : ce sont tous des traitements qui composent avec des « spécifications qui changent plus tard ». Le principe de conception est donc le même dans les trois cas : ne pas écrire dans le code ce qui est susceptible de changer, mais l’isoler dans les données et dans une couche de conversion. Unifiez le traitement interne et le stockage sur le calendrier grégorien en réservant l’ère japonaise à une conversion d’affichage ; conservez les jours fériés dans une table de référence assortie d’un processus de mise à jour ; documentez « fin de mois », « jour ouvré » et « ajustement de l’échéance en cas de jour férié » comme des spécifications contractuelles avant de les calculer à partir d’une date de référence. En procédant ainsi, le prochain changement d’ère comme la prochaine révision de la loi sur les jours fériés deviennent, pour votre application, de simples événements se résolvant par « une mise à jour de la table de référence » et « l’application d’une mise à jour de l’OS ».

Nous prenons en charge la conception et l’implémentation du traitement des dates autour des bordereaux et de la facturation, le recensement et la refonte des noms d’ère et des jours fériés codés en dur dans une application existante, ainsi que des revues de conception incluant la clarification des spécifications de date de clôture. Nous pouvons intervenir même dès le stade où vous ignorez encore combien d’endroits de votre code nécessitent une remise en conformité liée au changement d’ère.

Articles connexes

Domaines de conseil associés

KomuraSoft LLC (合同会社小村ソフト) prend en charge la conception et l’implémentation du traitement des dates pour les applications métier conformes aux usages commerciaux japonais (bordereaux en ère japonaise, calcul des jours ouvrés, traitement des dates de clôture), ainsi que le recensement et la refonte de la prise en charge des changements d’ère et des jours fériés dans les applications existantes.

Références

  1. Microsoft Learn, Work with eras - .NET. Sur le fait que JapaneseCalendar et JapaneseLunisolarCalendar soient les seules classes de calendrier reconnaissant plusieurs ères, sur le formatage de l’ère via le spécificateur « g », et sur le fait que les informations d’ère sont fournies par la plateforme plutôt que codées en dur dans l’application.  2 3

  2. Cabinet Office du Japon, « À propos des jours fériés nationaux ». Sur la liste des jours fériés au titre de la loi sur les jours fériés nationaux, sur le fait que les équinoxes de printemps et d’automne sont fixés et publiés l’année précédente, sur les dispositions relatives au jour férié de substitution et au jour férié national, et sur la mise à disposition d’un CSV des dates et libellés des jours fériés de Showa 30 jusqu’à l’année suivante.  2 3 4 5

  3. Microsoft Learn, DateTime.AddMonths(Int32) Method. Sur le fait que, lorsque le jour du résultat dépasse le nombre de jours du mois résultant, le résultat est ajusté au dernier jour de ce mois (par exemple, le 31 janvier plus un mois devient le 28 ou le 29 février).  2

  4. Microsoft Learn, JapaneseCalendar Class. Sur la plage de dates prise en charge par JapaneseCalendar — à partir du 8 septembre de l’ère Meiji 1 (1868 apr. J.-C.) — et sur la gestion des ères. 

  5. Microsoft Learn, Work with calendars - .NET. Sur le comportement par défaut consistant à formater la première année d’une ère comme « Gan-nen », sur le commutateur Switch.System.Globalization.FormatJapaneseFirstYearAsANumber qui permet de revenir à la notation numérique, et sur le fait que la vérification de la plage d’années des ères est assouplie par défaut mais peut être rendue stricte via Switch.System.Globalization.EnforceJapaneseEraYearRanges.  2

  6. Microsoft Learn, CultureInfo.CurrentCulture Property. Sur le fait que CurrentCulture reflète par défaut les changements de paramètres régionaux effectués par l’utilisateur Windows (une surcharge utilisateur), et que le formatage par défaut dépend de cette culture. 

Articles récents partageant les mêmes étiquettes, pour approfondir des sujets proches.

Ces pages replacent le sujet dans un contexte plus large de services et de décisions.

Cet article est directement lié aux services suivants.

Développement d'applications Windows

Implémenter des exigences métier propres au Japon telles que l'affichage de la date en ère japonaise sur les bordereaux, le calcul des jours ouvrés et le traitement des dates de clôture relève du cœur même du conseil en développement d'applications métier Windows.

Questions fréquentes

Questions souvent posées lors d’une consultation sur le sujet de cet article.

Est-il déconseillé de stocker les dates en ère japonaise telles quelles dans la base de données ?
Oui, pour quatre raisons. D'abord, des chaînes d'ère comme « Heisei 9 » et « Reiwa 9 » perdent tout ordre cohérent en comparaison de chaînes dès qu'on franchit une frontière d'ère, ce qui rend le tri et les recherches par intervalle impossibles. Ensuite, dès que survient le prochain changement d'ère, les anciennes et les nouvelles notations se retrouvent mélangées dans les données déjà stockées. Troisièmement, des données corrompues contenant des dates qui n'ont jamais existé — comme « Showa 64, 10 janvier » (ce jour-là, on était déjà en Heisei 1, l'ère ayant changé le 8 janvier) — s'infiltrent facilement et deviennent une bombe à retardement pour l'analyse syntaxique. Quatrièmement, JapaneseCalendar de .NET ne prend en charge que les dates à partir du 8 septembre de l'ère Meiji 1 (1868) ; les dates antérieures — par exemple une date de naissance ancienne issue d'un registre d'état civil — ne peuvent pas être traitées correctement tant qu'elles restent en notation d'ère. Le principe est d'unifier le traitement interne et le stockage sur le calendrier grégorien, et de ne convertir en ère japonaise qu'au moment de l'affichage ou de l'impression.
Comment une application doit-elle se préparer au prochain changement d'ère ?
La définition des noms d'ère n'est pas codée en dur dans .NET lui-même : le mécanisme référence les informations d'ère fournies par le système d'exploitation. Il suffit donc de continuer à appliquer les mises à jour de l'OS et de .NET pour que « l'affichage dans la nouvelle ère » suive automatiquement. Lors du passage à l'ère Reiwa, l'essentiel de l'adaptation s'est d'ailleurs résumé à l'application des mises à jour de la plateforme. Le vrai problème se situe du côté du code et des données de l'application elle-même : les branches conditionnelles codant en dur « Heisei » ou « Reiwa » sous forme de chaînes, les fonctions de conversion maison possédant leur propre table de correspondance ère/année grégorienne, les noms d'ère écrits directement dans les modèles de bordereaux, ou encore les champs « Reiwa __ » sur des formulaires préimprimés — rien de tout cela ne suit automatiquement. Quatre mesures concrètes sont réalistes : (1) unifier le traitement interne et le stockage sur le calendrier grégorien pour que l'ère japonaise reste purement un affichage, (2) abandonner sa propre logique de conversion d'ère au profit de JapaneseCalendar, (3) passer l'ensemble du code source au crible avec un grep sur « Showa », « Heisei » et « Reiwa » pour en dresser l'inventaire, et (4) préparer des tests simulant un changement d'ère fictif.
Ne peut-on pas déterminer les jours fériés par une formule de calcul ? Pourquoi faut-il absolument les stocker sous forme de données ?
Une formule complète est par principe impossible à établir, pour trois raisons. D'abord, le jour de l'équinoxe de printemps et celui de l'équinoxe d'automne sont fixés l'année précédente par décision du Conseil des ministres sur la base d'un calcul astronomique : ils ne sont pas inscrits dans la loi sous forme de dates fixes. Ensuite, les jours fériés sont ajoutés, déplacés ou renommés par révision légale : création du Jour de la montagne (11 août) en 2016, déplacement de l'anniversaire de l'Empereur du 23 décembre au 23 février en 2019 (**il n'y a eu aucun anniversaire de l'Empereur en 2019**), ou encore renommage du Jour du sport et de la santé en Jour du sport. Troisièmement, en 2020 et 2021, une loi de mesures spéciales liée aux Jeux olympiques a déplacé le Jour de la mer, le Jour du sport et le Jour de la montagne **pour ces seules années-là**, montrant qu'une règle du type « toujours le troisième lundi » peut elle-même être ponctuellement écrasée. La détermination des jours fériés doit donc être conçue comme « une table de référence des jours fériés + un processus de mise à jour », la logique de calcul se limitant à des règles auxiliaires comme le jour férié de substitution.
Mon échéance de paiement a dérivé par rapport à la fin du mois après un AddMonths(1). Pourquoi ?
Parce que DateTime.AddMonths est spécifié pour arrondir au dernier jour du mois résultant lorsque le calcul naïf donnerait un jour qui n'existe pas dans ce mois. Appliquer AddMonths(1) au 31 janvier donne le 28 février (ou le 29 les années bissextiles) — ce comportement est en soi correct. Mais si l'on reporte ensuite ce résultat arrondi dans un nouvel AddMonths, appliquer AddMonths(1) au 28 février donne le 28 mars, et à partir de là le décalage se poursuit indéfiniment : ce qui devait être « la fin du mois » se transforme silencieusement, et durablement, en « le 28 ». La parade comporte deux volets : pour un calcul répété, toujours calculer à partir d'un point de référence fixe (la date du contrat, ou la définition de « fin de chaque mois ») plutôt que d'additionner au résultat précédent, et dériver « fin de mois » à partir de DateTime.DaysInMonth plutôt que d'un littéral de date.

Profil de l’auteur

Page de présentation de l’auteur de l’article.

Go Komura

Représentant de KomuraSoft LLC

Spécialisé dans le développement de logiciels Windows, le conseil technique et l’analyse de pannes, notamment pour les systèmes existants et les incidents difficiles à reproduire.

Retour au blog