COBOL Y2K redux


36

Negli anni '90, gli ingegneri informatici COBOL hanno escogitato un modo per estendere i campi di data a sei cifre convertendoli in YYYDDDdove si YYYtrova year - 1900ed DDDè il giorno dell'anno [001 to 366]. Questo schema potrebbe estendere la data massima a 2899-12-31.

Nell'anno 2898, gli ingegneri iniziarono a farsi prendere dal panico perché le loro basi di codice di 900 anni stavano per fallire. Essendo dell'anno 2898, hanno appena usato la loro macchina del tempo per inviare un Codeinator solitario all'anno 1998 con questo algoritmo e il compito di implementarlo nel modo più ampio possibile:

Utilizzare uno schema in PPQQRRcui se 01 ≤ QQ ≤ 12quindi è una YYMMDDdata standard nel 1900, ma se QQ > 12quindi rappresenta i giorni successivi 2000-01-01nella base 100 per PPe RRma base 87 per QQ - 13.

Questo schema si estende ben oltre l'anno 2899 ed è anche retrocompatibile con le date standard, quindi non sono necessarie modifiche agli archivi esistenti.

Qualche esempio:

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

La tua sfida è scrivere un programma o una funzione per accettare input come PPQQRR e output come data ISO YYYY-MM-DD. Il metodo di input può essere un parametro, una console o una riga comandi, qualunque sia la più semplice.

Per il tuo divertimento, ecco una soluzione non competitiva in 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.

4
"Ma non programmare in COBOL se puoi evitarlo." - Il Tao della Programmazione
TSH


1
@ user202729 perché yymmddnon funziona da anni >=2000, questo è l'intero punto della debacle Y2K.
JAD

2
@Adám - Nello spirito di COBOL che è I / O molto esigente, dovrò dire che deve essere in yyyy-mm-ddformato ISO .

4
@Giuseppe - Nello spirito di COBOL che non differenzia realmente stringhe e numeri, sì! A condizione che sia possibile inserire zeri iniziali, ad es 001300.

Risposte:


5

T-SQL, 99 98 byte

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

L'interruzione di riga è solo per leggibilità. Grazie al cielo per il casting implicito.

L'input avviene tramite una tabella preesistente t con CHARcolonna i , secondo le nostre regole di IO .

Passa attraverso i seguenti passaggi:

  1. Il controllo iniziale avviene tramite la funzione SQL ISDATE(). (Il comportamento di questa funzione cambia in base alle impostazioni della lingua, funziona come previsto sul mioenglish-us server). Nota che questo è solo un controllo della validità, se provassimo ad analizzarlo direttamente, verrebbe 250101mappato come 2025-01-01, non 1925-01-01.
  2. Se la stringa viene analizzata correttamente come data, virata 19 in primo piano (anziché modificare l'impostazione di interruzione dell'anno a livello di server). La conversione della data finale arriverà alla fine.
  3. Se la stringa non viene analizzata come data, convertirla invece in un numero. La matematica più breve che potessi trovare fu 8700*PP + QQRR - 1300, che evita il (molto lungo) SQLSUBSTRING() funzione . Questa matematica verifica i campioni forniti, sono abbastanza sicuro che sia giusto.
  4. Utilizzare DATEADDper aggiungere molti giorni a 2000-01-01, che può essere abbreviato 2000 .
  5. Prendi quel risultato finale (o una stringa dal passaggio 2, o un DATETIME dal passaggio 4) e CONVERT()raggiungilo a puro DATE.

Ho pensato a un certo punto che ho trovato una data problematica: 000229. Questa è l'unica data che analizza in modo diverso per 19xx vs 20xx (dal 2000 era un anno bisestile, ma 1900 non lo era, a causa di strane eccezioni nell'anno bisestile ). Per questo motivo, tuttavia, 000229non è nemmeno un input valido (poiché, come accennato, il 1900 non è stato un anno bisestile), quindi non deve essere preso in considerazione.


