Redux COBOL Y2K

36

Pada 1990-an, insinyur komputer COBOL menemukan cara untuk memperluas bidang tanggal enam digit dengan mengonversinya ke YYYDDDtempat YYYadalah year - 1900dan DDDmerupakan hari dalam setahun [001 to 366]. Skema ini dapat memperpanjang tanggal maksimum ke 2899-12-31.

Pada tahun 2898, para insinyur mulai panik karena basis kode 900 tahun mereka akan gagal. Dari tahun 2898, mereka hanya menggunakan mesin waktu mereka untuk mengirim Codeinator sendiri ke tahun 1998 dengan algoritma ini dan tugas untuk mengimplementasikannya seluas mungkin:

Gunakan skema di PPQQRRmana jika 01 ≤ QQ ≤ 12maka itu adalah YYMMDDtanggal standar di tahun 1900-an, tetapi jika QQ > 12kemudian itu mewakili hari-hari setelahnya 2000-01-01di basis 100 untuk PPdan RRtetapi basis 87 untuk QQ - 13.

Skema ini melampaui tahun 2899 dan juga kompatibel dengan tanggal standar, jadi tidak diperlukan modifikasi arsip yang ada.

Beberapa contoh:

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

Tantangan Anda adalah menulis program atau fungsi untuk menerima input sebagai PPQQRRdan output sebagai tanggal ISO YYYY-MM-DD. Metode input dapat berupa parameter, konsol atau baris perintah, apa pun yang termudah.

Untuk hiburan Anda, berikut adalah solusi tidak bersaing di 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.

sumber
4
"Tapi jangan memprogram di COBOL jika kamu bisa menghindarinya." - The Tao of Programming
tsh
1
@ user202729 karena yymmddtidak berfungsi selama bertahun-tahun >=2000, itulah inti dari bencana Y2K.
JAD
2
@ Adám - Dalam semangat COBOL yang sangat cerewet I / O, saya harus mengatakan itu harus dalam yyyy-mm-ddformat ISO .
4
@Giuseppe - Dalam semangat COBOL yang tidak benar-benar membedakan string dan angka, ya! Asalkan Anda dapat memasukkan nol di depan, mis 001300.

Jawaban:

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

Istirahat baris hanya untuk keterbacaan. Terima kasih Tuhan atas casting implisit.

Input adalah melalui tabel t yang sudah ada sebelumnya dengan CHARkolom i , sesuai aturan IO kami .

Ikuti langkah-langkah berikut:

  1. Pemeriksaan awal adalah melalui fungsi SQL ISDATE(). (Perilaku fungsi ini berubah berdasarkan pengaturan bahasa, berfungsi seperti yang diharapkan pada english-usserver saya ). Perhatikan bahwa ini hanya pemeriksaan validitas, jika kami mencoba menguraikannya secara langsung, ini akan dipetakan 250101sebagai 2025-01-01, bukan 1925-01-01.
  2. Jika string diurai dengan benar sebagai tanggal, tempelkan 19pada bagian depan (alih-alih ubah pengaturan cutoff tahun level server). Konversi tanggal akhir akan datang di akhir.
  3. Jika string tidak diuraikan sebagai tanggal, gantilah ke angka. Matematika terpendek yang dapat saya temukan adalah 8700*PP + QQRR - 1300, yang menghindari SUBSTRING()fungsi SQL (sangat panjang) . Matematika ini memeriksa sampel yang disediakan, saya cukup yakin itu benar.
  4. Gunakan DATEADDuntuk menambahkan banyak hari 2000-01-01, yang dapat disingkat 2000.
  5. Ambil hasil akhir itu (baik string dari langkah 2, atau DATETIME dari langkah 4), dan CONVERT()itu menjadi murni DATE.

Saya pikir pada satu titik yang saya temukan tanggal bermasalah: 000229. Ini adalah satu-satunya tanggal yang mem-parsing berbeda untuk 19xx vs 20xx (sejak tahun 2000 adalah tahun kabisat, tetapi tahun 1900 tidak, karena pengecualian tahun kabisat yang aneh ). Karena itu, meskipun, 000229bahkan bukan input yang valid (karena, sebagaimana disebutkan, 1900 bukan tahun kabisat), jadi tidak harus diperhitungkan.

