COBOL Y2K redux


36

1990年代には、COBOLのコンピュータエンジニアがそれらを変換することにより、6桁の日付フィールドを拡張する方法働いたYYYDDDところYYYであるyear - 1900DDD年の日です[001 to 366]。このスキームは、最大日付をまで延長でき2899-12-31ます。

2898年に、900年前のコードベースが失敗するため、エンジニアはパニックに陥り始めました。2898年から、彼らはタイムマシンを使用して、このアルゴリズムとそれを可能な限り広く実装するタスクとともに、単独のCodeinatorを1998年に送信しました。

PPQQRR場合は1900年代の01 ≤ QQ ≤ 12標準YYMMDD日付ですが、QQ > 12その後2000-01-01はベース100でPPRRベース87で以降の日を表すスキームを使用しQQ - 13ます。

このスキームは2899年をはるかに超えて拡張され、標準の日付と下位互換性があるため、既存のアーカイブを変更する必要はありません。

いくつかの例:

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

あなたの課題は、入力をPPQQRRISO日付として受け入れ、出力をISO日付として受け入れるプログラムまたは関数を作成することですYYYY-MM-DD。入力方法は、最も簡単なものであれば、パラメーター、コンソール、またはコマンドラインです。

あなたの娯楽のために、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
「ただし、回避できる場合はCOBOLでプログラミングしないでください。」- プログラミングのタオ
-tsh


1
@ user202729 yymmddは何年も機能しないため>=2000、これがY2Kの大失敗の全体的なポイントです。
JAD

2
@Adám-I / Oに関して非常に面倒なCOBOLの精神では、ISO yyyy-mm-dd形式である必要があると言わざるを得ません。

4
@Giuseppe-文字列と数字を実際に区別しないCOBOLの精神で、はい!たとえば、先行ゼロを入力できる場合001300

回答:


5

T-SQL、99 98バイト

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

改行は読みやすくするためのものです。暗黙的なキャストの良さに感謝します。

入力は、IOルールに従って、列iを持つ既存のテーブルtを介して行われますCHAR

次の手順を実行します。

  1. 初期チェックは、SQL関数を介して行われますISDATE()。(この関数の動作は言語設定に基づいて変化し、私のenglish-usサーバーでは期待通りに動作します)。これは有効性のチェックにすぎないことに注意してください。直接解析しようとすると、2501011925-01-01ではなく2025-01-01としてマップされます。
  2. 文字列が日付として正しく解析される場合は19、サーバーレベルの年のカットオフ設定を変更するのではなく、前に付け加えます。最終的な日付変換は最後に行われます。
  3. 文字列が日付として解析されない場合は、代わりに数値に変換します。私が見つけることができた最短の数学8700*PP + QQRR - 1300は、(非常に長い)SQL SUBSTRING()関数を回避しました。この数学は提供されたサンプルをチェックアウトします、私はそれが正しいと確信しています。
  4. を使用DATEADDして、その日数をに追加2000-01-012000ます。
  5. その最終結果(ステップ2からの文字列、またはステップ4からのDATETIME)を取得し、CONVERT()それをpureにしDATEます。

ある時点で、問題のある日付を見つけたと思いました000229。これは、19xxと20xxで異なる方法で解析される唯一の日付です(2000年はうるう年だったが、1900はそうではなかった、奇妙なうるう年の例外のため)。ただし、そのため000229、有効な入力でさえないため(前述のように、1900年はle年ではなかったため)、考慮する必要はありません。


いい物。ISDATEブール値が返されないか、整数が暗黙的にブール値に変換できないのは残念IIFですが、そうでない場合は2バイトを節約できます。

@YiminRongうん、SQLでの暗黙的なキャストは試行錯誤であり、他の点では非常に類似している一部の関数で異なる動作をします。幸運なことに、結果を乗算する前に明示的に自分の関数LEFT()RIGHT()関数の結果を整数にキャストする必要がなかったので、実際にはバイトカウントが台無しになっていたでしょう
BradC

1
に置き換え-1300,'2000'て余分な文字を削除できると思います-935,'1999'
ラズバンソコル

クールなアイデア、@ RazvanSocol。さらに365日の倍数に戻ろうとしましたが、残念ながらそれより短いものは見つかりませんでした。
BradC

5

R、126バイト

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

オンラインでお試しください!

  • 文字列の代わりに数値入力を取得する@Giuseppeの提案のおかげで、-5バイト

4
1969年1月1日より前の日付(000101またはまたは681231)を表す入力に失敗しました
ジョナサンアラン

2
@JonathanAllan:よく見つけた、ありがとう。今すぐ修正する必要があります(残念ながらさらに5バイトが必要です...)
digEmAll

4

JavaScript(SpiderMonkey)、103バイト

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]

オンラインでお試しください!


.toJSONUTC + Xタイムゾーンで失敗します。このコードは機能しますが、より長い(+11バイト):

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

あなたはできる13のバイトを保存し.toJSON()
アーナウルド

また、入力文字列を3つの2文字のサブ文字列に分割することで、さらに9バイト節約できます
アーナウルド

@Arnauld私はもともとこれを自分のマシンで試していました。しかし、私のタイムゾーンはUTC + 8であるため機能しません。ただし、少なくともTIOでは機能します。
tsh

実装(ここでは「TIOで実行されているNode.js」)によって言語を定義しているので、本当に無効ですか?
アーナウルド

防弾バージョンの場合は、そのようにして1バイトを節約できます。
アーナルド


2

ABAP、173 171バイト

出力をさらに最適化して2バイトを節約

伝説によると、21世紀初頭のSAPのお客様はかつて次のように述べています。

完全な破壊の核戦争の後、残っているのはSAPGUIです。

彼は正しかったです。今日、2980年には、C ++もCOBOLもなくなりました。戦後、全員がSAP ABAPでコードを書き直す必要がありました。2800のCOBOLプログラムの残りに後方互換性を提供するために、私たちの科学者はそれを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.

次のようなプログラムから呼び出すことができます。

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

私のコードの説明:

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.

WRITE内部形式がYYYYMMDDであるにもかかわらず、ABAPの日付型には、使用時にDDMMYYYYとしてフォーマットされる奇数プロパティがあります-ロケールに依存する場合もあります。しかし、サブストリングセレクターを使用するd(4)と、内部形式の最初の4文字が選択されるため、YYYYが得られます。

更新:説明の出力フォーマットは古くなったため、ゴルフバージョンでは2バイト最適化しました。

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

素晴らしい、私はそれが好きです。必要なのはバージョンのみでMUMPS、何でも生き残ります!

1
@YiminRongありがとう!あなたのCOBOLベースの質問は、基本的にこのようなことを求めていて、私には選択肢がありませんでした。
マズ

1

Kotlin、222バイト

49バイトを節約するために、ハードコーディングされたカレンダーフィールド名の定数。

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

オンラインでお試しください!

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.