Rollover Calendar

17

2

This is inspired by one of Downgoat's questions in Sandbox, where I suggested that he include April 31 as Pi day for people who use day/month format, only for him to inform me that there is no April 31!

Given a date string in month/day format that might be invalid, output the correct date using rollover. (First rollover the month, then rollover the day).

Examples:

"15/43" - This reads as the 43rd day of the 15th month. First, we roll over the month to the next year, so we end up with 3 (March). Now, since March only has 31 days, we rollover the extra days to April, so we output the actual date as "4/12" (April 12th).

"3/16" - This is a valid date (March 16th). Return it as is.

"12/64" - Ah, so many fond memories from December 64th... December has 31 days, January has 31 days, so what I really mean is "2/2" (February 2nd).

"19/99" - First, the 19 becomes a 7 (July). July has 31 days, August has 31 days, September has 30 days, so the output is "10/7" (October 7th).

"1/99999" - A year has 365 days. 99999 (mod 365) = 354. The 354 day of the year is "12/20".

"9999999/10" - Apparently, 9999999 (mod 12) = 3, so this is "3/10" (March 10th).

Criteria:

Input month is an integer > 0. Input day is an integer > 0. Year never needs to be specified, as such there are no leap years to deal with.

Update:

As I think it would overly simplify the challenge, calendar functions, such as those in the Java Calendar class, are banned. Date parsing/formatting functions are still allowed though.

geokavel

Posted 2015-12-17T16:38:58.137

Reputation: 6 352

1Related. – cat – 2015-12-17T16:40:04.623

1Another important test case would be one with enough days to roll over more than 8 years, such that answers which use built-ins have to pay attention to those built-ins using leap years. – Martin Ender – 2015-12-17T17:28:00.807

Oh, that answers that... I was just about to post a Mathematica answer which uses a built-in. :/ – Martin Ender – 2015-12-17T17:28:55.170

@Martin Sorry about that ;). – geokavel – 2015-12-17T17:29:00.330

For reference, the Mathematica solution was still 90 bytes long (although that was massively dominated by the string processing). If you want to add such a test case nevertheless, 1/99999 should yield 12/19 I think. – Martin Ender – 2015-12-17T17:32:23.350

@Martin Hm, I got 12/20. Which one of us is right? – geokavel – 2015-12-17T17:38:35.947

@geokavel You're right, there was a bug in my code (in fact, I had 12/20 earlier). – Martin Ender – 2015-12-17T17:42:13.227

Answers

11

LabVIEW, 32 LabVIEW Primitives

Eumel

Posted 2015-12-17T16:38:58.137

Reputation: 2 487

That was quick. – Addison Crump – 2015-12-17T16:53:37.113

Can you assure me there are no calendar functions used here? – geokavel – 2015-12-18T18:18:59.853

The good thing about LabVIEW is that its basically what it looks to be. First thing scans numbers from a string 2 modulos, the box is a switch case structure that gives out 28,30 or 31 and then it gets put together as a string again. – Eumel – 2015-12-19T08:28:25.717

4

C#, 269 223

string v(string n){var x=new[]{31,28,31,30,31,30,31,31,30,31,30,31};var s=n.Split('/');Func<string,int> p=int.Parse;var m=p(s[0]);var d=p(s[1]);m=m>=12?m%12:m;while(d>x[m]){d-=x[m];m=++m>=12?m%12:m;}return(m==0?1:m)+"/"+d;}

Edit: Fixed to work for cases like 24/1, 36/1, etc. and golfed a little. Thanks for the comments, there are several places I saved a bit!

Ungolfed:

string v(string n)
{
    var x = new [] { 31 ,28, 31, 30, 31, 30,31, 31, 30, 31, 30, 31 };

    var s = n.Split('/');
    Func<string,int> p = int.Parse;
    var m = p(s[0]);
    var d = p(s[1]);
    m = m >= 12 ? m % 12 : m;
    while (d > x[m])
    {
        d -= x[m];
        m = ++m >= 12 ? m % 12 : m;
    }
    return (m==0?1:m) + "/" + d;
}

DrewJordan

Posted 2015-12-17T16:38:58.137

Reputation: 141