BradC
sumber
Barang bagus. Sayang ISDATEsekali tidak mengembalikan boolean, atau integer tidak dapat secara implisit dikonversi menjadi boolean IIFjika tidak, Anda dapat menyimpan dua byte.
@ YiminRong Yap, casting implisit dalam SQL sangat trial-and-error, dan bekerja secara berbeda dalam beberapa fungsi yang sebaliknya sangat mirip. Saya beruntung saya tidak harus secara eksplisit melemparkan hasil fungsi LEFT()dan RIGHT()ke bilangan bulat sebelum mengalikannya, itu akan benar-benar mengacaukan jumlah byte saya
BradC
1
Saya pikir Anda dapat menghapus karakter tambahan dengan menggantinya -1300,'2000'dengan -935,'1999'.
Razvan Socol
Ide keren, @RazvanSocol. Saya mencoba kembali kelipatan 365 hari, tetapi sayangnya tidak menemukan yang lebih pendek dari itu.
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'))

Cobalah online!

  • -5 byte berkat saran @Giuseppe untuk mengambil input numerik dan bukan string
menggali semua
sumber
4
Gagal untuk input yang mewakili tanggal sebelum Januari 1969 pertama (mis. 000101Atau 681231)
Jonathan Allan
2
@ JonathanAllan: terlihat dengan baik, terima kasih. Sekarang ini harus diperbaiki (sayangnya membutuhkan 5 byte lebih ...)
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]

Cobalah online!


.toJSONakan gagal pada zona waktu UTC + X. Kode ini berfungsi, tetapi lebih lama (+ 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])))
tsh
sumber
Anda dapat menyimpan 13 byte dengan .toJSON().
Arnauld
Dan Anda dapat menyimpan 9 byte lebih banyak dengan memecah string input menjadi tiga substring 2-char.
Arnauld
@Arnauld Saya awalnya mencoba ini di komputer saya. Tapi itu tidak berfungsi karena zona waktu saya adalah UTC + 8. Tapi setidaknya berfungsi di TIO.
tsh
Karena kita mendefinisikan bahasa dengan implementasinya (di sini 'Node.js berjalan di TIO'), apakah ini benar-benar tidak valid?
Arnauld
Untuk versi anti peluru, Anda dapat melakukannya dengan cara itu untuk menghemat 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]

Cobalah online!

Chas Brown
sumber
Trik yang bagus untuk digunakan ... and ... or ...alih-alih ... if ... else ....
Alexander Revo
2

ABAP, 173 171 byte

Disimpan 2 byte dengan lebih mengoptimalkan output

Menurut legenda, seorang pelanggan SAP di awal abad ke-21 pernah berkata:

Setelah perang nuklir kehancuran total, satu hal yang tersisa adalah SAPGUI.

Dia benar. Hari ini, pada 2980, tidak ada lagi C ++, tidak ada lagi COBOL. Setelah perang semua orang harus menulis ulang kode mereka dalam SAP ABAP. Untuk memberikan kompatibilitas ke belakang pada sisa-sisa program COBOL 2800, para ilmuwan kami membangunnya kembali sebagai subrutin di 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.

Itu bisa dipanggil oleh program seperti ini:

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

Penjelasan kode saya:

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.

Tipe Tanggal ABAP memiliki properti ganjil yang akan diformat sebagai DDMMYYYY saat menggunakan WRITE- bahkan mungkin bergantung pada lokal - meskipun format internal YYYYMMDD. Tetapi ketika kita menggunakan pemilih substring seperti d(4)itu memilih 4 karakter pertama dari format internal , maka memberi kita YYYY.

Pembaruan : Pemformatan keluaran dalam penjelasan sekarang kedaluwarsa, saya mengoptimalkannya dengan 2 byte dalam versi golf:

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
sumber
Luar biasa, saya menyukainya. Sekarang yang kita butuhkan hanyalah versi MUMPSdan kita akan selamat dari apapun!
1
@YiminRong Terima kasih! Pertanyaan berbasis COBOL Anda pada dasarnya menanyakan hal seperti ini, saya tidak punya pilihan.
Maz
1

Kotlin , 222 byte

Konstanta bidang Kalender nama kode keras untuk menyimpan 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))}}

Cobalah online!

JohnWells
sumber