Рассчитайте разницу между двумя днями.

11

Еще одна проблема манипулирования датами: P

задача

Напишите программу или функцию, которая вычисляет разницу между двумя датами, заданными пользователем.

Ввод, вывод

Как и в предыдущем случае , входными данными являются два YYYYMMDDs, разделенные пробелом , запятой ,или знаком минус -.

Пример входных значений:

20100101-20010911
20110620-20121223
19000101 20101010
33330101,19960229
00010101 99991231

Вывод представляет собой целое число, которое представляет собой разницу между двумя датами в днях.

Например, ввод 20110101-20100101дает 365, и 33320229 17000101дает 596124.

Вы можете проверить результаты здесь на здесь . (См. Комментарии rintaun ниже.) Если две даты совпадают, программа должна вернуться 0, если дата действительна (см. Оценка ).

ограничение

Конечно, вы не должны использовать какие-либо функции / классы / ..., связанные с отметкой времени или датой, и вам следует использовать григорианский календарь .

Гол

Если ваш код не соблюдает ограничение, тогда score = -∞.

По умолчанию bonus1.

  • Если ваш код работает независимо от порядка ввода (например, 20100101,20110101возвращает 365или -365) bonus+=1,.
  • Если ваш код может работать с 0 года , bonus+=0.5.
  • Если ваш код распознает недопустимый месяц (от 1 до 12) / дату (от 1 до 31), например 20109901или 34720132, и печатает E(& завершает программу или возвращает что-то подобное 0) bonus+=1,.
  • Независимо от вышеприведенного правила, если ваш код распознает недопустимые даты, например 20100230, 20100229или 20111131, и печатает E(& завершает программу или возвращает что-то подобное 0) bonus+=1,.
  • Независимо от вышеупомянутых двух правил, если ваш код распознает недопустимую входную строку, например 20100101|20100202или 2010010120100202, и печатает E(& завершает программу или возвращает что-то подобное 0) bonus+=1,.

score = floor(-4.2*code.length/bonus), Код с наибольшим количеством очков выигрывает. Если два верхних кода имеют одинаковое количество очков, выигрывают коды с наибольшим бонусом. Если два верхних кода имеют одинаковый счет и бонус, выигрывают коды с наибольшим количеством голосов.

(Должно быть: когда есть более 5 кодов, у которых больше (или равно) +1голосов.)

JiminP
источник
Считается ли 20030229 недействительной датой по третьему бонусу?
Ринтаун
@rintaun Да. Это неверно, в отличие от 20040229. : P
JiminP
1
Действительно ли WolframAlpha возвращает правильный результат? Я получаю противоречивые ответы от него и timeanddate.com . Моя программа, которая, как я считаю, работает правильно (по крайней мере, в этом случае: P), согласна с последней.
Ринтаун
@rintaun Я думаю, что Wolfram | Alpha был не прав с 365*4 + 2 + 21464 года. Спасибо за информацию!
JiminP
1
Следует отметить, что даже с timeanddate.com есть некоторые проблемы: он принимает только годы 1-3999 и автоматически настраивается на 11-дневное расхождение между юлианским и григорианским календарями для дат, предшествующих 3 сентября 1752 года. (поэтому с 17520903 по 17520914 не являются действительными датами). Помните об этом при тестировании результатов.
Ринтаун

Ответы:

3

Perl 5.14, оценка = -162

-163 -181 -196 -214 -167 -213 -234
  • code.length = 211: 208 исходных символов + 3 для запуска perl с -pопцией
  • бонус = 5,5: по умолчанию, ордер, год 0, недействительный месяц / день, недопустимая дата, полностью недопустимый ввод

Код

