COBOL Y2K redux

36

Na década de 1990, os engenheiros de computação da COBOL desenvolveram uma maneira de estender os campos de data de seis dígitos, convertendo-os para YYYDDDonde YYYé year - 1900e DDDé o dia do ano [001 to 366]. Esse esquema pode estender a data máxima para 2899-12-31.

No ano de 2898, os engenheiros começaram a entrar em pânico porque suas bases de código de 900 anos estavam falhando. Sendo do ano de 2898, eles apenas usaram sua máquina do tempo para enviar um Codeinator solitário para o ano de 1998 com esse algoritmo e a tarefa de implementá-lo o mais amplamente possível:

Use um esquema em PPQQRRque se 01 ≤ QQ ≤ 12é uma YYMMDDdata padrão nos anos 1900, mas se QQ > 12representa os dias seguintes 2000-01-01na base 100 PPe na RRbase 87 para QQ - 13.

Esse esquema se estende muito além do ano 2899 e também é compatível com as datas padrão, portanto, não são necessárias modificações nos arquivos existentes.

Alguns exemplos:

PPQQRR  YYYY-MM-DD
000101  1900-01-01  -- minimum conventional date suggested by J. Allen
010101  1901-01-01  -- edge case suggested by J. Allen
681231  1968-12-31  -- as above
991231  1999-12-31  -- maximum conventional date
001300  2000-01-01  -- zero days after 2000-01-01
008059  2018-07-04  -- current date
378118  2899-12-31  -- maximum date using YYYDDD scheme
999999  4381-12-23  -- maximum date using PPQQRR scheme

Seu desafio é escrever um programa ou função para aceitar entrada PPQQRRe saída como uma data ISO YYYY-MM-DD. O método de entrada pode ser parâmetro, console ou linha de comando, o que for mais fácil.

Para sua diversão, aqui está uma solução não-competitiva no COBOL-85:

IDENTIFICATION DIVISION.
    PROGRAM-ID. DATE-CONVERSION.
DATA DIVISION.
    WORKING-STORAGE SECTION.
    01 T PIC 9(8).
    01 U PIC 9(8).
    01 D VALUE '999999'. 
        05 P PIC 9(2).
        05 Q PIC 9(2).
        05 R PIC 9(2).
    01 F.
        05 Y PIC 9(4).
        05 M PIC 9(2).
        05 D PIC 9(2).
PROCEDURE DIVISION.
    IF Q OF D > 12 THEN
        MOVE FUNCTION INTEGER-OF-DATE(20000101) TO T
        COMPUTE U = R OF D + 100 * ((Q OF D - 13) + 87 * P OF D) + T
        MOVE FUNCTION DATE-OF-INTEGER(U) TO F
        DISPLAY "Date: " Y OF F "-" M OF F "-" D OF F
    ELSE
        DISPLAY "Date: 19" P OF D "-" Q OF D "-" R OF D 
    END-IF.
STOP RUN.

fonte
4
"Mas não programe em COBOL se puder evitá-lo." - O Tao da Programação
TSH
1
@ user202729 porque yymmddnão funciona há anos >=2000, esse é o ponto principal do desastre do Y2K.
JAD
2
@ Adám - No espírito do COBOL, que é muito exigente em relação à E / S, devo dizer que ele precisa estar no yyyy-mm-ddformato ISO .
4
@ Giuseppe - No espírito de COBOL, que realmente não diferencia cordas e números, sim! Desde que você possa inserir zeros à esquerda, por exemplo 001300.

Respostas:

5

T-SQL, 99 98 bytes

SELECT CONVERT(DATE,IIF(ISDATE(i)=1,'19'+i,
       DATEADD(d,8700*LEFT(i,2)+RIGHT(i,4)-935,'1999')))FROM t

A quebra de linha é apenas para legibilidade. Graças a Deus por elenco implícito.

A entrada é feita através de uma tabela preexistente t com a CHARcoluna i , de acordo com nossas regras de IO .