First of all: Welcome to the community! I would guess you could save some Bytes by assigning the dictionary in a loop or using a switch function (no C# pro here though). Also directly printing ´m + "/" + d´ might help a little. And last (this might or might not work) using char* instead of string in the arguments. – Eumel – 2015-12-17T21:41:35.123

This fails on certain test cases, e.g. 24/1. – LegionMammal978 – 2015-12-17T23:58:19.760

@Eumel thanks! Directly printing uses Console.Write which is more than return, and in C# I would use char[] as a replacement for string, but it's the same amount of chars, and makes splitting on the / more problematic. But, your note on the Dictionary did lead me to a way better version! – DrewJordan – 2015-12-18T17:53:23.933

4

R, 208 182 bytes

m=c(31,28,31,30,31,30,31,31,30,31,30,31)
e=scan(sep="/");n=(e[1]/12-1)*12;if(!n%%12)n=12;if(n<0)n=e[1];j=e[2];while((j<-j-m[n])>0){n=n+1;if(n>12)n=1};j=m[n]+j;cat(n,j,sep="/")

Get the month by dividing by 12, then loop, removing the number of days of the current month until you get a negative number., inverse last step and print.

On multiple lines (need to use a file and source it):

m=c(31,28,31,30,31,30,31,31,30,31,30,31)
e=scan(sep="/")
n=(e[1]/12-1)*12
if(!n%%12)n=12
if(n<0)n=e[1]
j=e[2]
while((j<-j-m[n])>0){n=n+1;if(n>12)n=1}
j=m[n]+j;cat(n,j,sep="/")

Tensibai

Posted 2015-12-17T16:38:58.137

Reputation: 409

This is the message I get when trying to run your program in R: http://pastebin.com/dPh1n64a

– geokavel – 2015-12-18T17:10:05.063

Strange I'll recheck later – Tensibai – 2015-12-18T17:13:09.113

I also got this message. Your program seems to have problems with some months. Otherwise it's running. http://pastebin.com/g3BCUDi8

– geokavel – 2015-12-18T17:24:41.667

thanks for the feedback. I'm on road actually, I'll correct it – Tensibai – 2015-12-18T17:27:18.497

I understand. It seems like your program breaks with the "TRUE/FALSE" error message if the month is higher than 24. – geokavel – 2015-12-18T17:31:35.377

Got it, should be n<1 as first if condition – Tensibai – 2015-12-18T17:34:19.270

@geokavel Sorry for the late feedback, I corrected the multiple of 12 months to give valid results now. – Tensibai – 2015-12-30T08:27:24.887

@plannapus Yes, thanks. It default to double according to my doc, but should not be a problem. – Tensibai – 2015-12-30T08:36:01.253

1@Tensibai yes you're right, my bad (i should have remembered that the error message says that it expects "a real"). – plannapus – 2015-12-30T09:11:54.157

3

PHP >= 5.5, 181 bytes

list($m,$d)=explode("/",$argv[1]);$m%=12;$d%=365;$i=0;while($d>100)$d-=[31,28,31,30,31,30,31,31,30,31,30,31][$i++];$m+=$i;echo date_create_from_format("m/d","$m/$d")->format("n/j");

PHP almost supports rollover with date parsing and formatting instructions alone. For example:

echo date_create_from_format("m/d","12/64")->format("n/j"); // Output: 2/2

However, once either number gets bigger than 100, PHP rejects parsing it and returns an error (probably for some arbitrary reason). So, the theory with this answer is to get it back down to where PHP will parse it, then submit it to date_create_from_format().

Ungolfed:

list($month, $day) = explode("/", $argv[1]);
$month = $month % 12;
$day = $day % 365;
$i = 0;
$days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
while($day > 31) $day -= $days[$i++];
$month += $i;
echo DateTime::createFromFormat("m/d", $month . "/" . $day)->format("n/j");

Try it online

nickb

Posted 2015-12-17T16:38:58.137

Reputation: 351

1You need to give me a way to input the date string, or make it a function. – geokavel – 2015-12-17T21:55:35.583

GET parameters are not acceptable as an input method in PHP. You will need to either make this a function and pass the input as function parameters, or get input from $argv or STDIN. – Mego – 2015-12-18T08:35:01.837

@Mego Jeeze, give me some time to update my answer since the OP didn't state any requirements for input - it's simple enough to change $t to $argv[1] and now it reads from command line input. – nickb – 2015-12-18T15:13:47.953

The OP stating requirements for input is irrelevant; we have a list of default acceptable I/O methods (which I linked) so that challenge authors don't have to specify them on every challenge. – Mego – 2015-12-18T15:44:55.720

2

JavaScript (ES6), 106 bytes

s=>eval('q="030101001010";p=s.split`/`;for(d=i=p[1],m=p[0]-1;i--;d>n&&(m++,d-=n))n=31-q[m%=12];m+1+"/"+d')

Explanation

s=>
  eval(`              // use eval to enable for loop without needing to write {} or return
    q="030101001010"; // q = array of 31 - days in each month
    p=s.split\`/\`;   // p = array of [ month, day ]
    for(
      d=i=p[1],       // d = day
        m=p[0]-1;     // m = month - 1
      i--;            // loop for each day, this is more iterations than needed but extra
                      //     iterations do not affect the result and it's the shortest way
                      //     to guarantee all months have been subtracted from d, it also
                      //     ensures the loop runs at least once to get m % 12
      d>n&&(m++,d-=n) // if too many days, subtract the month's days and increment month
    )
      n=31-q[m%=12];  // n = number of days in month, get m % 12
    m+1+"/"+d         // return the result
  `)

Test

var solution = s=>eval('q="030101001010";p=s.split`/`;for(d=i=p[1],m=p[0]-1;i--;d>n&&(m++,d-=n))n=31-q[m%=12];m+1+"/"+d')
<input type="text" id="input" value="19/99" />
<button onclick="result.textContent=solution(input.value)">Go</button>
<pre id="result"></pre>

user81655

Posted 2015-12-17T16:38:58.137

Reputation: 10 181

1

Non-competing answer - Bash + coreutils, 55

date -d1-$[(${1%/*}-1)%12+1]-1+$[${1#*/}-1]day +%-m/%-d

"Date parsing/formatting functions are still allowed though" - I assume this means the date utility is allowed.

Input is read from command line, e.g.:

$ ./rollovercal.sh 15/43
4/12
$ 

This is non-competing because the date command expands to something like this which is adding a number of days:

date -d1-3-1+42day +%-m/%-d

Digital Trauma

Posted 2015-12-17T16:38:58.137

Reputation: 64 644

It's allowed as long as it isn't doing anything like date.add(875) to increment the date by 875 days. That's mostly what I was going after. – geokavel – 2015-12-17T18:06:03.623

@geokavel oh, I see. The fully expanded date command here is date -d1-3-1+42day +%-m/%-d, so its doing exactly that. I guess this is a non-competing answer then :( – Digital Trauma – 2015-12-17T18:08:05.503