COBOL Y2K Redux

36

In den 1990er Jahren arbeitete COBOL Informatiker einen Ausweg aus sechsstelligen Datumsfelder zu erweitern , indem sie auf die Umwandlung , YYYDDDwo YYYist das year - 1900und DDDist der Tag des Jahres [001 to 366]. Dieses Schema könnte das maximale Datum auf verlängern 2899-12-31.

Im Jahr 2898 gerieten die Ingenieure in Panik, weil ihre 900 Jahre alten Codebasen ausfallen würden. Da sie aus dem Jahr 2898 stammten, benutzten sie nur ihre Zeitmaschine, um einen einsamen Codeinator mit diesem Algorithmus und der Aufgabe, ihn so weit wie möglich zu implementieren, in das Jahr 1998 zu schicken:

Verwenden Sie ein Schema, PPQQRRin dem, wenn es sich 01 ≤ QQ ≤ 12dann um ein Standarddatum YYMMDDin den 1900er Jahren handelt, in dem Fall jedoch QQ > 12die Tage danach 2000-01-01in Basis 100 für PPund RRBasis 87 für dargestellt werden QQ - 13.

Dieses Schema geht weit über das Jahr 2899 hinaus und ist auch mit Standarddaten abwärtskompatibel, sodass keine Änderungen an vorhandenen Archiven erforderlich sind.

Einige Beispiele:

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

Ihre Herausforderung besteht darin, ein Programm oder eine Funktion zu schreiben, die Eingaben als PPQQRRISO-Datum und Ausgaben als ISO-Datum akzeptiert YYYY-MM-DD. Die Eingabemethode kann ein Parameter, eine Konsole oder eine Befehlszeile sein, was immer am einfachsten ist.

Für Ihre Unterhaltung ist hier eine nicht konkurrierende Lösung 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.

quelle
4
"Aber programmieren Sie nicht in COBOL, wenn Sie dies vermeiden können." - Das Tao der Programmierung
tsh
9
Related HNQ: Wie kann man Menschen dazu
Pipe
1
@ user202729 weil seit Jahren yymmddnicht mehr funktioniert >=2000, das ist der springende Punkt des Y2K-Debakels.
JAD
2
@ Adám - Im Geiste von COBOL, der sehr pingelig für I / O ist, muss ich sagen, dass es im ISO- yyyy-mm-ddFormat sein muss.
4
@ Giuseppe - Im Geiste von COBOL, das nicht wirklich Zeichenfolgen und Zahlen unterscheidet, ja! Vorausgesetzt, Sie können führende Nullen eingeben, z 001300.

Antworten:

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

Zeilenumbruch dient nur der Lesbarkeit. Gott sei Dank für das implizite Casting.

Die Eingabe erfolgt über eine bereits vorhandene Tabelle t mit CHARSpalte i , je unseren IO Regeln .

Führt die folgenden Schritte aus:

  1. Die Erstprüfung erfolgt über die SQL-Funktion ISDATE(). (Das Verhalten dieser Funktion hängt von den Spracheinstellungen ab und funktioniert auf meinem english-usServer wie erwartet .) Beachten Sie, dass dies nur eine Überprüfung der Gültigkeit ist. Wenn wir versuchen, es direkt zu analysieren, wird es 250101als 2025-01-01 und nicht als 1925-01-01 dargestellt.
  2. Wenn die Zeichenfolge korrekt als Datum analysiert wird, markieren Sie 19die Vorderseite (und ändern Sie nicht die Einstellung für die Jahresschneidung auf Serverebene). Die Konvertierung des Enddatums erfolgt am Ende.
  3. Wenn die Zeichenfolge nicht als Datum analysiert wird, konvertieren Sie sie stattdessen in eine Zahl. Die kürzeste Mathematik, die ich finden konnte 8700*PP + QQRR - 1300, vermeidet die (sehr lange) SQL- SUBSTRING()Funktion. Diese Mathematik sucht nach den bereitgestellten Beispielen, ich bin mir ziemlich sicher, dass es richtig ist.
  4. Verwenden Sie diese Option, um die Anzahl der DATEADDTage zu addieren 2000-01-01, die kurzgeschlossen werden können 2000.
  5. Nehmen Sie das Endergebnis (entweder eine Zeichenfolge aus Schritt 2 oder eine DATETIME aus Schritt 4) und führen Sie CONVERT()es zu einem reinen Ergebnis DATE.

