Calcola la differenza tra due giorni.


11

Un altro problema di manipolazione della data: P

Compito

Scrivi un programma o una funzione che calcola la differenza tra due date fornite da un utente.

Input Output

Simile al precedente , gli input sono due YYYYMMDDs, separati da uno spazio , una virgola ,o un segno meno -.

Esempio di valori di input:

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

L'output è un numero intero, che è la differenza tra due date, in giorni.

Ad esempio, i 20110101-20100101rendimenti di input 365e i 33320229 17000101rendimenti 596124.

Puoi testare i risultati qui qui . (Vedi i commenti di rintaun di seguito.) Se due date sono uguali, il programma dovrebbe tornare 0, se la data è valida (vedi Punteggio ).

Restrizione

Naturalmente, non è necessario utilizzare alcun tipo di funzione / classe / ... correlati a data / ora e data e si dovrebbe usare il calendario gregoriano .

Punto

Se il tuo codice non mantiene la restrizione, allora score = -∞.

L'impostazione predefinita bonusè 1.

  • Se il codice funziona indipendentemente dall'ordine degli input (ad esempio, 20100101,20110101resi 365o -365) bonus+=1,.
  • Se il codice in grado di gestire l'anno 0 , bonus+=0.5.
  • Se il tuo codice riconosce un mese non valido (tra 1 ~ 12) / data (tra 1 ~ 31), mi piace 20109901o 34720132, e stampa E(e termina il programma o restituisce qualcosa di simile 0) bonus+=1,.
  • Indipendentemente dalla regola di cui sopra, se il codice riconosce date non valide, come 20100230, 20100229, o 20111131, e le stampe E(e termina il programma o ritorna qualcosa di simile 0), bonus+=1.
  • Indipendentemente dalle due regole precedenti, se il tuo codice riconosce una stringa di input non valida, come 20100101|20100202o 2010010120100202, e stampa E(e termina il programma o restituisce qualcosa di simile 0) bonus+=1,.

score = floor(-4.2*code.length/bonus). Il codice con il punteggio più alto vince. Se due codici principali hanno lo stesso punteggio, vincono i codici con il bonus più alto. Se due codici principali hanno lo stesso punteggio e bonus, vincono i codici con il punteggio più alto.

(Scaduto: quando ci sono più di 5 codici con più di (o uguali) +1voti.)


20030229 è considerata una data non valida dal terzo bonus?
rintaun,

@rintaun Sì. Non è valido, a differenza 20040229. : P
JiminP,

