Calculez la différence entre deux jours.

11

Un autre problème de manipulation de date: P

Tâche

Écrivez un programme ou une fonction qui calcule la différence entre deux dates données par un utilisateur.

Entrée sortie

Semblable à la précédente , les entrées sont deux YYYYMMDDs, séparées par un espace , une virgule ,ou un signe moins -.

Exemple de valeurs d'entrée:

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

La sortie est un entier, qui est la différence entre deux dates, en jours.

Par exemple, les 20110101-20100101rendements des intrants 365et les 33320229 17000101rendements 596124.

Vous pouvez tester les résultats ici ici . (Voir les commentaires de rintaun ci-dessous.) Si deux dates sont identiques, le programme devrait revenir 0, si la date est valide (voir Score ).

Restriction

Bien sûr, vous ne devez utiliser aucun type de fonction / classe / ... lié à l'horodatage ou à la date, et vous devez utiliser le calendrier grégorien .

But

Si votre code ne respecte pas la restriction, alors score = -∞.

La valeur par défaut bonusest 1.

  • Si votre code fonctionne quel que soit l'ordre des entrées (par exemple, 20100101,20110101retourne 365ou -365) bonus+=1,.
  • Si votre code peut gérer l' année 0 , bonus+=0.5.
  • Si votre code reconnaît un mois non valide (entre 1 ~ 12) / date (entre 1 ~ 31), comme 20109901ou 34720132, et s'imprime E(& termine le programme ou renvoie quelque chose comme 0) bonus+=1,.
  • Indépendamment de la règle ci-dessus, si votre code reconnaît des dates non valides, comme 20100230, 20100229ou 20111131, et s'imprime E(& termine le programme ou renvoie quelque chose comme 0) bonus+=1,.
  • Indépendamment des deux règles ci-dessus, si votre code reconnaît une chaîne d'entrée non valide, comme 20100101|20100202ou 2010010120100202, et s'imprime E(& termine le programme ou renvoie quelque chose comme 0) bonus+=1,.

score = floor(-4.2*code.length/bonus). Le code avec le score le plus élevé gagne. Si deux codes supérieurs ont le même score, alors les codes avec les gains de bonus les plus élevés. Si deux codes supérieurs ont à la fois le même score et le même bonus, alors les codes avec les votes les plus élevés l'emportent.

