4
\$\begingroup\$

I'm a code golf novice, but I'm eager to give it a go.

My challenge is: Write the shortest program in C which takes in a date D as input and outputs the date D + 1 day

Rules:

  • program must compile only with gcc progname.c -o progname So no compiler/makefile tricks (if there are any) - compiler warnings don't matter
  • The input and output format must be day/month/year zero padding of single digit values does not matter: 1/3/1998 is equal to 01/03/1998
  • It doesn't matter how you give the the program it's input date, it can be a program argument, scanf whatever. Output must be to stdout however
  • February has 29 days when the year is divisible by 4
  • No date validation necessary, i.e. assume the input is always a valid date.
  • any integer year should be supported, i.e. INT_MIN -> INT_MAX

Shortest code length wins, and there are extra points for extra bastardised C.

My solution uses 126 123* 121 characters

Good luck

*s="%i/%i/%i",m,d;main(y){scanf(s,&d,&m,&y);printf(s,d,m,(d=d%((m>7^m&1)+27+(m-2?3:y%4?1:2))+1)-1?y:(m=m%12+1)-1?y:y+1);}

*because of an idea Gareth's code gave me

\$\endgroup\$
  • 3
    \$\begingroup\$ February doesn't have 29 days when the year is divisible by 100 unless it is also divisible by 400. Or is your last rule there to simplify matters slightly? \$\endgroup\$ – Gareth Aug 9 '11 at 19:16
  • \$\begingroup\$ @Gareth I think so. \$\endgroup\$ – FUZxxl Aug 9 '11 at 19:20
  • \$\begingroup\$ @Gareth, yes it's a simplification. \$\endgroup\$ – Griffin Aug 9 '11 at 19:20
  • 3
    \$\begingroup\$ Code golf is usually open for all languages, except if the problem can only be solved in one language. Please lift the restriction on using C only. \$\endgroup\$ – FUZxxl Aug 9 '11 at 19:21
  • \$\begingroup\$ @FUZxxl "Usually"... Not this time :) \$\endgroup\$ – Griffin Aug 9 '11 at 19:26
3
\$\begingroup\$

Okay, it's been a while since I wrote any C but I'll give it a go:

m,y,*q="%d/%d/%d";
main(d){
scanf(q,&d,&m,&y);
++d>30+(m+(m>7))%2-(m^2?0:2-!(y%4))?(m+=d=1)>12?y+=m=1:0:0;
printf(q,d,m,y);}

120 characters.
With a bit of formatting(not that it makes it any more readable any more...):

m,y,*q="%d/%d/%d";
main(d)
{
    scanf(q,&d,&m,&y);
    ++d>30+(m+(m>7))%2-(m^2?0:2-!(y%4))?(m+=d=1)>12?y+=m=1:0:0;
    printf(q,d,m,y);
}

The compiler throws out warnings like nobody's business, but it works.

I'm not sure I can get it any smaller than this now without stealing a couple of tricks from Griffin. With those tricks I can get to 116 characters:

m,y,*q="%d/%d/%d";
main(d){
scanf(q,&d,&m,&y);
printf(q,d,m,++d>30+(m>7^m&1)-(m^2?0:2-!(y%4))?(m+=d=1)>12?y+=m=1:y:y);}
\$\endgroup\$
  • \$\begingroup\$ Looks good mate, I reckon you can replace those ifs for ternary operators, also there's some repetition there that you can take out. \$\endgroup\$ – Griffin Aug 11 '11 at 21:18
  • \$\begingroup\$ I'm not entirely sure how I'd use the ternary operator in this case. I've got no if else blocks. \$\endgroup\$ – Gareth Aug 11 '11 at 22:03
  • \$\begingroup\$ You can just put a 0 in the else part: if(x)b; becomes x?b:0; you save one character, might take some refactoring though \$\endgroup\$ – Griffin Aug 11 '11 at 22:50
  • \$\begingroup\$ @Griffin 125 characters. Do I get a prize? ;-) \$\endgroup\$ – Gareth Aug 11 '11 at 23:21
  • \$\begingroup\$ That's excellent! You get a upvote from me. Though you've given me an idea that got my code down to 123 characters. I'll post it up since I've stolen your idea of making the vars global so as to not define a type for them. \$\endgroup\$ – Griffin Aug 11 '11 at 23:41
2
\$\begingroup\$

C, 112 chars

Many months late, but I just saw it and decided to improve it.
The leading * isn't needed for 32bit platforms (and some 64bit platforms), so it can perhaps be removed.

*s="%d/%d/%d";
main(y,m,d){
    scanf(s,&d,&m,&y);
    d++>29+(m-2?m+m/8&1:!(y%4)-2)?d=m++<12||(y+=m=1):0;
    printf(s,d,m,y);
}

