SQL Server Datetime object persistent reformatting issue in Excel

前端 未结 1 1971
攒了一身酷
攒了一身酷 2021-01-21 10:11

I have an annoying issue working with SQL Server DATETIME objects in Excel 2013. The problem has been stated several times here in SO, and I know that the work arou

1条回答
  •  深忆病人
    2021-01-21 10:25

    Without any code it's hard to guess how the data gets from SQL Server to Excel. I assume it's not through a data connection, because Excel wouldn't have any issues displaying the data as dates directly.

    What about data connections?

    Excel doesn't support any kind of formatting or any useful designer for that matter, when working with data connections only. That functionality is provided by Power Query or the PivotTable designer. Power Query is integrated in Excel 2016 and available as a download for Excel 2010+.

    Why you need to format dates

    Excel doesn't preserve type information. Everything is a string or number and its display is governed by the cell's format.

    Dates are stored as decimals using the OLE Automation format - the integral part is the number of dates since 1900-01-01 and the fractional part is the time. This is why the System.DateTime has those FromOADate and ToOADate functions.

    To create an Excel sheet with dates, you should set the cell format at the same time you generate the cell.

    How to format cells

    Doing this is relatively if you use the Open XML SDK or a library like EPPlus. The following example creates an Excel sheet from a list of customers:

    static void Main(string[] args)
    {
        var customers = new[]
        {
            new Customer("A",DateTime.Now),
            new Customer("B",DateTime.Today.AddDays(-1))
        };
        File.Delete("customers.xlsx");
        var newFile = new FileInfo(@"customers.xlsx");
        using (ExcelPackage pck = new ExcelPackage(newFile))
        {                
            var ws = pck.Workbook.Worksheets.Add("Content");
    
            // This format string *is* affected by the user locale!
            // and so is "mm-dd-yy"!
            ws.Column(2).Style.Numberformat.Format = "m/d/yy h:mm";
    
            //That's all it needs to load the data
            ws.Cells.LoadFromCollection(customers,true);
            pck.Save();
        }
    }
    

    The code uses the LoadFromCollection method to load a list of customers directly, without dealing with cells. true means that a header is generated.

    There are equivalent methods to load data from other source: LoadFromDatatable, LoadFromDataReader, LoadFromText for CSV data and even LoadFromArrays for jagged object arrays.

    The weird thing is that specifying the m/d/yy h:mm or mm-dd-yy format uses the user's locale for formatting, not the US format! That's because these formats are built-in into Excel and are treated as the locale-dependent formats. In the list of date formats they are shown with an asterisk, meaning they are affected by the user's locale.

    The reason for this weirdness is that when Excel moved to the XML-based XLSX format 10 years ago, it preserved the quirks of the older XLS format for backward-compatibility reasons.

    When EPPlus saves the xlsx file it detects them and stores a reference to the built-in format ID (22 and 14 respectively) instead of storing the entire format string.

    Finding Format IDs

    The list of standard format IDs is shown in the NumberingFormat element documentation page of the Open XML standard. Excel originally defined IDs 0 (General) through 49.

    EPPlus doesn't allow setting the ID directly. It checks the format string and maps only the formats 0-49 as shown in the GetBfromBuildIdFromFormat method of ExcelNumberFormat. In order to get ID 22 we need to set the Format property to "m/d/yy h:mm"

    Another trick is to check the stylesheets of an existing sheet. xlsx is a zipped package of XML files that can be opened with any decompression utility. The styles are stored in the xl\styles.xml file.

    0 讨论(0)
提交回复
热议问题