Redux Y2K


36

Trong những năm 1990, COBOL kỹ sư máy tính làm việc ra một cách để mở rộng trường ngày sáu chữ số bằng cách chuyển đổi chúng sang YYYDDDnơi YYYyear - 1900DDDlà ngày trong năm [001 to 366]. Chương trình này có thể kéo dài ngày tối đa đến 2899-12-31.

Vào năm 2898, các kỹ sư bắt đầu hoảng loạn vì cơ sở mã 900 năm tuổi của họ sẽ thất bại. Từ năm 2898, họ chỉ sử dụng cỗ máy thời gian của mình để gửi một Codeinator đơn độc đến năm 1998 với thuật toán này và nhiệm vụ khiến nó được triển khai rộng rãi nhất có thể:

Sử dụng một lược đồ PPQQRRtrong đó nếu 01 ≤ QQ ≤ 12đó là một YYMMDDngày tiêu chuẩn vào những năm 1900, nhưng nếu QQ > 12sau đó nó đại diện cho những ngày sau 2000-01-01trong cơ sở 100 cho PPRRnhưng cơ sở 87 cho QQ - 13.

Chương trình này mở rộng ra xa hơn năm 2899 và cũng tương thích ngược với ngày tiêu chuẩn, do đó không cần sửa đổi tài liệu lưu trữ hiện có.

Vài ví dụ:

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

Thách thức của bạn là viết một chương trình hoặc chức năng để chấp nhận đầu vào PPQQRRvà đầu ra dưới dạng ngày ISO YYYY-MM-DD. Phương thức nhập liệu có thể là tham số, bàn điều khiển hoặc dòng lệnh, bất cứ điều gì là dễ nhất.

Để bạn giải trí, đây là một giải pháp không thể lọc trong 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
"Nhưng đừng lập trình trong COBOL nếu bạn có thể tránh nó." - Đạo của lập trình
tsh


1
@ user202729 vì yymmddkhông hoạt động trong nhiều năm >=2000, đó là toàn bộ vấn đề của sự kiện Y2K.
JAD

2
@ Adám - Theo tinh thần của COBOL, I / O rất phức tạp, tôi phải nói rằng nó cần phải ở yyyy-mm-ddđịnh dạng ISO .

4
@Giuseppe - Theo tinh thần của COBOL, nơi không thực sự phân biệt các chuỗi và số, vâng! Với điều kiện bạn có thể nhập số không hàng đầu, vd 001300.

Câu trả lời:


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

Ngắt dòng chỉ dành cho khả năng đọc. Cảm ơn lòng tốt cho đúc ngầm.

Đầu vào là thông qua một bảng t có sẵn với CHARcột i , theo quy tắc IO của chúng tôi .

Đi qua các bước sau:

  1. Kiểm tra ban đầu là thông qua chức năng SQL ISDATE(). (Hành vi của chức năng này thay đổi dựa trên cài đặt ngôn ngữ, nó hoạt động như mong đợi trên english-usmáy chủ của tôi ). Lưu ý đây chỉ là kiểm tra tính hợp lệ, nếu chúng tôi cố gắng phân tích trực tiếp, nó sẽ ánh xạ 250101thành 2025-01-01, không phải 1925-01-01.
  2. Nếu chuỗi phân tích chính xác như một ngày, hãy 19giải quyết ở mặt trước (thay vì thay đổi cài đặt cắt năm cấp độ máy chủ). Chuyển đổi ngày cuối cùng sẽ đến vào cuối.
  3. Nếu chuỗi không phân tích cú pháp như một ngày, thay vào đó hãy chuyển đổi nó thành một số. Toán học ngắn nhất tôi có thể tìm thấy là 8700*PP + QQRR - 1300, nó tránh SUBSTRING()hàm SQL (rất dài) . Toán học này kiểm tra các mẫu được cung cấp, tôi khá chắc chắn rằng nó đúng.
  4. Sử dụng DATEADDđể thêm nhiều ngày 2000-01-01, có thể rút ngắn 2000.
  5. Lấy kết quả cuối cùng đó (hoặc một chuỗi từ bước 2, hoặc một dữ liệu từ bước 4) và CONVERT()chuỗi đó thành thuần túy DATE.

Tôi đã nghĩ tại một thời điểm mà tôi tìm thấy một ngày có vấn đề : 000229. Đây là ngày duy nhất phân tích cú pháp khác nhau cho 19xx so với 20xx (kể từ năm 2000 là một năm nhuận, nhưng năm 1900 thì không, do các trường hợp ngoại lệ trong năm nhuận ). Do đó, mặc dù, 000229thậm chí không phải là một đầu vào hợp lệ (vì, như đã đề cập, 1900 không phải là một năm nhuận), do đó không cần phải tính đến.