Lots of similarity to Gareth's solution, but honestly - I didn't look.
The only thing I did borrow was saving the format string in a variable (which is kind of obvious, but I missed it).

I do fix a problem with Gareth's solution - it relies on the evaluation order of printf parameters.

\$\endgroup\$
1
\$\begingroup\$

C - 293

#define w(x) while(*(i++)!='/');x=atoi(i);
main(){char x[99];scanf("%s",x);f(x);}
f(char* i){
int a=atoi(i),b,c,m[]={0,1,0,1,0,1,0,1,1,0,1,0,1};
w(b);w(c);
if(b==2)if((a==28&&c%4)||a==29){a=1;b=3;}else a++;else if(a-m[b]-30==0)if(b==12){a=b=1;c++;}else{a=1;b++;}else a++;
printf("%d/%d/%d",a,b,c);}

I've never really tried to golf C (and I am by no means an expert in C), but I feel this is pretty good. Basically it goes like this: get input from scanf, convert days, shift pointer, convert months, shift pointer, convert years (latter two have a #define to help), do logic with calendar logic. year%4 was a cool trick, and I have a month hash table indicating if the month is 30 or 31 days (kinda big to declare, but I think it was worth it). Finally, print. I'm sure there is a neat way to refactor using ternary (?:), but again, I'm no expert.

EDIT: fixed some typos.

\$\endgroup\$
  • \$\begingroup\$ Brilliant, someone is having a go. Good start but your program does not work correctly. If you input any date with month != 1, your code outputs day+1/1/year e.g. input: 1/12/1999 -> output: 2/1/1999, input: 24/2/1999 -> output: 25/1/1999. I suspect you maybe have a couple of typos - also remove the #include<stdio.h> you can printf and scanf without it, you'll just get compiler warnings :) \$\endgroup\$ – Griffin Aug 11 '11 at 18:13
  • \$\begingroup\$ Oh whoops, the error in my program is because I had the mindset DD/MM/YYYY and then switched to MM/DD/YYYY halfway through and mixed it all up. I'm interested in seeing your solution. \$\endgroup\$ – matt Aug 11 '11 at 19:52
  • \$\begingroup\$ Hmmm, still not working: 23/06/20101 -> 1/7/20101 \$\endgroup\$ – Griffin Aug 11 '11 at 20:12
  • \$\begingroup\$ Ok try some test cases now. \$\endgroup\$ – matt Aug 11 '11 at 20:19
  • \$\begingroup\$ Looks good to me :) try and chop some characters off now. Try and get a few of those ternary operators in, also consider if taking input via scanf using the same format you printf in is better or worse than parsing the input with a macro. \$\endgroup\$ – Griffin Aug 11 '11 at 20:46
0
\$\begingroup\$

C (in a way), 97 71

I will lose fifty rep for this but it was necessary.

The actual rule violation is that it doesn't quite do INT_MIN..INT_MAX; the range instead seems to be 0..1410065407 (on 64-bit Ubuntu 11... I'm saying nothing about portability :P).

main(){system("read a;date -d@$((`date -d$a +%s`+86400)) +%m/%d/%Y");}

(old version:)

d,e[9];main(){sprintf(e,"date -d@$((`date -d%s +%%s`+86400)) +%%m/%%d/%%Y",gets(&d))&system(e);}
\$\endgroup\$
  • \$\begingroup\$ I think this solution will not meet the spec if the given date is 28/02/1900. The correct (real world) answer would be 01/03/1900 but the spec calls for the answer to be 29/02/1900. (And I don't see why you should lose any rep for this answer) \$\endgroup\$ – Gareth Mar 4 '12 at 12:54
0
\$\begingroup\$

106 100 characters

Another entry that breaks the year rule by supporting only the years 1 to 9999, and also implements correct leap year handling instead of following requirements. The code simply uses POSIX libraries while abusing the type system:

t[9],b[9],*f="%d/%m/%Y";main(){gets(b);strptime(b,f,t);t[3]++;mktime(t);strftime(b,99,f,t);puts(b);}
\$\endgroup\$
  • \$\begingroup\$ You could save 7 characters by using gets instead of read and moving the gets call into strptime. \$\endgroup\$ – marinus Mar 4 '12 at 13:51
  • \$\begingroup\$ @marinus: Thanks, I had completely forgotten gets. I didn't move the call inside strptime, since treating the implicit int return value as a pointer is likely to crash on 64-bit systems. \$\endgroup\$ – han Mar 4 '12 at 17:00

Your Answer

By clicking "Post Your Answer", you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.