КОБОЛ Y2K редукс

36

В 1990 - х годах, COBOL компьютерные инженеры разработали способ продлить шесть цифр полой даты путем преобразования их в YYYDDDгде YYYэто year - 1900и DDDесть день года [001 to 366]. Эта схема может продлить максимальную дату до 2899-12-31.

В 2898 году инженеры начали паниковать, потому что их 900-летние базы кода потерпели неудачу. Начиная с 2898 года, они просто использовали свою машину времени, чтобы отправить одинокий Codeinator в 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

Разрыв строки предназначен только для удобства чтения. Слава Богу за неявное приведение.

Ввод осуществляется с уже существующей таблицей т с 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, либо DATETIME из шага 4), и CONVERT()он в чистом виде DATE.

В какой-то момент я подумал, что нашел проблемную дату 000229. Это единственная дата, которая по- разному анализируется для 19xx и 20xx (с 2000 года был високосный год, а 1900 - нет, из-за странных исключений високосного года ). Из-за этого, тем не менее, 000229даже не является действительным вкладом (поскольку, как уже упоминалось, 1900 год не был високосным годом), поэтому его не нужно учитывать.

BradC
источник
Хорошая вещь. Это слишком плохо , 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 принимать числовой ввод вместо строки
digEmAll
источник
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

Python 2 , 159 байт

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]

Попробуйте онлайн!

Час Браун
источник
Хороший трюк с использованием ... and ... or ...вместо ... if ... else ....
Александр Рево
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.

Тип Date в ABAP имеет нечетное свойство, которое при форматировании должно быть отформатировано как DDMMYYYY, WRITEможет зависеть даже от локали, несмотря на то, что внутренним форматом является YYYYMMDD. Но когда мы используем селектор подстроки, 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

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

Попробуйте онлайн!

JohnWells
источник