Đồ tốt. Thật tệ ISDATEkhi không trả về boolean hoặc số nguyên không thể được chuyển đổi hoàn toàn thành boolean IIFnếu không bạn có thể lưu hai byte.

@YiminRong Yep, việc truyền ẩn trong SQL rất dễ xảy ra lỗi và hoạt động khác nhau trong một số chức năng rất giống nhau. Tôi may mắn vì tôi đã không phải phân phối rõ ràng kết quả LEFT()RIGHT()chức năng của mình cho các số nguyên trước khi nhân chúng, điều đó thực sự sẽ làm rối loạn số byte của tôi
BradC

1
Tôi nghĩ bạn có thể loại bỏ một nhân vật phụ bằng cách thay thế -1300,'2000'bằng -935,'1999'.
Razvan Socol

Ý tưởng tuyệt vời, @RazvanSatio. Tôi đã cố gắng quay lại bội số của 365 ngày, nhưng tiếc là không thể tìm thấy thứ gì ngắn hơn thế.
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'))

Hãy thử trực tuyến!

  • -5 byte nhờ đề xuất @Giuseppe để lấy đầu vào số thay vì chuỗi

4
Thất bại cho các đầu vào đại diện cho ngày trước tháng 1 năm 1969 đầu tiên (ví dụ 000101hoặc 681231)
Jonathan Allan

2
@Jonathan ALLan: phát hiện tốt, cảm ơn. Bây giờ nó đã được sửa (không may cần thêm 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]

Hãy thử trực tuyến!


.toJSONsẽ thất bại trên múi giờ UTC + X. Mã này hoạt động, nhưng dài hơn (+ 11byte):

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

Bạn có thể lưu 13 byte với .toJSON().
Arnauld

Và bạn có thể lưu thêm 9 byte bằng cách chia chuỗi đầu vào thành ba chuỗi 2 char.
Arnauld

@Arnauld Ban đầu tôi đã thử cái này trên máy của tôi. Nhưng nó không hoạt động vì múi giờ của tôi là UTC + 8. Nhưng nó ít nhất hoạt động trên TIO.
tsh

Vì chúng tôi xác định ngôn ngữ bằng cách triển khai chúng (ở đây 'Node.js chạy trên TIO'), điều đó có thực sự không hợp lệ?
Arnauld

Đối với phiên bản chống đạn, bạn có thể làm theo cách đó để tiết kiệm 1 byte.
Arnauld


2

ABAP, 173 171 byte

Đã lưu 2 byte bằng cách tối ưu hóa thêm đầu ra

Theo các truyền thuyết, một khách hàng của SAP vào đầu thế kỷ 21 đã từng nói:

Sau một cuộc chiến tranh hạt nhân hủy diệt hoàn toàn, một thứ còn lại sẽ là SAPGUI.

Anh ấy đã đúng. Ngày nay, vào năm 2980, không còn C ++, không còn COBOL nữa. Sau chiến tranh, mọi người phải viết lại mã của họ trong SAP ABAP. Để cung cấp khả năng tương thích ngược với phần còn lại của các chương trình COBOL của 2800, các nhà khoa học của chúng tôi đã xây dựng lại nó như một chương trình con trong 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.

Nó có thể được gọi bởi một chương trình như thế này:

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

Giải thích về mã của tôi:

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.

Loại Ngày của ABAP có thuộc tính lẻ được định dạng là DDMMYYYY khi sử dụng WRITE- có thể phụ thuộc vào địa phương - mặc dù định dạng bên trong là YYYYMMDD. Nhưng khi chúng tôi sử dụng bộ chọn chuỗi con như d(4)nó sẽ chọn 4 ký tự đầu tiên của định dạng bên trong , do đó mang lại cho chúng tôi YYYY.

Cập nhật : Định dạng đầu ra trong phần giải thích hiện đã lỗi thời, tôi đã tối ưu hóa nó bằng 2 byte trong phiên bản 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

Tuyệt vời, tôi thích nó. Bây giờ tất cả những gì chúng ta cần là một phiên bản MUMPSvà chúng ta sẽ tồn tại bất cứ điều gì!

1
@YiminRong Cảm ơn! Câu hỏi dựa trên COBOL của bạn về cơ bản yêu cầu một cái gì đó như thế này, tôi không có lựa chọn nào khác.
Maz

1

Kotlin , 222 byte

Các tên trường Lịch được mã hóa cứng để lưu 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))}}

Hãy thử trực tuyến!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.