1
WolframAlpha restituisce effettivamente il risultato corretto? Sto ricevendo risposte contrastanti da esso e timeanddate.com . Il mio programma, che credo funzioni correttamente (almeno in quell'istanza: P), è d'accordo con quest'ultimo.
rintaun,

@rintaun Penso che Wolfram | Alpha abbia sbagliato, poiché 365*4 + 2 + 2= 1464. Grazie per l'informazione!
JiminP,

1
Va notato che anche con timeanddate.com, ci sono alcuni problemi: accetta solo anni 1-3999 e si autoregola per la discrepanza di 11 giorni tra i calendari giuliano e gregoriano per le date precedenti al 3 settembre 1752 (quindi da 17520903 a 17520914 non sono date valide). Tienilo a mente durante il test dei risultati.
rintaun

Risposte:


3

Perl 5,14, punteggio = -162

-163 -181 -196 -214 -167 -213 -234
  • code.length = 211: 208 caratteri sorgente + 3 per l'esecuzione di perl con l' -popzione
  • bonus = 5,5: impostazione predefinita, ordine, anno 0, mese / giorno non validi, data non valida, input completamente non valido

Codice

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

Calcola un numero di giorno giuliano modificato per ciascuna data (ignorando le rettifiche relative all'epoca per salvare la lunghezza del codice) e sottrae le due. (rif. "Giorno giuliano" a Wikipedia ).

  • richiede perl 5.14+ per l' /ropzione sulle sostituzioni
  • calcolo 30+($m&1^$m>7)della durata di un mese per ottenere il bonus data non valido: la parte indica la durata di qualsiasi mese tranne febbraio; il resto si aggiusta per febbraio in un anno ordinario o bisestile

ipotesi

  • "usa il calendario gregoriano" indica il proletico calendario gregoriano per le date precedenti a qualsiasi transizione da Giuliano a Gregoriano che stiamo utilizzando. Cioè, non sottrarre 11 giorni per gli intervalli che attraversano, ad esempio, la transizione britannica del 3 settembre 1752-14 settembre 1752.
  • "handle year 0" significa, ad esempio, che 00000101-00010101dovrebbe dare 366, poiché 0 è un multiplo integrale di 400, e quindi l'anno 0 è un anno bisestile.

Con le modifiche apportate, sembra che il tuo programma ora accetti mesi e giorni non validi, come i 20111300-20119999resi 2717.
migimaru,

@migimaru: ho davvero ottimizzato la correttezza. Darn. Modificherò e forse ci tornerò.
DCharness,

2

PHP, Punteggio: -539,1

  • 706 caratteri
  • Tutti gli oggetti bonus; bonus = 5,5

Codice

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

Calcola il numero di giorni ripetendo ogni data valida tra i due forniti. È piuttosto lento su distanze maggiori. Sono sicuro che questo non è il modo migliore per risolverlo, ma sono diventato impaziente e questo è quello che ho finito. :)

Inoltre, so che il codice "ungolfed" non è ancora molto leggibile, ma riscriverlo completamente richiederebbe troppi sforzi.


2

Ruby 1.9, Score: -175 -186 -191 -199

  • Lunghezza del codice: 229243 250 260 caratteri
  • Bonus: 5.5 (impostazione predefinita, ordine, anno 0, mese / giorno non validi, data non valida, input non valido)

Il codice accetta input tramite 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

Appunti:

  • h restituisce il numero di anni bisestili fino a quell'anno (incluso l'anno 0 per il bonus).
  • Il regex gestisce il bonus di input non valido.
  • La (!x[e]||e*f<1||f>x[e])condizione gestisce i bonus mese / giorno / data non validi.
  • Il risultato viene visualizzato come prima data meno la seconda data, quindi se la seconda data è successiva, verrà visualizzato come un numero negativo.
  • Non si adatta alla modifica tra i calendari giuliano e gregoriano, quindi 33320229 17000101risulta 596134.

Grazie per il controllo degli errori della mia soluzione e per avermi spinto a continuare a migliorare. Mi piace particolarmente il tuo calcolo di febbraio qui.
DCharness,

@DCharness Grazie anche per avermi spinto. Mi sono reso conto che c'era molto spazio per migliorare la mia presentazione originale.
migimaru,

1

Python, Punteggio: -478

  • personaggi: 455
  • bonus: date inverse, giorno / mese non validi, data non valida

soluzione:

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'

Non ho una versione "ungolfed" poiché è così che l'ho scritta. Non l'ho testato correttamente, quindi se trovi un bug - per favore commenta.

modifica: si spera che sia stato corretto un bug segnalato in un commento e aggiunto il disimballaggio in forma di [a, b], [c, d] = [[1,2], [3,4]


Spiacenti, ma quando ho provato con Python 2.7 Shell, gli input non validi come '20000001,20010101' non vengono stampati E. (FYI, 0>-1>12, 0>6>12, 0>13>12ritorna False.)
JiminP

Grazie. Sono abbastanza nuovo per Python. Scrivendo questo script ho imparato che Python fa questo x<y<zconfronto o che esiste un x if y else z. Ho provato a risolverlo.
rplnt,

@rpInt: per il golf, c'è anche quello [x,z][y]che è più breve di x if y else z, anche se non sempre funziona poiché, a differenza dell'espressione if, non è pigro.
Lie Ryan,

1

PHP, punteggio: -516

caratteri: 685 676

bonus: 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;

<?All'inizio il codice PHP deve essere eseguito, altrimenti stampa semplicemente il codice.
Gareth,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.