$_=eval(join'-',map{($y,$m,$d)=/(....)(..)(..)/;die"E\n"if!($m*$d)||$m>12||$d>30+($m&1^$m>7)-($m==2)*(2-!($y=~s/00$//r%4));$y-=($m<3)-400;$d+int(($m+9)%12*30.6+.4)+int(365.2425*$y)}/^(\d{8})[ ,-](\d{8})$/)//E

Вычисляет модифицированный юлианский номер дня для каждой даты (игнорируя корректировки, относящиеся к эпохе, чтобы сохранить длину кода) и вычитает их. (ссылка "Юлианский день" в Википедии ).

  • требует perl 5.14+ для /rопции на подстановках
  • расчет длины месяца для получения бонуса за недействительную дату: в 30+($m&1^$m>7)части указывается продолжительность любого месяца, кроме февраля; остаток корректируется на февраль в обычном или високосном году

Предположения

  • «Использовать григорианский календарь» означает пролаптический григорианский календарь для дат, предшествующих любому переходу от юлианского к григорианскому. То есть не вычитайте 11 дней для интервалов, которые пересекают, например, британский переход 3 сентября 1752 - 14 сентября 1752 года.
  • «обрабатывать год 0» означает, например, 00000101-00010101должно дать 366, так как 0 - это целое кратное 400, и поэтому год 0 является високосным годом.
DCharness
источник
После внесенных вами изменений ваша программа теперь принимает недействительные месяцы и дни, например, 20111300-20119999возвраты 2717.
Мигимару
@migimaru: Я действительно оптимизировал правильность. Штопать. Я отредактирую и, возможно, вернусь к этому.
DCharness
2

PHP, оценка: -539,1

  • 706 символов
  • Все бонусные предметы; бонус = 5,5

Код

<?php $a='(\d{4})(0[0-9]|1[0-2])([0-2][0-9]|3[01])';@$p=preg_match;if(!$p('/^(\d{8})[- ,](\d{8})$/',fgets(STDIN),$z))@die(E);unset($z[0]);sort($z);foreach($z AS$x){if(!$p('/(\d{4})(0[0-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])/',$x,$w))die(E);$n[]=$w;}$m=array(31,28,31,30,31,30,31,31,30,31,30,31);$r=0;$b=$n[0][1];$c=$n[0][2];$d=$n[0][3];$e=$n[1][1];$f=$n[1][2];$g=$n[1][3];@$t=str_pad;if((($b.$e==229)&&(!(!($b%4)+!($b%100)-!($b%400))))||($c>12))die(E);for($z=$b.$c.$d;;$s=$d,$r++){if($z==$e.$f.$g)break;if($z>$e.$f.$g)@die(E);if(@$s==$d)$d++;if((($c!=2)&&($d>$m[$c-1]))||(($c==2)&&($d>($m[$c-1]+!($b%4)-!($b%100)+!($b%400))))){$c++;$d=1;}if($c>12){$b++;$c=1;}$z=$b.$t($c,2,0,0).$t($d,2,0,0);}echo($r>0)?--$r:0;

Ungolfed

<?php
$a='(\d{4})(0[0-9]|1[0-2])([0-2][0-9]|3[01])';
@$p=preg_match;
if(!$p('/^(\d{8})[- ,](\d{8})$/',fgets(STDIN),$z)) @die(E);
unset($z[0]);
sort($z);
foreach($z AS $x)
{
        if (!$p('/(\d{4})(0[0-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])/',$x,$w)) die(E);
        $n[]=$w;
}
$m=array(31,28,31,30,31,30,31,31,30,31,30,31);
$r=0;
$b=$n[0][1];
$c=$n[0][2];
$d=$n[0][3];
$e=$n[1][1];
$f=$n[1][2];
$g=$n[1][3];
@$t=str_pad;
if ((($b.$e==229)&&(!(!($b%4)+!($b%100)-!($b%400))))||($c>12)) die(E);
for ($z=$b.$c.$d;;$s=$d,$r++)
{
        if ($z==$e.$f.$g)break;
        if ($z>$e.$f.$g)@die(E);
        if (@$s==$d)$d++;
        if ((($c!=2)&&($d>$m[$c-1]))||(($c==2)&&($d>($m[$c-1]+!($b%4)-!($b%100)+!($b%400)))))
        {
                $c++;
                $d=1;
        }
        if ($c>12)
        {
                $b++;
                $c=1;
        }
        $z=$b.$t($c,2,0,0).$t($d,2,0,0);
}
echo($r>0)?--$r:0;

Запись

Вычисляет количество дней, повторяя каждую действительную дату между двумя указанными. Это довольно медленно на больших диапазонах. Я уверен, что это не лучший способ решить эту проблему, но я потерял терпение, и это то, чем я закончил. :)

Кроме того, я знаю, что «неопрятный» код все еще не очень читабелен, но полное его переписывание потребует слишком больших усилий.

rintaun
источник
2

Ruby 1.9, оценка: -175 -186 -191 -199

  • Длина кода: 229 243 250 260 символов
  • Бонус: 5.5 (по умолчанию, заказ, год 0, неверный месяц / день, неверная дата, неверный ввод)

Код принимает ввод через стандартный ввод.

h=->n{n/4-n/100+n/400+1}
u,v=gets.split(/[ ,-]/).map{|s|s=~/^\d{8}$/?(d,e,f=[s[0,4],s[4,2],s[6,2]].map &:to_i;x=[0,y=31,28+h[d]-z=h[d-1]]+[y,30,y,30,y]*2
(!x[e]||e*f<1||f>x[e])?0:d*365+z+eval(x[0,e]*?+)+f):0}
puts (v*u>0)?u-v :?E

Заметки:

  • h возвращает количество високосных лет до этого года (включая год 0 для бонуса).
  • Регулярное выражение обрабатывает недействительный бонус ввода.
  • (!x[e]||e*f<1||f>x[e])Условие обрабатывает недействительные бонусы месяц / день / дата.
  • Результат отображается как первая дата минус вторая дата, поэтому, если вторая дата будет позже, она будет отображаться как отрицательное число.
  • Не корректирует изменение между юлианским и григорианским календарями, поэтому 33320229 17000101приводит к 596134.
migimaru
источник
Спасибо за проверку ошибок моего решения и за то, что я продолжал совершенствоваться. Мне особенно нравится ваш расчет длины в феврале здесь.
DCharness
@DCharness Спасибо, что подтолкнул меня тоже. Я понял, что в моем первоначальном представлении было много возможностей для улучшения.
Мигимару
1

Python, оценка: -478

  • символов: 455
  • бонус: обратные даты, недействительный день / месяц, недействительная дата

решение:

import re
a=re.split('[-, ]',raw_input())
def c(x):return x[0]
def f(x,y=3):return(1if x%400==0 or x%100!=0and x%4==0 else 0)if y>2 else 0
t=[31,28,31,30,31,30,31,31,30,31,30,31]
[q,w,e],[i,o,p]=sorted([map(int,[a[x][:4],a[x][4:6],a[x][6:]])for x in[0,1]],key=c)
print sum(map(f,range(q,i)))+(i-q)*365+p+sum(t[:o-1])-e-sum(t[:w-1])+f(i,o)-f(q,w)if 0<w<13and 0<e<32and 0<o<13and 0<p<32and(e<=t[w-1]or(f(q)and e==29))and(p<=t[o-1]or(f(i)and p==29))else 'E'

У меня нет версии «без гольфа», так как я ее написал. Я не проверял это должным образом, поэтому, если вы найдете ошибку - пожалуйста, прокомментируйте.

edit: надеюсь исправлена ​​ошибка, указанная в комментариях и добавлена ​​распаковка в виде [a, b], [c, d] = [[1,2], [3,4]

rplnt
источник
Извините, но когда я тестировал с оболочкой Python 2.7, недопустимые данные, такие как «20000001,20010101», не печатались E. (FYI, 0>-1>12, 0>6>12, 0>13>12возвращается False.)
JiminP
Спасибо. Я довольно новичок в питоне. При написании этого скрипта я узнал, что python делает это x<y<zсравнение или есть x if y else z. Пытался это исправить.
rplnt
@rpInt: для игры в гольф есть и [x,z][y]короче x if y else z, хотя это не всегда работает, так как в отличие от выражения if оно не ленивое.
Ли Райан
1

PHP, оценка: -516

символы: 685 676

бонус: 5.5

<? $z='/((\d{1,4})(\d\d)(\d\d))[- ,]((\d{1,4})(\d\d)(\d\d))/';if(!preg_match($z,$argv[1],$m))die('E');$s=1;if($m[1]>$m[5]){if(!preg_match($z,"$m[5] $m[1]",$m))die('E');$s=-1;}$b=array(31,28,31,30,31,30,31,31,30,31,30,31);list($x,$v,$c,$d,$e,$w,$f,$g,$h)=$m;if($d>12||1>$d||$g>12||1>$g||1>$e||1>$h||($e>$b[$d-1]&&!($d==2&&$e<30&&$c%4==0))||($h>$b[$g-1]&&!($g==2&&$h<30&&$f%4==0)))die('E');$z='array_slice';$y='array_sum';$x=$d!=$g||$e>$h;$r=$x?$b[$d-1]+$h-$e:$h-$e;$d+=$x;if($d>12){$c++;$d=1;}$r+=$d>$g?$y($z($b,$d-1,13-$d))+$y($z($b,0,$g-1)):($d!=$g?$y($z($b,$d-1,$g-$d)):0);$r+=($f-$c-($d>$g))*365;for($i=$c;$i<=$f;$i++)if($i%4==0&&$i.'0229'>$v&&$i.'0229'<$w)$r++;echo $s*$r;
Alfwed
источник
PHP-код должен <?запускаться в начале, иначе он просто печатает код.
Гарет