Calcule la diferencia entre dos días.

11

Otro problema de manipulación de fecha: P

Tarea

Escriba un programa o una función que calcule la diferencia entre dos fechas dadas por un usuario.

De entrada y salida

Similar a la anterior , las entradas son dos YYYYMMDDs, separadas por un espacio , una coma ,o un signo menos -.

Ejemplo de valores de entrada:

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

La salida es un número entero, que es la diferencia entre dos fechas, en días.

Por ejemplo, 20110101-20100101rendimientos de entrada 365y 33320229 17000101rendimientos 596124.

Puedes probar los resultados aquí en aquí . (Véanse los comentarios de Rintaun a continuación.) Si dos fechas son iguales, el programa debería regresar 0, si la fecha es válida (ver Puntuación ).

Restricción

Por supuesto, no debe usar ningún tipo de función / clase / ... que esté relacionado con la marca de tiempo o la fecha, y debe usar el calendario gregoriano .

Puntuación

Si su código no mantiene la restricción, entonces score = -∞.

El valor predeterminado bonuses 1.

  • Si su código funciona independientemente del orden de las entradas (por ejemplo, 20100101,20110101devoluciones 365o -365) bonus+=1,.
  • Si su código puede manejar el año 0 , bonus+=0.5.
  • Si su código reconoce un mes no válido (entre 1 ~ 12) / fecha (entre 1 ~ 31), me gusta 20109901o 34720132, e imprime E(y finaliza el programa o devuelve algo como 0) bonus+=1,.
  • Independientemente de la regla anterior, si su código reconoce fechas no válidas, como 20100230, 20100229, o 20111131, e impresiones E(y termina el programa o vuelve algo así 0), bonus+=1.
  • Independientemente de las dos reglas anteriores, si su código reconoce una cadena de entrada no válida, como 20100101|20100202o 2010010120100202, e imprime E(y finaliza el programa o devuelve algo como 0) bonus+=1,.

score = floor(-4.2*code.length/bonus). El código con la puntuación más alta gana. Si dos códigos principales tienen el mismo puntaje, entonces los códigos con la mayor bonificación ganan. Si dos códigos principales tienen la misma puntuación y bonificación, entonces ganan los códigos con los votos más altos.

(Debido: cuando hay más de 5 códigos que tienen más de (o igual) +1votos).

JiminP
fuente
¿Se considera 20030229 una fecha no válida para el tercer bono?
Rintaun
@rintaun Sí. Es inválido, a diferencia 20040229. : P
JiminP
1
¿WolframAlpha realmente devuelve el resultado correcto? Estoy recibiendo respuestas de conflicto que y timeanddate.com . Mi programa, que creo que funciona correctamente (al menos en ese caso: P), está de acuerdo con este último.
rintaun
@rintaun Creo que Wolfram | Alpha estaba equivocado, ya que 365*4 + 2 + 2= 1464. ¡Gracias por la información!
JiminP
1
Cabe señalar que incluso con timeanddate.com, hay algunos problemas: solo acepta los años 1-3999, y se ajusta automáticamente para la discrepancia de 11 días entre los calendarios juliano y gregoriano para fechas anteriores al 3 de septiembre de 1752 (por lo tanto, 17520903 a 17520914 no son fechas válidas). Tenga esto en cuenta cuando pruebe los resultados.
Rintaun

Respuestas:

3

Perl 5.14, puntaje = -162

-163 -181 -196 -214 -167 -213 -234
  • code.length = 211: 208 caracteres de origen + 3 para ejecutar perl con la -popción
  • bono = 5.5: predeterminado, orden, año 0, mes / día nunca válido, fecha 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 un número de día juliano modificado para cada fecha (ignorando los ajustes relacionados con la época para guardar la longitud del código) y resta los dos. (Ref. "Julian Day" en Wikipedia ).

  • requiere perl 5.14+ para la /ropción en las sustituciones
  • Cálculo de la duración del mes para obtener el bono de fecha no válida: la 30+($m&1^$m>7)parte proporciona la duración de cualquier mes, excepto febrero; el resto se ajusta para febrero en un año ordinario o bisiesto