(En raison: lorsqu'il y a plus de 5 codes qui ont plus de +1votes (ou égaux) .)

JiminP
la source
20030229 est-il considéré comme une date non valide par le troisième bonus?
rintaun
@rintaun Oui. C'est invalide, contrairement à 20040229. : P
JiminP
1
WolframAlpha renvoie-t-il réellement le bon résultat? Je reçois des réponses contradictoires de ce et timeanddate.com . Mon programme, qui je crois fonctionne correctement (au moins dans ce cas: P), est d'accord avec ce dernier.
rintaun
@rintaun Je pense que Wolfram | Alpha avait tort, car 365*4 + 2 + 2= 1464. Merci pour l'information!
JiminP
1
Il convient de noter que même avec timeanddate.com, il existe certains problèmes: il n'accepte que les années 1-3999, et il s'ajuste automatiquement pour l'écart de 11 jours entre les calendriers julien et grégorien pour les dates antérieures au 3 septembre 1752. (les dates 17520903 à 17520914 ne sont donc pas valides). Gardez cela à l'esprit lorsque vous testez les résultats.
rintaun

Réponses:

3

Perl 5.14, score = -162

-163 -181 -196 -214 -167 -213 -234
  • code.length = 211: 208 caractères source + 3 pour exécuter perl avec l' -poption
  • bonus = 5.5: par défaut, commande, année 0, mois / jour non valide, date invalide, entrée totalement invalide

Code

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

Calcule un numéro de jour julien modifié pour chaque date (en ignorant les ajustements liés à l'époque pour enregistrer la longueur du code) et soustrait les deux. (réf. "Julian Day" sur Wikipedia ).

  • nécessite perl 5.14+ pour l' /roption sur les substitutions
  • calcul de 30+($m&1^$m>7)la durée du mois pour obtenir le bonus de date invalide: la partie donne la durée de n'importe quel mois sauf février; le reste s'ajuste pour février dans une année ordinaire ou bissextile

Hypothèses

  • «utiliser le calendrier grégorien» signifie le calendrier grégorien proleptique pour les dates antérieures à la transition julienne à grégorienne que nous utilisons. Autrement dit, ne soustrayez pas 11 jours pour les intervalles qui traversent, par exemple, la transition britannique du 3 septembre 1752 au 14 septembre 1752.
  • "gérer l'année 0" signifie, par exemple, 00000101-00010101devrait donner 366, car 0 est un multiple entier de 400, et donc l'année 0 est une année bissextile.
DCharness
la source
Avec les modifications que vous avez apportées, il semble que votre programme accepte désormais les mois et les jours invalides, comme les 20111300-20119999retours 2717.
migimaru
@migimaru: J'ai en effet optimisé l'exactitude. Zut. Je vais éditer et peut-être y revenir.
DCharness
2

PHP, score: -539,1

  • 706 caractères
  • Tous les articles bonus; bonus = 5,5

Code

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

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

Remarque

Calcule le nombre de jours en parcourant chaque date valide entre les deux fournies. C'est assez lent sur de plus grandes plages. Je suis sûr que ce n'est pas la meilleure façon de résoudre ce problème, mais je me suis impatienté, et c'est ce que j'ai fini avec. :)

De plus, je sais que le code "non golfé" n'est toujours pas très lisible, mais le réécrire complètement demanderait trop d'efforts.

rintaun
la source
2

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

  • Longueur de code: 229 243 250 260 caractères
  • Bonus: 5.5 (par défaut, commande, année 0, mois / jour invalide, date invalide, entrée invalide)

Le code accepte l'entrée via 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

Remarques:

  • h renvoie le nombre d'années bissextiles jusqu'à cette année (y compris l'année 0 pour le bonus).
  • L'expression régulière gère le bonus d'entrée non valide.
  • La (!x[e]||e*f<1||f>x[e])condition gère les bonus mois / jour / date invalides.
  • Le résultat est affiché comme la première date moins la deuxième date, donc si la deuxième date est postérieure, elle sera affichée sous forme de nombre négatif.
  • Ne s'ajuste pas au changement entre les calendriers julien et grégorien, donc 33320229 17000101résulte en 596134.
migimaru
la source
Merci d'avoir vérifié ma solution et de m'avoir poussé à continuer de m'améliorer. J'aime particulièrement votre calcul de la longueur de février ici.
DCharness
@DCharness Merci aussi de m'avoir poussé. J'ai réalisé qu'il y avait beaucoup de place pour l'amélioration dans ma soumission originale.
migimaru
1

Python, Score: -478

  • caractères: 455
  • bonus: dates inversées, jour / mois invalide, date invalide

Solution:

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'

Je n'ai pas de version "non golfée" car c'est ainsi que je l'ai écrite. Je ne l'ai pas testé correctement, donc si vous trouvez un bug - veuillez commenter.

edit: j'espère avoir corrigé un bug signalé dans les commentaires et ajouté le déballage sous la forme de [a, b], [c, d] = [[1,2], [3,4]

rplnt
la source
Désolé, mais lorsque j'ai testé avec Python 2.7 Shell, les entrées non valides comme «20000001,20010101» ne s'impriment pas E. ( Pour votre information, 0>-1>12, 0>6>12, 0>13>12retours False.)
JiminP
Merci. Je suis assez nouveau sur python. En écrivant ce script, j'ai appris que python fait cette x<y<zcomparaison ou qu'il y en a un x if y else z. J'ai essayé de le réparer.
rplnt
@rpInt: pour le golf, il y a aussi le [x,z][y]qui est plus court que x if y else z, bien que cela ne fonctionne pas toujours car contrairement à l'expression si ce n'est pas paresseux.
Lie Ryan
1

PHP, score: -516

caractères: 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;
Alfwed
la source
Le code PHP a besoin de <?au début pour s'exécuter, sinon il affiche simplement le code.
Gareth