COBOL Y2K redux


36

1990'larda, COBOL bilgisayar mühendisleri dönüştürerek altı haneli tarihi alanlarını genişletmek için bir çıkış yolu çalıştı YYYDDDnerede YYYolduğunu year - 1900ve DDDyılın günüdür [001 to 366]. Bu şema en fazla tarihi uzatabilir 2899-12-31.

2898 yılında, mühendisler paniklemeye başladılar çünkü 900 yıllık kod üsleri başarısız olacaktı. 2898 yılından itibaren, zaman makinelerini bu algoritma ve mümkün olduğunca geniş bir şekilde uygulayabilmek için 1998 yılına yalnız bir Codeinator göndermek için kullandılar:

Bir şemayı kullanın PPQQRReğer 01 ≤ QQ ≤ 12o zaman bir standarttır YYMMDD1900'lü yıllarda tarih, ama eğer QQ > 12o zaman günler sonra temsil 2000-01-01için tabanın 100 PPve RRiçin değil, tabanın 87 QQ - 13.

Bu program 2899 yılının çok ötesine uzanıyor ve standart tarihlerle geriye dönük olarak da uyumlu, bu nedenle mevcut arşivlerin değiştirilmesine gerek yok.

Bazı örnekler:

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

Buradaki zorluk, girdiyi PPQQRRve çıktıyı ISO tarihi olarak kabul edecek bir program veya işlev yazmaktır YYYY-MM-DD. Giriş yöntemi, en kolay olanı parametre, konsol veya komut satırı olabilir.

Eğlenceniz için, COBOL-85’te rekabetçi olmayan bir çözüm:

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
"Ama eğer kaçınabiliyorsan, COBOL'de programlamayın." - Programlama Tao
tsh


1
@ user202729 yymmdd, yıllardır çalışmadığı için >=2000, Y2K bozulma noktasının tamamı budur.
JAD,

2
@ Adám - G / Ç ile çok titiz olan COBOL ruhunda, ISO yyyy-mm-ddformatında olması gerektiğini söylemeliyim .

4
@Giuseppe - Dizeleri ve sayıları gerçekten ayırt etmeyen COBOL ruhu, evet! Sağlanan önde gelen sıfırları girebilirsiniz, örn 001300.

Yanıtlar:


5

T-SQL, 99 98 bayt

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

Satır sonu yalnızca okunabilirlik içindir. Örtük döküm için çok şükür.

Girdi önceden var olan bir tablo yoluyladır t ile CHARkolon i , bizim ES kurallarına göre .

Aşağıdaki adımlardan geçer:

  1. İlk kontrol, SQL işlevi üzerinden yapılır ISDATE(). (Bu işlevin davranışı dil ayarlarına göre değişir, english-ussunucumda beklendiği gibi çalışır ). Bunun sadece geçerliliği 250101denetlediğini, doğrudan ayrıştırmayı denememiz durumunda 1925-01-01 değil 2025-01-01 olarak eşleyeceğini unutmayın.
  2. Dizi tarih olarak doğru ayrışıyorsa 19, ön tarafa dokunun (sunucu düzeyinde yıl kesim ayarını değiştirmek yerine). Son tarih dönüşümü sonunda gelecek.
  3. Dize yoksa değil tarih olarak ayrıştırmak yerine bir sayıya dönüştürün. Bulabildiğim en kısa matematik 8700*PP + QQRR - 1300, (çok uzun) SQL SUBSTRING()işlevinden kaçınmaktı . Bu matematik verilen örnekleri araştırıyor, doğru olduğuna eminim.
  4. Kısa sürecek olan DATEADDo günleri eklemek için kullanın .2000-01-012000
  5. Bu son sonucu (2. adımdan bir dize veya 4. adımdan bir DATETIME) alın ve CONVERT()onu saf hale getirin DATE.

