COBOL Y2K редукс


36

У 1990 - х роках, COBOL комп'ютерні інженери розробили спосіб продовжити шість цифр порожнистої дати шляхом перетворення їх в YYYDDDде YYYце year - 1900і DDDє день року [001 to 366]. Ця схема може продовжити максимальну дату до 2899-12-31.

У 2898 році інженери почали панікувати, оскільки їхні 900-річні бази кодів зазнали збою. Починаючи з 2898 року, вони просто використовували свою машину часу, щоб надіслати самотній кодифікатор до 1998 року за допомогою цього алгоритму та завдання максимально широко його реалізувати:

Використовуйте схему , PPQQRRде , якщо 01 ≤ QQ ≤ 12тоді це стандартна YYMMDDдата в 1900 - х роках, але якщо QQ > 12тоді вона являє дні після того, як 2000-01-01в базі 100 для PPі , RRкрім підстави 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

Ваше завдання полягає в тому, щоб написати програму або функцію, щоб прийняти введення як PPQQRRі вихід як дату 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 - У дусі COBOL, який дуже метушливий з входом / виводом, потрібно сказати, що він повинен бути у yyyy-mm-ddформаті ISO .

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

Розрив рядка призначений лише для читабельності. Подякуйте добру за неявну кастинг.

Введення здійснюється через попередньо існуючу таблицю t зі CHARстовпцем i , згідно з нашими правилами вводу- виводу .

Проходить наступні кроки:

  1. Початкова перевірка здійснюється через функцію SQL ISDATE(). (Поведінка цієї функції змінюється залежно від мовних налаштувань, вона працює так, як очікувалося на моєму english-usсервері). Зауважте, що це лише перевірка на дійсність, якщо ми 250101спробуємо проаналізувати її безпосередньо, вона відобразиться як 2025-01-01, а не 1925-01-01.
  2. Якщо рядок правильно розбирається як дата, торкніться 19лицьової сторони (замість того, щоб змінити налаштування відсіка на рівні сервера на рівні сервера). Кінцева дата перетворення настане в кінці.
  3. Якщо рядок не розбирається як дата, перетворіть її на число. Найкоротша математика, яку я міг знайти 8700*PP + QQRR - 1300, - це уникає (дуже довгої) SUBSTRING()функції SQL . Ця математика перевіряє надані зразки, я майже впевнений, що це правильно.
  4. Використовуйте, DATEADDщоб додати стільки днів, до 2000-01-01яких можна скоротити 2000.
  5. Візьміть цей кінцевий результат (або рядок із кроку 2, або ДАТЕТИМЕ з кроку 4), і CONVERT()це чистий DATE.

Я думав , що в якому -то момент , що я знайшов проблемну дату: 000229. Це єдина дата, яка розбирається по- різному за 19xx проти 20xx (оскільки 2000 рік був високосним, але 1900 рік не був, через дивні винятки за високосний рік ). Через те, що 000229це навіть не є дійсним вкладом (оскільки, як було сказано, 1900 рік не був високосним), тому його не потрібно враховувати.


Хороший матеріал. Це занадто погано, ISDATEщо не повертає булевого або що цілі числа не можуть бути неявно перетворені в булеві, IIFінакше ви можете зберегти два байти.

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

Спробуйте в Інтернеті!

  • -5 байт завдяки пропозиції @Giuseppe взяти числовий ввід замість рядка

4
Не вдається ввести дані, що відображають дати до першого січня 1969 року (наприклад, 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]

Спробуйте в Інтернеті!


.toJSONне вдасться в часовому поясі UTC + 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().
Арнольд

І ви можете зберегти ще 9 байт , розділивши вхідний рядок на три 2-знакові підрядки.
Арнольд

@Arnauld Я спочатку пробував це на своїй машині. Але це не працює, оскільки мій часовий пояс UTC + 8. Але це принаймні працює на TIO.
tsh

Оскільки ми визначаємо мови за їх реалізацією (тут 'Node.js працює на TIO'), чи справді вона недійсна?
Арнольд

Для пуленебезпечної версії ви можете це зробити, щоб зберегти 1 байт.
Арнольд


2

ABAP, 173 171 байт

Збережено 2 байти шляхом подальшої оптимізації результатів

Згідно з легендами, клієнт SAP на початку 21 століття колись сказав:

Після ядерної війни тотального знищення єдине, що залишиться, - це SAPGUI.

Він мав рацію. Сьогодні, у 2980 році, більше немає C ++, не більше COBOL. Після війни всі повинні були переписати свій код у SAP ABAP. Щоб забезпечити зворотну сумісність із залишками програм COBOL 2800 року, наші вчені відновили її як підпрограму в 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.

Тип дати ABAP має непарне властивість, яке слід форматувати як DDMMYYYY при використанні WRITE- може бути навіть залежним від локальної точки зору - незважаючи на те, що внутрішній формат є YYYYMMDD. Але коли ми використовуємо селектор підрядків, як d(4)він вибирає перші 4 символи внутрішнього формату, отже, дає нам РРРР.

Оновлення : формат виводу в поясненні тепер застарів, я оптимізував його на 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

Котлін , 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))}}

Спробуйте в Інтернеті!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.