Calcule a diferença entre dois dias.

11

Outro problema de manipulação de datas: P

Tarefa

Escreva um programa ou uma função que calcule a diferença entre duas datas fornecidas por um usuário.

Entrada e Saída

Semelhante à anterior , as entradas são dois YYYYMMDDs, separadas por um espaço , vírgula ,ou sinal de menos -.

Exemplo de valores de entrada:

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

A saída é um número inteiro, que é a diferença entre duas datas, em dias.

Por exemplo, 20110101-20100101rendimentos de entrada 365e 33320229 17000101rendimentos 596124.

Você pode testar os resultados aqui aqui . (Veja os comentários de rintaun abaixo.) Se duas datas forem iguais, o programa retornará 0, se a data for válida (consulte Pontuação ).

Restrição

Obviamente, você não deve usar nenhum tipo de função / classe / ... que esteja relacionado ao carimbo de data / hora ou data e deve usar o calendário gregoriano .

Ponto

Se o seu código não mantiver a restrição, então score = -∞.

O padrão bonusé 1.

  • Se o seu código funcionar independentemente da ordem das entradas (por exemplo, 20100101,20110101retorna 365ou -365) bonus+=1,.
  • Se o seu código pode manipular ano 0 , bonus+=0.5.
  • Se o seu código reconhecer um mês inválido (entre 1 e 12) / data (entre 1 e 31), curtir 20109901ou 34720132, e imprimir E(e encerra o programa ou retorna algo como 0) bonus+=1,.
  • Embora a regra acima, se o seu código reconhece datas inválidas, como 20100230, 20100229, ou 20111131e impressões E(e termina o programa ou retorna algo parecido 0), bonus+=1.
  • Independentemente das duas regras acima, se o seu código reconhece uma string de entrada inválida, como 20100101|20100202ou 2010010120100202, e imprime E(& finaliza o programa ou retorna algo como 0) bonus+=1,.

score = floor(-4.2*code.length/bonus). Código com maior pontuação ganha. Se dois códigos principais tiverem a mesma pontuação, os códigos com bônus mais altos serão ganhos. Se dois códigos principais tiverem a mesma pontuação e bônus, os códigos com os votos mais altos vencerão.

(Vencimento: quando houver mais de 5 códigos com mais de (ou igual) +1voto).

JiminP
fonte
20030229 é considerada uma data inválida pelo terceiro bônus?
rintaun
@rintaun Sim. É inválido, ao contrário 20040229. : P
JiminP:
1
O WolframAlpha realmente retorna o resultado correto? Estou ficando conflitantes respostas de ele e timeanddate.com . Meu programa, que acredito estar funcionando corretamente (pelo menos nesse caso: P), concorda com o último.
rintaun
@rintaun Acho que Wolfram | Alpha estava errado, desde 365*4 + 2 + 2= 1464. Obrigado pela informação!
JiminP
1
Deve-se observar que, mesmo no timeanddate.com, existem alguns problemas: ele aceita apenas os anos 1-3 a 9999 e se ajusta automaticamente para a discrepância de 11 dias entre os calendários juliano e gregoriano para datas anteriores a 3 de setembro de 1752 (de 17520903 a 17520914 não são datas válidas). Lembre-se disso ao testar os resultados.
rintaun

Respostas:

3

Perl 5,14, pontuação = -162

-163 -181 -196 -214 -167 -213 -234
  • code.length = 211: 208 caracteres de origem + 3 para executar o perl com a -popção
  • bônus = 5,5: padrão, pedido, ano 0, mês / dia nunca válido, data inválida, entrada totalmente inválida

Código

$_=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

Calcula um número de dia juliano modificado para cada data (ignorando os ajustes relacionados à época para economizar o comprimento do código) e subtrai os dois. (ref. "Dia Juliano" na Wikipedia ).

  • requer perl 5.14+ para a /ropção nas substituições
  • cálculo da duração do mês para obter o bônus de data inválido: a 30+($m&1^$m>7)parte fornece a duração de qualquer mês, exceto fevereiro; o restante é ajustado para fevereiro em um ano ordinário ou bissexto

