如果 00-49 出生,将 DDMMYY 转换为 DDMMYYYY

Converting DDMMYY to DDMMYYYY if born 00-49

挪威的社会安全号码 (SSN) 是这样组成的

substr(ssn,1,6) = ddmmyy
substr(ssn,7,3) =
Person individual numbers:
000–499 persons born 1900–1999.
500–749 persons born 1854–1899.
500–999 persons born 2000–2039.
900–999 persons born 1940–1999.

substr(ssn,11,2)=control digits

我正在努力将 1900 年至 1949 年间出生的人的出生日期转换为日期格式DD.MM.YYYY

select to_date('121049','dd.mm.rrrr') from dual; -- Returns: 12.10.2049
select to_date('121049','dd.mm.rr')from dual; -- Returns: 12.10.2049
select to_date('121049','dd.mm.yy')from dual; -- Returns: 12.10.2049

select to_date('121050','dd.mm.rrrr')from dual; -- Returns: 12.10.1950
select to_date('121050','dd.mm.rr')from dual; -- Returns: 12.10.1950
select to_date('121050','dd.mm.yy')from dual; -- Returns: 12.10.2050

我预计 121049 到 return12.10.1949。根据我掌握的信息,如何强制 oracle return 预期的日期格式?

来自维基百科:https://en.wikipedia.org/wiki/National_identification_number#Norway

Historically, the number has been composed of the date of birth (DDMMYY), a three digit individual number, and two check digits. The individual number and the check digits are collectively known as the Personal Number.

The individual number has been selected from a range depending on century of birth: for the years 1854–1899 the range is 500–749, for the years 1900–1999 the range is 000-499, for the years 2000–2039 the range is 500–999. For the years 1940–1999, the range 900–999 was also used for special purposes, such as adoptions from abroad and immigrants. Women have been assigned even individual numbers, men are assigned odd individual numbers.

两位数年份及其隐含世纪的解释似乎同时基于其值和 PIN。该范围重叠,但全年受到限制;所以看起来你可以使用一个 case 表达式来检查两者:

-- CTE for dummy data
with t42 (ssn) as (
  select '12104900000' from dual
  union all select '12105099999' from dual
  union all select '01010000001' from dual
  union all select '02029949902' from dual
  union all select '03035450003' from dual
  union all select '04049974904' from dual
  union all select '05050050005' from dual
  union all select '06063999906' from dual
  union all select '07074090007' from dual
  union all select '08089999908' from dual
)
select ssn, to_date(substr(ssn, 1, 4)
  || case
      when to_number(substr(ssn, 7, 3)) between 0 and 499
        and to_number(substr(ssn, 5, 2)) between 0 and 99 then '19'
      when to_number(substr(ssn, 7, 3)) between 500 and 749
        and to_number(substr(ssn, 5, 2)) between 54 and 99 then '18'
      when to_number(substr(ssn, 7, 3)) between 500 and 999
        and to_number(substr(ssn, 5, 2)) between 0 and 39 then '20'
      when to_number(substr(ssn, 7, 3)) between 900 and 999
        and to_number(substr(ssn, 5, 2)) between 40 and 99 then '19'
    end
  || substr(ssn, 5, 2), 'DDMMYYYY') as dob
from t42;

根据您的两个示例和涉及的范围,对于该数据,给出:

SSN         DOB       
----------- ----------
12104900000 1949-10-12
12105099999 1950-10-12
01010000001 1900-01-01
02029949902 1999-02-02
03035450003 1854-03-03
04049974904 1899-04-04
05050050005 2000-05-05
06063999906 2039-06-06
07074090007 1940-07-07
08089999908 1999-08-08

该案例根据 PIN 选择一个两位数的世纪值,然后 - 因为它们重叠 - 两位数的年份范围。

如果数据设计发生变化,因此基于两位数年份的重叠不再是唯一的,您还有其他问题。看看我们到 2040 年时会发生什么会很有趣...

如果您的 SSN 与您显示的范围不匹配,请说 12105050000(密码为 500,但两位数年份不在 00-39 或 54-99 范围内) 那么 case 表达式将为 return null 并且两位数的年份将被解释为 0050。您可以通过更改格式模型使其出错 - 取决于它是否会发生以及您希望如何处理它确实如此。


这一点你大概能猜出来,但是要处理评论中提到的 day+40 场景,你可以使用另一个 case 表达式来调整天数:

select ssn, to_date(
    case
      when substr(ssn, 1, 2) > 31 then to_char(to_number(substr(ssn, 1, 2)) - 40, 'FM99')
      else substr(ssn, 1, 2)
    end
  || substr(ssn, 3, 2)
  || case
      when to_number(substr(ssn, 7, 3)) between 0 and 499
...