Ben sorunlu bir tarih bulduğu bir noktada düşündü: 000229. Bu, 19xx ile 20xx arasında farklı bir şekilde ayrıştırılan tek tarihtir (2000'den beri bir artık yıldı, ancak 1900, artık artık istisnalar nedeniyle değildi ). Bu nedenle, 000229geçerli bir girdi bile değil (1900’de artık yıl olmadığı için), hesaba katılması gerekmiyor.


İyi şeyler. Çok kötü ISDATEbir boole döndürmüyor veya tamsayılar dolaylı olarak boole dönüştürülemiyor, IIFaksi takdirde iki bayt kazanabilirsiniz.

@YiminRong Yep, SQL'de dolaylı döküm çok deneme ve yanılmadır, aksi halde çok benzer olan bazı işlevlerde farklı çalışır. Şanslıyım ki, LEFT()ve RIGHT()işlev sonuçlarımı
çarpmadan

1
Sana değiştirerek fazladan karakteri kaldırın düşünüyorum -1300,'2000'ile -935,'1999'.
Razvan Socol

Harika bir fikir, @RazvanSocol. 365 gün katları geri dönmeyi denedim, ancak ne yazık ki bundan daha kısa bir şey bulamadım.
BradC

5

R , 126 bayt

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

Çevrimiçi deneyin!

  • @Giuseppe önerisine string yerine sayısal bir giriş yapması sayesinde -5 bayt

4
İlk 1969 Ocak ayından önceki tarihleri ​​temsil eden girdiler için başarısız (örneğin 000101veya 681231)
Jonathan Allan

2
@JonathanAllan: benekli, teşekkürler. Şimdi düzeltilmesi gerekiyor (maalesef 5 bayt daha gerekiyor ...)
digEmAll

4

JavaScript (SpiderMonkey) , 103 bayt

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]

Çevrimiçi deneyin!


.toJSONUTC + X saat diliminde başarısız olur. Bu kod çalışıyor ancak daha uzun (+ 11 bayt):

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

Sen edebilirsiniz 13 bayt kaydetmek ile .toJSON().
Arnauld,

Ve giriş dizesini üç adet 2 karakterli alt dizeye bölerek 9 bayt daha tasarruf edebilirsiniz .
Arnauld,

@Aslında makinemde bunu deniyordum. Fakat saat dilimim UTC + 8 olduğundan, çalışmaz. Ama en azından TIO'da çalışıyor.
tsh

Dilleri uygulamalarıyla tanımladığımızdan (burada 'TIO'da çalışan Node.js)', gerçekten geçersiz mi?
Arnauld,

Kurşun geçirmez sürümde 1 bayt kaydetmek için bu işlemi yapabilirsiniz .
Arnauld,

2

Python 2 , 159 bayt

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]

Çevrimiçi deneyin!


İyi numara kullanarak ... and ... or ...yerine ... if ... else ....
Alexander Revo

2

ABAP, 173 171 bayt

Çıkışı daha da optimize ederek 2 bayt kaydedildi

Efsanelere göre, 21. yüzyılın başlarında bir SAP müşterisi bir keresinde şöyle dedi:

Nükleer bir imha savaşından sonra, kalan tek şey SAPGUI olacak.

Haklıydı. Bugün, 2980'de artık C ++ yok, COBOL yok. Savaştan sonra herkes kodlarını SAP ABAP'ta yeniden yazmak zorunda kaldı. 2800’lerin COBOL programlarının kalanlarına geriye dönük uyumluluk sağlamak için bilim adamlarımız ABAP’ta alt rutin olarak yeniden inşa ettiler.

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.

Bu gibi bir program tarafından çağrılabilir:

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

Kodumun açıklaması:

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.

ABAP’ın Tarih türü, WRITEiç biçimin YYYYMMDD olmasına rağmen - yerel ayarlara bağlı olsa bile - DDMMYYYY olarak biçimlendirilecek tek özelliğe sahiptir . Ancak, bir alt dize seçici kullandığımızda d(4), biçimin ilk 4 karakterini seçer , bu nedenle bize YYYY verir.

Güncelleme : Açıklamadaki çıktı biçimlendirme artık modası geçmiş, golf versiyonunda 2 byte optimize ettim:

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

Mükemmel, hoşuma gitti. Şimdi tek ihtiyacımız olan sürüm MUMPSve bir şey hayatta kalacağız!

1
@YiminRong Teşekkürler! COBOL-temelli sorunuz temelde böyle bir şey istedi, başka seçeneğim yoktu.
Maz,

1

Kotlin , 222 bayt

Sabit kodlu Takvim alanı, 49 bayttan tasarruf etmek için sabitleri adlandırır.

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

Çevrimiçi deneyin!

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.