Roba buona. È un peccato ISDATEche non restituisca un valore booleano o che i numeri interi non possano essere implicitamente convertiti in booleani, IIFaltrimenti potresti risparmiare due byte.

@YiminRong Sì, il cast implicito in SQL è molto difficile, e funziona in modo diverso in alcune funzioni che sono altrimenti molto simili. Sono fortunato a non dover LEFT()RIGHT()
trasmettere

1
Penso che puoi rimuovere un personaggio aggiuntivo sostituendolo -1300,'2000'con -935,'1999'.
Razvan Socol,

Bella idea, @RazvanSocol. Ho provato a tornare indietro di altri multipli di 365 giorni, ma sfortunatamente non sono riuscito a trovare niente di più breve.
BradC,

5

R , 126 byte

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'))

Provalo online!

  • -5 byte grazie al suggerimento di @Giuseppe di prendere un input numerico invece della stringa

4
Non riesce a immettere gli input che rappresentano le date precedenti a gennaio il primo 1969 (ad esempio 000101o 681231)
Jonathan Allan,

2
@JonathanAllan: ben individuato, grazie. Ora dovrebbe essere corretto (purtroppo richiede altri 5 byte ...)
digEmAll

4

JavaScript (SpiderMonkey) , 103 byte

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]

Provalo online!


.toJSONfallirà in un fuso orario UTC + X. Questo codice funziona, ma più a lungo (+ 11 byte):

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])))

È possibile salvare 13 byte con .toJSON().
Arnauld,

E puoi salvare altri 9 byte dividendo la stringa di input in tre sottostringhe a 2 caratteri.
Arnauld,

@Arnauld Inizialmente stavo provando questo sul mio computer. Ma non funziona poiché il mio fuso orario è UTC + 8. Ma almeno funziona su TIO.
TSH

Dato che definiamo le lingue in base alla loro implementazione (qui 'Node.js in esecuzione su TIO'), è davvero non valido?
Arnauld,

Per la versione a prova di proiettile, puoi farlo in questo modo per salvare 1 byte.
Arnauld,

2

Python 2 , 159 byte

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]

Provalo online!


Bel trucco usando ... and ... or ...invece di ... if ... else ....
Alexander Revo,

2

ABAP, 173 171 byte

Salvato 2 byte ottimizzando ulteriormente l'output

Secondo le leggende, un cliente SAP all'inizio del XXI secolo disse una volta:

Dopo una guerra nucleare di distruzione totale, l'unica cosa rimasta sarà SAPGUI.

Lui aveva ragione. Oggi, nel 2980, non c'è più C ++, né più COBOL. Dopo la guerra tutti hanno dovuto riscrivere il loro codice in SAP ABAP. Per garantire la retrocompatibilità con gli avanzi dei programmi COBOL del 2800, i nostri scienziati lo hanno ricostruito come una subroutine in 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.

Può essere chiamato da un programma come questo:

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

Spiegazione del mio codice:

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.

Il tipo di data ABAP ha la proprietà dispari da formattare come GGMMAAA durante l'utilizzo WRITE - potrebbe dipendere anche dalle locali - nonostante il formato interno sia AAAAMMGG. Ma quando usiamo un selettore di sottostringa come d(4)questo seleziona i primi 4 caratteri del formato interno , dandoci quindi AAAA.

Aggiornamento : la formattazione dell'output nella spiegazione è ora obsoleta, l'ho ottimizzata di 2 byte nella versione golfata:

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

Eccellente, mi piace. Ora tutto ciò di cui abbiamo bisogno è una versione MUMPSe sopravviveremo a qualsiasi cosa!

1
@YiminRong Grazie! La tua domanda su COBOL fondamentalmente ha chiesto qualcosa del genere, non avevo scelta.
Maz

1

Kotlin , 222 byte

Costanti dei nomi dei campi del calendario con codifica rigida per salvare 49 byte.

{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))}}

Provalo online!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.