如何将 XML 文件中的 Excel 序列日期编号转换为 SSIS 包中 SQL 服务器的 mm/dd/yyyy?

How do I convert Excel Serial Date Numbers in an XML file to mm/dd/yyyy for SQL Server in an SSIS Package?

我收到了一个 XML 文件,其中日期字段是 Excel 序列日期编号,而不是通常的 mm/dd/yyyy 日期。我在向我现有的 SSIS 包添加数据转换时遇到问题,因为序列号在 XML 文件中,而不是 excel 文件中。

SSIS 包清理并加载一个 xml 文件到 SQL 服务器 table。

有人有什么想法吗?我在 Visual Studios 2015 fyi 工作。

XML 数据片段:

    <AGREEMENT_CODE>1960-EMPR</AGREEMENT_CODE>
        <AGREEMENT_NAME>1960-Legacy Employer Conversion 
    Default</AGREEMENT_NAME>
        <AGREEMENT_TYPE>MBA</AGREEMENT_TYPE>
        <FUND_TYPE>Health &amp; Pension</FUND_TYPE>
        <CONTRACT_START_DATE>21916</CONTRACT_START_DATE>
        <EMPLOYER_ID>25568</EMPLOYER_ID>
        <EMPLOYER_NAME>10409</EMPLOYER_NAME>
        <BILLING_ENTITY_CODE>ACT III TELEVISION, L.P.</BILLING_ENTITY_CODE>
        <BILLING_ENTITY_NAME>10409</BILLING_ENTITY_NAME>
        <PARTICIPATION_START_DATE>ACT III TELEVISION, L.P. 
   </PARTICIPATION_START_DATE>
        <PARTICIPATION_SIGNED_DATE>35917</PARTICIPATION_SIGNED_DATE>

Excel 序列号位于第 6 行和第 13 行。它们是 5 位数字。

Excel 中的日期实际上是一个数字,代表(出于所有意图和目的)自 1899 年 12 月 30 日以来的天数。但是下面有一个警告。

因此,您可以使用 DATEADD(day, @yourDateNum, '18991230')

将数字转换为日期

需要注意的是,Microsoft(以及之前的 Lotus 1-2-3)在日期计算中存在错误:they assume 1900 is a leap year,事实并非如此。因此,还有额外的一天,我已经调整了上面的公式以考虑到这一点。然而,结果是此公式不适用于 1900 年 3 月 1 日之前的日期。

请在投入生产前用已知值检查您的公式!

我用了十多年的代码现在来自一个已经不存在的网站

    /// <summary>
    /// Seriously?  For the loss
    /// <see cref="http://www.debugging.com/bug/19252"></see>
    /// </summary>
    /// <param name="excelDate">Number of days since 1900-01-01</param>
    /// <returns>The converted days to date</returns>
    public static DateTime ConvertXlsdtToDateTime(int excelDate)
    {
        DateTime dt = new DateTime(1899, 12, 31);

        // adjust for 29 Feb 1900 which Excel considers a valid date
        if (excelDate >= 60)
        {
            excelDate--;
        }

        return dt.AddDays(excelDate);
    }

如果您要在派生列任务中使用它,则需要将 C# 转换为 SSIS 表达式语言。我们将使用三元运算符 (boolean) ? truevalue : falsevalue 来实现这一点

DATEADD("Day", [CONTRACT_START_DATE] - ([CONTRACT_START_DATE] >= 60 ? 1 : 0), (DT_DATE)"1899-12-30")

通常,我的数据流中有两个派生列组件,因为这是调试事物的唯一方法。在您的情况下,我会让第一个派生列向数据流添加 1 + Number Of Excel 列。

注意,我在此处显示的作业 = 是您在派生任务

中输入 Derived Column NameExpression 列的值的简写语法
BaseDate = (DT_DATE)"1899-12-30"
CONTRACT_START_DATE_Offset = [CONTRACT_START_DATE] >= 60 ? -1 : 0
PARTICIPATION_SIGNED_DATE_Offset = [PARTICIPATION_SIGNED_DATE] >= 60 ? -1 : 0

等现在我可以检查我是否正确处理了 1900 错误的偏移值。

然后将我的第一个表达式简化为

DATEADD("Day", [CONTRACT_START_DATE] - [CONTRACT_START_DATE_Offset], [BaseDate])

如果您真的想找出可能出错的地方,我什至会考虑将那里的数学逻辑 Col1 - Col1_Offset 封装到前体派生列中,这样我就可以将数据 tap/data 查看器并捕获值。

作为最小复制,我创建了一个包含 OLESRC 组件的包,该组件将我们的 CONTRACT_START_DATE 添加到数据流

SELECT 35 AS CONTRACT_START_DATE
UNION ALL SELECT 21916

我使用第一个表达式的派生列“OneShot”添加列“OneShot”

DER Multistatement 添加第二组表达式中的 BaseDate 和 CONTRACT_START_DATE_Offset 值

DER CSD 使用第三个表达式将 ContractStartDate 添加到数据流中。

在上图中,您可以看到数据查看器的结果(以及此时的元数据副本)。使用 DATEADD 函数后,我的数据类型符合预期 - DT_DBTIMESTAMP.

如果您的类型显示为 DT_WSTR,请确保您添加的是新列,而不是试图替换现有列。