JD Edwards / IBM iSeries DB2 Julian Date / Calendar Date SQL 转换

JD Edwards / IBM iSeries DB2 Julian Date / Calendar Date SQL Conversions

IBM iSeries 上仍有许多上世纪 运行 遗留的应用程序在使用。例如JD爱德华兹。日期字段通常以多种格式统一存储,通常称为 Julian Date。这些格式是年份和年份的组合,今天更准确地称为 Ordinal Date

JD Edwards ERP 应用程序数据库中广泛使用的特定儒略日期格式是一种 6 位格式 (YYYDDD),其中包含 3 位年份(表示为年数自 1900 年以来)和 3 位数字(带前导零)表示一年中的第几天。 在 SQL 中有一个清晰简洁的方法在这两种格式之间进行转换非常有帮助。

这是 IBM 为 Converting Julian Dates to Calendar Dates 提供的解决方案:

Select date (days(concat(cast(integer(1900000+"SDIVD") /1000 as Char(4)),'-01-01'))+mod(integer(1900000+" SDIVD"),1000)-1)

哎呀。我的头好痛...

我希望有一种更简单、更容易的方法来仅使用 IBM iSeries DB2 SQL 语法来转换任何一种方式?

对于 IBM i Db2(7.2 版),我确定了以下两种 Julian Dates 和 Calendar Dates 之间的转换方法:

-- Julian to Db2 Date (Method 1.0)
DATE(CAST(120050 + 1900000 AS CHAR(7)))

-- Db2 Date to Julian (Method 2.0)
1000 * (YEAR(DATE('02/19/2020')) - 1900) + DAYOFYEAR(DATE('02/19/2020'))

更新。我发现第一种方法可以缩短为:

-- Julian to Db2 Date (Method 1.1)
DATE(CHAR(120050 + 1900000))

这里有一些在 SQL 语句中使用的示例(以及 IBM 方法):

SELECT
    TEST_DATA."DATE"
    , TEST_DATA."JULIAN"
    , YEAR(TEST_DATA."DATE") AS "YEAR"
    , DAYOFYEAR(TEST_DATA."DATE") AS "DAYOFYEAR"
    , DATE(DAYS(CONCAT(CAST(INTEGER(1900000 + TEST_DATA."JULIAN") / 1000 AS CHAR(4)), '-01-01')) + MOD(INTEGER(1900000 + TEST_DATA."JULIAN"), 1000) - 1) AS "METHOD_IBM"
    , DATE(CAST((TEST_DATA."JULIAN" + 1900000) AS CHAR(7))) AS "METHOD_1.0"
    , DATE(CHAR(TEST_DATA."JULIAN" + 1900000)) AS "METHOD_1.1"
    , 1000 * (YEAR(TEST_DATA."DATE") - 1900) + DAYOFYEAR(TEST_DATA."DATE") AS "METHOD_2.0"
FROM
    TABLE
    (
        VALUES
              (DATE('12/31/1938'), 039365)
            , (DATE('12/31/1939'), 039365)
            , (DATE('01/01/1940'), 040001)
            , (DATE('02/19/2020'), 120050)
            , (DATE('2020-01-01'), 119366)
            , (DATE('2039-01-01'), 139001)
            , (DATE('2039-12-31'), 139365)
            , (DATE('2040-01-01'), 140001)
            , (DATE('2041-01-15'), 141015)
    )
    AS TEST_DATA("DATE", "JULIAN")
;

突出显示的记录是由另一个答案建议的:

Try IBM's version and yours with 119366...

我不知道是否有 "standard" 表示这个特定值,但我会说 119366 不是有效的儒略日期,因为 2019 年不是闰年,只有 365 天。我认为允许 "overflow" 进入后续年份是一种不利的方法,我也没有看到任何遗留应用程序以这种方式存储日期。一般来说,我希望我的查询突出显示数据中的不规则性,而不是掩盖它们。

请注意,至少有一些数据库标量函数只计算 1940 年到 2039 年之间的日期。我已经包括了一些超出这个范围的日期来演示奇怪的行为。我不确定 IBM 在哪里记录了该行为,但 documentation for the scalar DAYOFYEAR function.

中没有提及任何内容

使用 119366 试用 IBM 和您的版本...

无论您选择哪种转换方法,我都会将其包装在用户定义函数 (UDF) 中。

这样做开启了自定义错误处理的可能性,例如使用存储日期的数字字段。所有的 0 可能意味着 "not set" 并且可以被 null 替换,而所有的 9 应该被替换为 9999-12-31.

使用 UDF 意味着您可以考虑使用不同的语言来进行转换,例如 RPG。有一个现有的日期转换 UDF 开源包,称为 iDate

要考虑的另一个选项是 "dates"/"calendar"/"date dimension" table。随身携带非常方便,不仅用于日期转换。 google 搜索将找到适用于任何数据库的大量信息。

对于 IBM i 的特定示例,请查看红皮书的第 5 章 IBM DB2 Web Query for i: The Nuts and Bolts(请注意,如果您碰巧安装了 Db2 for i Web Query,则有一个脚本可以创建这样的日期 table .)

For example, a date dimension table might include the following columns:
- Julian legacy date, which is used when joining to files that use Julian legacy dates
- Packed decimal(8,0) legacy date, which is used when joining to files that use Packed decimal(8,0) legacy dates
- Character(8) legacy date, which is used when joining to files that use Character(8) legacy dates
- Date (a true DB2 date field)
- Fiscal year - Fiscal quarter
- Day of the week (Monday, Tuesday, and so on)
- Month of the year (January, February, and so on)
- Season (spring, summer, autumn, and winter)
- Same day (of the week) last year
- Week ending date
- Week of the year
- Super Bowl Sunday flag (Y or N)
- Day before a holiday flag (Y or N)
- Day after a holiday flag (Y or N)
- Full moon flag (Y or N)

我不确定你用什么来显示数据,但是

的限制

dates inclusively between 1940 and 2039

与您的会话设置相关联,不应 return NULL,而是通常由“++++++++”表示的计算错误。空值通常表示为单个破折号“-”。如果您将会话设置更改为以 ISO 格式而不是 MDY 格式显示日期,则超出范围的日期应该可以正常显示。关于 String representations of datetime values 的相关 IBM 文章。我不知道为什么这个设置甚至会影响像 DAYOFYEAR 这样的标量函数,但它确实会影响。 (编辑:在向 IBM 提交错误报告后,他们发布了 PTF 来解决 DAYOFYEAR SI77129 (7.3) 和 SI77130 (7.4) 的这一特定问题)

我可以运行

SELECT DATE(CHAR(140001 + 1900000)) FROM sysibm.sysdummy1;

并得到

2040-01-01

为了将 date 转换为 long julian 然后再转换为您的序号日期,我找到了一个较短的版本:

SELECT INT(TO_CHAR('2019-10-18','YYYYDDD'))-1900000 FROM sysibm.sysdummy1;