Обчисліть різницю між двома днями.


11

Ще одна проблема маніпулювання датою: P

Завдання

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

Введення-виведення

Подібно до попереднього , входи - це два YYYYMMDDs, розділені пробілом , комою ,чи знаком мінус -.

Приклад вхідних значень:

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

Вихід - ціле число, яке відрізняється між двома датами, в днях.

Наприклад, вхідні 20110101-20100101врожаї 365та 33320229 17000101врожайність 596124.

Ви можете перевірити результати тут тут . (Див. Коментарі rintaun нижче.) Якщо дві дати однакові, програма повинна повернутися 0, якщо дата є дійсною (див. Оцінка ).

Обмеження

Звичайно, ви не повинні використовувати будь-які види функції / класу / ..., які пов'язані з часовою позначкою або датою, і ви повинні використовувати григоріанський каландр .

Оцінка

Якщо ваш код не зберігає обмеження, то score = -∞.

За замовчуванням bonus- 1.

  • Якщо ваш код працює незалежно від порядку введення (наприклад, 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голосів.)


Чи вважається 20030229 недійсною датою третім бонусом?
rintaun

@rintaun Так. Це недійсно, на відміну від 20040229. : P
JiminP

1
Чи реально повертає WolframAlpha правильний результат? Я отримую від нього суперечливі відповіді та timeanddate.com . Моя програма, яка, на мою думку, працює коректно (принаймні, у тому випадку: P), погоджується з останньою.
rintaun

@rintaun Я думаю, що Вольфрам | Альфа помилявся, оскільки 365*4 + 2 + 2= 1464. Дякую за інформацію!
JiminP

1
Слід зазначити, що навіть із timeanddate.com є деякі проблеми: він приймає лише роки 1-3999, і він автоматично налаштовує на 11-денну невідповідність між юліанським та григоріанським календарями на дати до 3 вересня 1752 року (отже, 17520903 до 17520914 недійсні дати). Майте це на увазі під час тестування результатів.
rintaun

Відповіді:


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 - високосний рік.

Зі змінами, які ви внесли, схоже, що ваша програма тепер приймає недійсні місяці та дні, як 20111300-20119999повернення 2717.
migimaru

@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;

Безумовно

<?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;

Примітка

Обчислює кількість днів, повторюючи кожну дійсну дату між двома наданими. На більшій дальності це досить повільно. Я впевнений, що це не найкращий спосіб вирішити це, але я став нетерплячим, і саме з цим я і закінчився. :)

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


2

Рубін 1,9, оцінка: -175 -186 -191 -199

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

Код приймає введення через stdin.

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.

Дякуємо за те, що перевірили помилку на моєму рішенні та за те, що я змусив мене продовжувати вдосконалюватися. Мені особливо подобається ваш розрахунок за лютневу довжину тут.
DCharness

@DCharness Дякую, що підштовхнув і мене. Я зрозумів, що в моїх оригінальних поданнях було багато можливостей для вдосконалення.
migimaru

1

Пітон, Оцінка: -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'

У мене немає "невольфної" версії, як це я написав. Я не перевірив її належним чином, якщо ви знайшли помилку - будь ласка, прокоментуйте.

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


Вибачте, але коли я тестував Python 2.7 Shell, недійсні введення типу "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-виразу, це не лінь.
Lie Ryan

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;

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