Premissas

  • "usar calendário gregoriano" significa o calendário gregoriano pró-séptico para datas anteriores à transição juliana para gregoriana que estamos usando. Ou seja, não subtraia 11 dias para intervalos que cruzam, por exemplo, a transição britânica de 3 de setembro de 1752 a 14 de setembro de 1752.
  • "manipular ano 0" significa, por exemplo, 00000101-00010101dar 366, pois 0 é um múltiplo integral de 400 e, portanto, o ano 0 é um ano bissexto.
DCharness
fonte
Com as alterações feitas, parece que seu programa agora aceita meses e dias inválidos, como 20111300-20119999devoluções 2717.
migimaru
@igimaru: Eu realmente otimizei a correção. Droga. Vou editar e talvez voltar a isso.
precisa saber é o seguinte
2

PHP, Pontuação: -539.1

  • 706 caracteres
  • Todos os itens de bônus; bônus = 5,5

Código

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

Nota

Calcula o número de dias repetindo cada data válida entre as duas fornecidas. É muito lento em intervalos maiores. Tenho certeza de que essa não é a melhor maneira de resolver isso, mas fiquei impaciente e foi com isso que acabei. :)

Além disso, eu sei que o código "ungolfed" ainda não é muito legível, mas reescrevê-lo completamente exigiria muito esforço.

rintaun
fonte
2

Ruby 1.9, Pontuação: -175 -186 -191 -199

  • Comprimento de código: 229 243 250 260 caracteres
  • Bônus: 5.5 (padrão, pedido, ano 0, mês / dia inválido, data inválida, entrada inválida)

O código aceita entrada por meio de 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

Notas:

  • h retorna o número de anos bissextos até esse ano (incluindo o ano 0 para o bônus).
  • A regex lida com o bônus de entrada inválido.
  • A (!x[e]||e*f<1||f>x[e])condição lida com os bônus de mês / dia / data inválidos.
  • O resultado é exibido como a primeira data menos a segunda data; portanto, se a segunda data for posterior, será exibida como um número negativo.
  • Não se ajusta à alteração entre os calendários juliano e gregoriano, 33320229 17000101resultando em 596134.
migimaru
fonte
Obrigado por verificar a minha solução e por me empurrar para continuar melhorando. Eu particularmente gosto do seu cálculo de duração de fevereiro aqui.
DCharness
@ DCharness Obrigado por me empurrar também. Percebi que havia muito espaço para melhorias na minha submissão original.
Migimaru
1

Python, Pontuação: -478

  • caracteres: 455
  • bônus: datas reversas, dia / mês inválido, data inválida

solução:

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'

Eu não tenho a versão "ungolfed", pois foi assim que a escrevi. Eu não testei corretamente, portanto, se você encontrar um bug - por favor, comente.

edit: espero que tenha corrigido um bug apontado nos comentários e adicionado a descompactação na forma de [a, b], [c, d] = [[1,2], [3,4]

rplnt
fonte
Desculpe, mas quando testei com o Python 2.7 Shell, entradas inválidas como '20000001,20010101' não são impressas E. (FYI, 0>-1>12, 0>6>12, 0>13>12retorna False.)
JiminP
Obrigado. Eu sou bastante novo em python. Ao escrever este script, aprendi que o python faz essa x<y<zcomparação ou existe um x if y else z. Tentei consertar isso.
rplnt
@rpInt: para o golfe, há também o [x,z][y]que é mais curto que x if y else z, embora nem sempre funcione, pois, diferentemente da expressão if, não é preguiçoso.
Lie Ryan
1

PHP, pontuação: -516

caracteres: 685 676

bônus: 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
fonte
O código PHP precisa que o <?início seja executado, caso contrário, apenas imprime o código.
Gareth