Supuestos

  • "usar calendario gregoriano" significa el calendario gregoriano proleptico para fechas antes de cualquier transición de juliano a gregoriano que estemos usando. Es decir, no reste 11 días para intervalos que se cruzan, por ejemplo, la transición británica del 3 de septiembre de 1752 al 14 de septiembre de 1752.
  • "manejar el año 0" significa, por ejemplo, que 00000101-00010101debe dar 366, ya que 0 es un múltiplo integral de 400, por lo que el año 0 es un año bisiesto.
DCharness
fuente
Con los cambios que realizó, parece que su programa ahora acepta meses y días no válidos, como 20111300-20119999devoluciones 2717.
migimaru
@migimaru: De hecho, he optimizado la corrección. Maldito. Lo editaré y quizás vuelva a hacerlo.
DCharness
2

PHP, Puntuación: -539.1

  • 706 caracteres
  • Todos los artículos de bonificación; bonificación = 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;

Sin golf

<?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 el número de días iterando a través de cada fecha válida entre los dos proporcionados. Es bastante lento en rangos más grandes. Estoy seguro de que esta no es la mejor manera de resolver esto, pero me impaciente, y esto es con lo que terminé. :)

Además, sé que el código "no protegido" todavía no es muy legible, pero reescribirlo completamente requeriría demasiado esfuerzo.

rintaun
fuente
2

Ruby 1.9, Puntuación: -175 -186 -191 -199

  • Longitud del código: 229243 250 260 caracteres
  • Bonificación: 5.5 (predeterminado, orden, año 0, mes / día no válido, fecha no válida, entrada no válida)

El código acepta entradas a través 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 devuelve el número de años bisiestos hasta ese año (incluido el año 0 para la bonificación).
  • La expresión regular maneja la bonificación de entrada no válida.
  • La (!x[e]||e*f<1||f>x[e])condición maneja los bonos de mes / día / fecha no válidos.
  • El resultado se muestra como la primera fecha menos la segunda fecha, por lo que si la segunda fecha es posterior, se mostrará como un número negativo.
  • No se ajusta al cambio entre los calendarios juliano y gregoriano, por lo que 33320229 17000101resulta en 596134.
migimaru
fuente
Gracias por verificar mi solución y por presionarme para seguir mejorando. Particularmente me gusta su cálculo de duración de febrero aquí.
DCharness
@DCharness Gracias por presionarme también. Me di cuenta de que había mucho margen de mejora en mi presentación original.
migimaru
1

Python, Puntuación: -478

  • caracteres: 455
  • bonificación: fechas inversas, día / mes inválido, fecha inválida

solución:

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'

No tengo una versión "sin golf" ya que así es como la escribí. No lo probé correctamente, así que si encuentras un error, por favor comenta.

editar: con suerte se corrigió un error señalado en un comentario y se agregó desempaquetado en forma de [a, b], [c, d] = [[1,2], [3,4]

rplnt
fuente
Lo siento, pero cuando probé con Python 2.7 Shell, las entradas inválidas como '20000001,20010101' no se imprimen E. (FYI, 0>-1>12, 0>6>12, 0>13>12retornos False.)
JiminP
Gracias. Soy bastante nuevo en Python. Al escribir este script, aprendí que Python hace esta x<y<zcomparación o hay una x if y else z. Intenté arreglarlo.
rplnt
@rpInt: para jugar al golf, también existe el [x,z][y]que es más corto que x if y else z, aunque no siempre funciona, ya que, a diferencia de la expresión if, no es vago.
Lie Ryan
1

PHP, puntaje: -516

caracteres: 685 676

bonificación: 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
fuente
El código PHP necesita <?al principio para ejecutarse, de lo contrario solo imprime el código.
Gareth