17
\$\begingroup\$

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.

\$\endgroup\$
7
  • 1
    \$\begingroup\$ Related. \$\endgroup\$
    – cat
    Commented Dec 17, 2015 at 16:40
  • 1
    \$\begingroup\$ Another 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. \$\endgroup\$ Commented Dec 17, 2015 at 17:28
  • \$\begingroup\$ Oh, that answers that... I was just about to post a Mathematica answer which uses a built-in. :/ \$\endgroup\$ Commented Dec 17, 2015 at 17:28
  • \$\begingroup\$ @Martin Sorry about that ;). \$\endgroup\$
    – geokavel
    Commented Dec 17, 2015 at 17:29
  • \$\begingroup\$ 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. \$\endgroup\$ Commented Dec 17, 2015 at 17:32

5 Answers 5

11
\$\begingroup\$

LabVIEW, 32 LabVIEW Primitives

\$\endgroup\$
3
  • \$\begingroup\$ That was quick. \$\endgroup\$ Commented Dec 17, 2015 at 16:53
  • \$\begingroup\$ Can you assure me there are no calendar functions used here? \$\endgroup\$
    – geokavel
    Commented Dec 18, 2015 at 18:18
  • \$\begingroup\$ 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. \$\endgroup\$
    – Eumel
    Commented Dec 19, 2015 at 8:28
4
\$\begingroup\$

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;
}
\$\endgroup\$
3
  • \$\begingroup\$ 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. \$\endgroup\$
    – Eumel
    Commented Dec 17, 2015 at 21:41
  • \$\begingroup\$ This fails on certain test cases, e.g. 24/1. \$\endgroup\$ Commented Dec 17, 2015 at 23:58
  • \$\begingroup\$ @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! \$\endgroup\$
    – DrewJordan
    Commented Dec 18, 2015 at 17:53
4
\$\begingroup\$

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="/")
\$\endgroup\$
9
  • \$\begingroup\$ This is the message I get when trying to run your program in R: pastebin.com/dPh1n64a \$\endgroup\$
    – geokavel
    Commented Dec 18, 2015 at 17:10
  • \$\begingroup\$ Strange I'll recheck later \$\endgroup\$
    – Tensibai
    Commented Dec 18, 2015 at 17:13
  • \$\begingroup\$ I also got this message. Your program seems to have problems with some months. Otherwise it's running. pastebin.com/g3BCUDi8 \$\endgroup\$
    – geokavel
    Commented Dec 18, 2015 at 17:24
  • \$\begingroup\$ thanks for the feedback. I'm on road actually, I'll correct it \$\endgroup\$
    – Tensibai
    Commented Dec 18, 2015 at 17:27
  • 1
    \$\begingroup\$ @Tensibai yes you're right, my bad (i should have remembered that the error message says that it expects "a real"). \$\endgroup\$
    – plannapus
    Commented Dec 30, 2015 at 9:11
2
\$\begingroup\$

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>

\$\endgroup\$
2
\$\begingroup\$

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

\$\endgroup\$
4
  • 1
    \$\begingroup\$ You need to give me a way to input the date string, or make it a function. \$\endgroup\$
    – geokavel
    Commented Dec 17, 2015 at 21:55
  • \$\begingroup\$ 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. \$\endgroup\$
    – user45941
    Commented Dec 18, 2015 at 8:35
  • \$\begingroup\$ @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. \$\endgroup\$
    – nickb
    Commented Dec 18, 2015 at 15:13
  • \$\begingroup\$ 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. \$\endgroup\$
    – user45941
    Commented Dec 18, 2015 at 15:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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