Passa pelas seguintes etapas:

  1. A verificação inicial é via função SQL ISDATE(). (O comportamento desta função muda com base nas configurações de idioma, funciona como esperado no meu english-usservidor). Observe que isso é apenas uma verificação de validade; se tentássemos analisá-lo diretamente, ele seria 250101mapeado como 2025-01-01, não 1925-01-01.
  2. Se a string analisar corretamente como uma data, cole 19na frente (em vez de alterar a configuração de corte de ano no nível do servidor). A conversão da data final chegará ao final.
  3. Se a sequência não analisar como uma data, converta-a em um número. A matemática mais curta que pude encontrar foi a 8700*PP + QQRR - 1300que evita a SUBSTRING()função SQL (muito longa) . Essa matemática verifica as amostras fornecidas, tenho certeza de que está certa.
  4. Use DATEADDpara adicionar o número de dias 2000-01-01que pode ser curto 2000.
  5. Tome que resultado final (quer seja uma cadeia de caracteres a partir do passo 2, ou uma data e hora do passo 4), e CONVERT()que para uma puro DATE.

Eu pensei em um ponto que eu achei uma data problemática: 000229. Esta é a única data que analisa diferentemente 19xx vs 20xx (desde 2000 foi um ano bissexto, mas 1900 não foi, devido a estranhas exceções de ano bissexto ). Por isso, porém, 000229nem sequer é uma entrada válida (já que, como mencionado, 1900 não foi um ano bissexto), não precisa ser contabilizada.

BradC
fonte
Coisa boa. Pena ISDATEque não retorne um booleano, ou que números inteiros não possam ser implicitamente convertidos em booleanos, IIFcaso contrário, você poderá salvar dois bytes.
@YiminRong Sim, a conversão implícita no SQL é muito tentativa e erro e funciona de maneira diferente em algumas funções que são muito similares. Tenho sorte de não ter que converter explicitamente os meus LEFT()e RIGHT()função resulta em inteiros antes de multiplicar-los, que teria realmente foi cancelada a minha contagem de bytes
BradC
1
Eu acho que você pode remover um caractere extra substituindo -1300,'2000'por -935,'1999'.
Razvan Socol
Idéia legal, @RazvanSocol. Tentei voltar vários múltiplos de 365 dias, mas infelizmente não consegui encontrar nada mais curto que isso.
18718 BradC
5

R , 126 bytes

function(x,a=x%/%100^(2:0)%%100,d=as.Date)'if'(a[2]<13,d(paste(19e6+x),'%Y%m%d'),d(a[3]+100*((a[2]-13)+87*a[1]),'2000-01-01'))

Experimente online!

  • -5 bytes graças à sugestão de @ Giuseppe de inserir uma entrada numérica em vez de string
digEmAll
fonte
4
Falha para entradas que representam datas anteriores a janeiro o primeiro de 1969 (por exemplo, 000101ou 681231)
Jonathan Allan
2
@ JonathanAllan: bem localizado, obrigado. Agora deve ser fixado (infelizmente exigindo mais 5 bytes ...)
digEmAll
4

JavaScript (SpiderMonkey) , 103 bytes

s=>new Date(...([a,b,c]=s.match(/../g),b>12?[2e3,0,(b-13+a*87)*100-~c]:[a,b-1,c])).toJSON().split`T`[0]

Experimente online!


.toJSONfalhará em um fuso horário UTC + X. Este código funciona, mas por mais tempo (+ 11 bytes):

s=>Intl.DateTimeFormat`ii`.format(new Date(...([a,b,c]=s.match(/../g),b>12?[2e3,0,(b-13+a*87)*100-~c]:[a,b-1,c])))
tsh
fonte
Você pode salvar 13 bytes com .toJSON().
Arnauld
E você pode salvar mais 9 bytes dividindo a sequência de entrada em três substrings de 2 caracteres.
Arnauld
@ Arnauld Eu estava originalmente tentando isso na minha máquina. Mas não funciona, pois meu fuso horário é UTC + 8. Mas pelo menos funciona no TIO.
tsh
Como definimos os idiomas por sua implementação (aqui 'Node.js em execução no TIO'), é realmente inválido?
Arnauld
Para a versão à prova de balas, você pode fazer dessa maneira para salvar 1 byte.
Arnauld
2

Python 2 , 159 bytes

from datetime import*
def f(s):D=datetime;p,q,r=map(int,(s[:2],s[2:4],s[4:]));return str(q>12and D(2000,1,1)+timedelta(100*(q-13+87*p)+r)or D(1900+p,q,r))[:10]