Ich dachte an einem Punkt , dass ich ein problematisches Datum gefunden: 000229. Dies ist das einzige Datum, das für 19xx und 20xx unterschiedlich analysiert wird (seit 2000 war es ein Schaltjahr, 1900 jedoch nicht, da es sich um seltsame Schaltjahr-Ausnahmen handelt ). Aus diesem Grund 000229ist 1900 jedoch nicht einmal eine gültige Eingabe (da 1900, wie bereits erwähnt, kein Schaltjahr war) und muss daher nicht berücksichtigt werden.

BradC
quelle
Gutes Zeug. Es ist zu schade, ISDATEdass kein Boolescher Wert zurückgegeben wird oder dass Ganzzahlen nicht implizit in Boolescher Wert konvertiert werden können, da IIFsonst zwei Bytes eingespart werden könnten.
@YiminRong Ja, das implizite Casting in SQL ist sehr probeweise und funktioniert in einigen Funktionen, die ansonsten sehr ähnlich sind, anders. Ich hatte das Glück, dass ich meine LEFT()und RIGHT()Funktionsergebnisse nicht explizit in Ganzzahlen
umwandeln musste,
1
Ich glaube , Sie können ein zusätzliches Zeichen entfernen , indem Sie ersetzen -1300,'2000'mit -935,'1999'.
Razvan Socol
Coole Idee, @RazvanSocol. Ich habe versucht, weitere Vielfache von 365 Tagen zurückzugehen, konnte aber leider nichts Kürzeres finden.
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'))

Probieren Sie es online!

  • -5 Bytes dank @Giuseppe Vorschlag, eine numerische Eingabe anstelle eines Strings zu verwenden
digEmAll
quelle
4
Eingaben für Daten vor dem 1. Januar 1969 (z. B. 000101oder 681231) sind nicht möglich
Jonathan Allan
2
@ JonathanAllan: gut gesehen, danke. Jetzt sollte es behoben sein (leider 5 weitere Bytes erforderlich ...)
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]

Probieren Sie es online!


.toJSONschlägt in einer UTC + X-Zeitzone fehl. Dieser Code funktioniert aber länger (+ 11Bytes):

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
quelle
Sie können 13 Bytes speichern mit .toJSON().
Arnauld
Sie können weitere 9 Bytes einsparen, indem Sie die Eingabezeichenfolge in drei Teilzeichenfolgen mit zwei Zeichen aufteilen.
Arnauld
@Arnauld Ich habe das ursprünglich auf meinem Computer versucht. Aber es funktioniert nicht, da meine Zeitzone UTC + 8 ist. Aber es funktioniert zumindest mit TIO.
TSH
Da wir Sprachen durch ihre Implementierung definieren (hier 'Node.js läuft auf TIO'), ist es wirklich ungültig?
Arnauld
Bei der kugelsicheren Version können Sie auf diese Weise 1 Byte sparen.
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]

Probieren Sie es online!

Chas Brown
quelle
Nizza Trick mit ... and ... or ...anstelle von ... if ... else ....
Alexander Revo
2

ABAP, 173 171 Bytes

Spart 2 Bytes durch weitere Optimierung der Ausgabe

Der Legende nach sagte ein SAP-Kunde zu Beginn des 21. Jahrhunderts einmal:

Nach einem Atomkrieg der totalen Zerstörung bleibt nur noch SAPGUI.

Er hatte recht. Heute, 2980, gibt es kein C ++, kein COBOL mehr. Nach dem Krieg mussten alle ihren Code in SAP ABAP umschreiben. Um die Abwärtskompatibilität mit den Resten der COBOL-Programme des 2800 zu gewährleisten, haben unsere Wissenschaftler diese als Subroutine in ABAP neu erstellt.

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.

Es kann von einem Programm wie dem folgenden aufgerufen werden:

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

Erklärung meines Codes:

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.

Der Dateityp von ABAP hat die Eigenschaft odd, die bei Verwendung als DDMMYYYY formatiert werden soll. Dies kann WRITEsogar vom Gebietsschema abhängig sein, obwohl das interne Format YYYYMMDD lautet. Wenn wir jedoch einen Teilstringselektor wie d(4)diesen verwenden, werden die ersten 4 Zeichen des internen Formats ausgewählt, sodass wir JJJJ erhalten.

Update : Die Ausgabeformatierung in der Erklärung ist jetzt veraltet, ich habe sie in der Golfversion um 2 Bytes optimiert:

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
quelle
Ausgezeichnet, ich mag es. Jetzt brauchen wir nur noch eine Version in MUMPSund wir werden alles überleben!
1
@YiminRong Danke! Ihre COBOL-basierte Frage stellte sich im Grunde genommen so, dass ich keine andere Wahl hatte.
Maz
1

Kotlin , 222 Bytes

Fest codierte Kalenderfeldnamenskonstanten zum Speichern von 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))}}

Probieren Sie es online!

JohnWells
quelle