From c45ac185f801be91a41119e2ecefabf7b2243787 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Luthi?= Date: Tue, 26 Apr 2022 16:36:23 +0200 Subject: [PATCH] Use standard built-it style formats As defined in ECMA-376, 3rd Edition, Part 1, 18.8.30 numFmt (Number Format). > Some of these Ids can be interpreted differently, depending on the UI language of the implementing application. This renders dates properly in Excel, matching the current computer locale settings. For example, cell D5 in the "Test" worksheet of the sample app is correctly rendered as `26.04.2022 13:14` on a computer configured with a French (Switzerland) regional format. Without this change, the date was incorrectly displayed as `4.26.2022 13:14`. --- src/Simplexcel.TestApp/Program.cs | 56 +++++++++++----------- src/Simplexcel/Cells/BuiltInCellFormat.cs | 8 ++-- src/Simplexcel/XlsxInternal/StyleWriter.cs | 39 ++++++++++++++- 3 files changed, 70 insertions(+), 33 deletions(-) diff --git a/src/Simplexcel.TestApp/Program.cs b/src/Simplexcel.TestApp/Program.cs index e48ff8e..f5f0399 100644 --- a/src/Simplexcel.TestApp/Program.cs +++ b/src/Simplexcel.TestApp/Program.cs @@ -71,33 +71,35 @@ static void Main() sheet.Cells[0, 6] = "๐Ÿ‘ช"; sheet.Cells[0, 7] = "๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ"; - sheet.Cells["D4"] = DateTime.Now; - sheet.Cells["D5"] = new Cell(CellType.Date, DateTime.Now, BuiltInCellFormat.DateOnly); - sheet.Cells["D6"] = new Cell(CellType.Date, DateTime.Now, BuiltInCellFormat.TimeOnly); - sheet.Cells["D7"] = long.MaxValue; - sheet.Cells["D8"] = long.MinValue; - sheet.Cells["D9"] = decimal.MaxValue; - sheet.Cells["D10"] = decimal.MinValue; - - sheet.Cells["D11"] = 9999999999L; - sheet.Cells["D12"] = 99999999999L; - sheet.Cells["D13"] = 100000000000L; - sheet.Cells["D14"] = 100000000001L; - sheet.Cells["D15"] = 1000000000000L; - sheet.Cells["D16"] = 1000000000001L; - sheet.Cells["D17"] = Cell.LargeNumberPositiveLimit; - sheet.Cells["D18"] = Cell.LargeNumberPositiveLimit + 1; - sheet.Cells["D19"] = Cell.LargeNumberPositiveLimit - 1; - - sheet.Cells["D20"] = -9999999999L; - sheet.Cells["D21"] = -99999999999L; - sheet.Cells["D22"] = -100000000000L; - sheet.Cells["D23"] = -100000000001L; - sheet.Cells["D24"] = -1000000000000L; - sheet.Cells["D25"] = -1000000000001L; - sheet.Cells["D26"] = Cell.LargeNumberNegativeLimit; - sheet.Cells["D27"] = Cell.LargeNumberNegativeLimit + 1; - sheet.Cells["D28"] = Cell.LargeNumberNegativeLimit - 1; + var dateTime = new DateTime(2022, 4, 26, 13, 14, 15); + sheet.Cells["D4"] = dateTime; + sheet.Cells["D5"] = new Cell(CellType.Date, dateTime, BuiltInCellFormat.DateOnly); + sheet.Cells["D6"] = new Cell(CellType.Date, dateTime, BuiltInCellFormat.TimeOnly); + sheet.Cells["D7"] = new Cell(CellType.Date, dateTime, "yyyy\"_\"mm\"_\"dd\"_\"hh\"_\"mm\"_\"ss"); + sheet.Cells["D8"] = long.MaxValue; + sheet.Cells["D9"] = long.MinValue; + sheet.Cells["D10"] = decimal.MaxValue; + sheet.Cells["D11"] = decimal.MinValue; + + sheet.Cells["D12"] = 9999999999L; + sheet.Cells["D13"] = 99999999999L; + sheet.Cells["D14"] = 100000000000L; + sheet.Cells["D15"] = 100000000001L; + sheet.Cells["D16"] = 1000000000000L; + sheet.Cells["D17"] = 1000000000001L; + sheet.Cells["D18"] = Cell.LargeNumberPositiveLimit; + sheet.Cells["D19"] = Cell.LargeNumberPositiveLimit + 1; + sheet.Cells["D20"] = Cell.LargeNumberPositiveLimit - 1; + + sheet.Cells["D21"] = -9999999999L; + sheet.Cells["D22"] = -99999999999L; + sheet.Cells["D23"] = -100000000000L; + sheet.Cells["D24"] = -100000000001L; + sheet.Cells["D25"] = -1000000000000L; + sheet.Cells["D26"] = -1000000000001L; + sheet.Cells["D27"] = Cell.LargeNumberNegativeLimit; + sheet.Cells["D28"] = Cell.LargeNumberNegativeLimit + 1; + sheet.Cells["D29"] = Cell.LargeNumberNegativeLimit - 1; sheet.LargeNumberHandlingMode = LargeNumberHandlingMode.StoreAsText; sheet.Cells["C5"] = "ThinDiagonalCrosshatch"; diff --git a/src/Simplexcel/Cells/BuiltInCellFormat.cs b/src/Simplexcel/Cells/BuiltInCellFormat.cs index d4d4dba..3c0ca72 100644 --- a/src/Simplexcel/Cells/BuiltInCellFormat.cs +++ b/src/Simplexcel/Cells/BuiltInCellFormat.cs @@ -36,14 +36,14 @@ public static class BuiltInCellFormat public const string Text = "@"; /// - /// m/d/yyyy h:mm + /// m/d/yy h:mm /// - public const string DateAndTime = "m/d/yyyy h:mm"; + public const string DateAndTime = "m/d/yy h:mm"; /// - /// m/d/yyyy + /// mm-dd-yy /// - public const string DateOnly = "m/d/yyyy"; + public const string DateOnly = "mm-dd-yy"; /// /// h:mm diff --git a/src/Simplexcel/XlsxInternal/StyleWriter.cs b/src/Simplexcel/XlsxInternal/StyleWriter.cs index 4f744e5..dc8e674 100644 --- a/src/Simplexcel/XlsxInternal/StyleWriter.cs +++ b/src/Simplexcel/XlsxInternal/StyleWriter.cs @@ -9,6 +9,41 @@ internal static class StyleWriter // There are up to 164 built in number formats (0-163), all else are custom (164 and above) private const int CustomFormatIndex = 164; + /// + /// Standard format codes as defined in ECMA-376, 3rd Edition, Part 1, 18.8.30 numFmt (Number Format) + /// + private static Dictionary StandardFormatIds = new Dictionary + { + ["General"] = 0, + ["0"] = 1, + ["0.00"] = 2, + ["#,##0"] = 3, + ["#,##0.00"] = 4, + ["0%"] = 9, + ["0.00%"] = 10, + ["0.00E+00"] = 11, + ["# ?/?"] = 12, + ["# ??/??"] = 13, + ["mm-dd-yy"] = 14, + ["d-mmm-yy"] = 15, + ["d-mmm"] = 16, + ["mmm-yy"] = 17, + ["h:mm AM/PM"] = 18, + ["h:mm:ss AM/PM"] = 19, + ["h:mm"] = 20, + ["h:mm:ss"] = 21, + ["m/d/yy h:mm"] = 22, + ["#,##0 ;(#,##0)"] = 37, + ["#,##0 ;[Red](#,##0)"] = 38, + ["#,##0.00;(#,##0.00)"] = 39, + ["#,##0.00;[Red](#,##0.00)"] = 40, + ["mm:ss"] = 45, + ["[h]:mm:ss"] = 46, + ["mmss.0"] = 47, + ["##0.0E+0"] = 48, + ["@"] = 49, + }; + /// /// Create a styles.xml file /// @@ -29,7 +64,7 @@ internal static XmlFile CreateStyleXml(IList styles) uniqueBorders.Add(style.Border); } - if (!numberFormats.Contains(style.Format)) + if (!numberFormats.Contains(style.Format) && !StandardFormatIds.ContainsKey(style.Format)) { numberFormats.Add(style.Format); } @@ -79,7 +114,7 @@ private static void StyleAddCellXfsElement(XDocument doc, IList s foreach (var style in styles) { - var numFmtId = numberFormats.IndexOf(style.Format) + CustomFormatIndex; + var numFmtId = StandardFormatIds.TryGetValue(style.Format, out var standardNumFmtId) ? standardNumFmtId : numberFormats.IndexOf(style.Format) + CustomFormatIndex; var fontId = fontInfos.IndexOf(style.Font); var fillId = fills.IndexOf(style.Fill); var borderId = uniqueBorders.IndexOf(style.Border);