Experimente online!

Chas Brown
fonte
Bom truque usando em ... and ... or ...vez de ... if ... else ....
Alexander Revo
2

ABAP, 173 171 bytes

Economizou 2 bytes otimizando ainda mais a saída

Segundo as lendas, um cliente da SAP no início do século 21 disse uma vez:

Após uma guerra nuclear de destruição total, a única coisa que resta será a SAPGUI.

Ele estava certo. Hoje, em 2980, não há mais C ++, nem mais COBOL. Após a guerra, todos tiveram que reescrever seu código no SAP ABAP. Para fornecer compatibilidade retroativa com as sobras dos programas COBOL do 2800, nossos cientistas a reconstruíram como uma sub-rotina na ABAP.

FORM x USING s.DATA d TYPE d.IF s+2 < 1300.d ='19'&& s.ELSE.d ='20000101'.d = d + s+4 + 100 * ( ( s+2(2) - 13 ) + 87 * s(2) ).ENDIF.WRITE:d(4),d+4,9 d+6,8'-',5'-'.ENDFORM.

Pode ser chamado por um programa como este:

REPORT z.
  PARAMETERS date(6) TYPE c. "Text input parameter
  PERFORM x USING date.      "Calls the subroutine

Explicação do meu código:

FORM x USING s.     "Subroutine with input s
  DATA d TYPE d.    "Declare a date variable (internal format: YYYYMMDD)
  IF s+2 < 1300.    "If substring s from index 2 to end is less than 1300
    d ='19'&& s.    "the date is 19YYMMDD
  ELSE.             "a date past 2000
    d ='20000101'.  "Initial d = 2000 01 01 (yyyy mm dd)

    "The true magic. Uses ABAPs implicit chars to number cast
    "and the ability to add days to a date by simple addition.
    "Using PPQQRR as input:
    " s+4 = RR, s+2(2) = QQ, s(2) = PP
    d = d + s+4 + 100 * ( ( s+2(2) - 13 ) + 87 * s(2) ).
  ENDIF.
    "Make it an ISO date by splitting, concatenating and positioning the substrings of our date.
    WRITE:             "Explanation:
      d(4),            "d(4) = YYYY portion. WRITE adds a space after each parameter, so...
      5 '-' && d+4,    "place dash at absolute position 5. Concatenate '-' with MMDD...
      8 '-' && d+6.    "place dash at absolute position 8, overwriting DD. Concatenate with DD again.
ENDFORM.

O tipo Data do ABAP tem a propriedade ímpar a ser formatada como DDMMYYYY ao usar WRITE- pode ser dependente da localidade mesmo - apesar do formato interno ser AAAAMMDD. Mas quando usamos um seletor de substring, d(4)ele seleciona os 4 primeiros caracteres do formato interno , fornecendo-lhe AAAA.

Atualização : A formatação de saída na explicação agora está desatualizada, eu a otimizei em 2 bytes na versão golfed:

WRITE:  "Write to screen, example for 2000-10-29
 d(4),   "YYYY[space]                =>  2000
 d+4,    "MMDD[space]                =>  2000 1029
 9 d+6,  "Overwrites at position 9   =>  2000 10229
 8'-',   "Place dash at position 8   =>  2000 10-29
 5'-'.   "Place dash at position 5   =>  2000-10-29
Maz
fonte
Excelente, eu gosto. Agora tudo o que precisamos é de uma versão MUMPSe vamos sobreviver a qualquer coisa!
1
@YiminRong Thanks! Sua pergunta baseada em COBOL basicamente pediu algo assim, não tive escolha.
Maz 10/07
1

Kotlin , 222 bytes

O campo do calendário codificado codifica nomes constantes para salvar 49 bytes.

{d:Int->val p=d/10000
val q=d/100%100
val r=d%100
if(q<13)"19%02d-%02d-%02d".format(p,q,r)
else{val c=Calendar.getInstance()
c.set(2000,0,1)
c.add(5,(p*87+q-13)*100+r)
"%4d-%02d-%02d".format(c.get(1),c.get(2)+1,c.get(5))}}

Experimente online!

JohnWells
fonte