15
\$\begingroup\$

Introduction

Time is confusing. Sixty seconds to a minute, sixty minutes to an hour, twenty-four hours to a day (and not to mention that pesky am/pm!).

There's no room for such silliness nowadays, so we've decided to adopt the only sensible alternative: decimal days! That is to say, each day is considered 1 whole unit, and anything shorter is written as a decimal fraction of that day. So, for example: "12:00:00" would be written as "0.5", and "01:23:45" might be written as "0.058159".

Because it will take time to get used to the new system, you are tasked with writing a program that can convert between them in both directions.

Challenge

Write a program in the language of your choice, that given a modern time in the ISO-8601 format of "hh:mm:ss", will return the equivalent decimal fraction unit. Likewise, given a decimal fraction, the program should return the time in the modern format initially specified.

You can make the following assumptions:

  • The modern time input and output can range from "00:00:00" to "24:00:00"
  • The decimal time input and output can range from "0" to "1", and should be able to accept/output up to at least 5 decimal places (such as "0.12345"). More precision is acceptable
  • The program should be able to know which conversion direction to perform based on input
  • You cannot use time related functions/libraries

The winner will be determined by the shortest code that accomplishes the criteria. They will be selected in at least 7 decimal day units, or if/when there have been enough submissions.

Examples

Here's a(n intentionally) poorly written piece of JavaScript code to be used as an example:

function decimalDay(hms) {
    var x, h, m, s;
    if (typeof hms === 'string' && hms.indexOf(':') > -1) {
        x = hms.split(':');
        return (x[0] * 3600 + x[1] * 60 + x[2] * 1) / 86400;
    }
    h = Math.floor(hms * 24) % 24;
    m = Math.floor(hms * 1440) % 60;
    s = Math.floor(hms * 86400) % 60;
    return (h > 9 ? '' : '0') + h + ':' + (m > 9 ? '' : '0') + m + ':' + (s > 9 ? '' : '0') + s;
}
decimalDay('02:57:46'); // 0.12344907407407407
decimalDay('23:42:12'); // 0.9876388888888888
decimalDay(0.5); // 12:00:00
decimalDay(0.05816); // 01:23:45
\$\endgroup\$
  • \$\begingroup\$ Hmm... 60 is almost 64. I wonder what time would be like if there were 64 seconds in a minute and 64 minutes in an hour (and 16 or 32 hours in a day). \$\endgroup\$ – user12166 Jul 9 '15 at 16:53
  • 1
    \$\begingroup\$ Do we have to handle leap seconds? so 23:59:60 is 1 second from the end of a 86401 second day? \$\endgroup\$ – Sparr Jul 10 '15 at 3:52
  • 1
    \$\begingroup\$ @Sparr No need to worry about leap seconds. This is the future, where we have decided that it's silly for a second to be considered an absolute value while also tying it to the relative speed of the rotation of the earth ;) \$\endgroup\$ – Mwr247 Jul 13 '15 at 14:24
  • 1
    \$\begingroup\$ @MichaelT It'd be a programmers dream world =P \$\endgroup\$ – Mwr247 Jul 13 '15 at 14:39
  • 1
    \$\begingroup\$ @Mwr247 yep. DNS TTL has (had?) a field that is n where n is 2^n seconds. So a value of '6' had a TTL of about 1 minute. A value of '12' had a TTL of about 1 hour. '15' was about 8 hours and so on. It allowed one byte to define the timeout and give you enough control for short or long times. \$\endgroup\$ – user12166 Jul 13 '15 at 14:52

13 Answers 13

6
\$\begingroup\$

CJam, 58 56 42 bytes

I am sure this is too long and can be golfed a lot. But here goes for starters:

86400q':/:d_,({60bd\/}{~*i60b{s2Ue[}%':*}?

Try it online here

\$\endgroup\$
  • \$\begingroup\$ Heh, we have similar ideas \$\endgroup\$ – aditsu Jul 9 '15 at 14:36
  • \$\begingroup\$ @aditsu Oh!. Didn't see yours before updating mine and then was in a hurry to commute. \$\endgroup\$ – Optimizer Jul 9 '15 at 15:01
  • \$\begingroup\$ You know what.. feel free to use my code: 86400q':/:d_,({60bd\/}{~*mo60bAfmd2/':*}?, I'm deleting my answer. The mo is so that 0.058159 converts to 01:23:45 \$\endgroup\$ – aditsu Jul 9 '15 at 15:07
3
\$\begingroup\$

Python 2, 159 150 141 + 2 = 143 Bytes

Straightforward solution, can probably be much shorter. Will work on it.

Added two bytes to account for input needing to be enclosed in "s. Also, Sp3000 pointed out an issue with eval() interpreting octals, and showed a way to shorten formatting, use map() and remove one print.

n=input();i=float;d=864e2
if':'in n:a,b,c=map(i,n.split(':'));o=a/24+b/1440+c/d
else:n=i(n);o=(':%02d'*3%(n*24,n*1440%60,n*d%60))[1:]
print o

Check it out on ideone here.

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

Javascript (ES6), 116 110 bytes

f=x=>x[0]?([h,m,s]=x.split(':'),+s+m*60+h*3600)/86400:[24,60,60].map(y=>('0'+~~(x*=y)%60).slice(-2)).join(':')


// for snippet demo:
i=prompt();
i=i==+i?+i:i; // convert decimal string to number type
alert(f(i))

Commented:

f=x=>
    x[0] ? // if x is a string (has a defined property at '0')
        ([h, m, s] = x.split(':'), // split into hours, minutes, seconds
        +s + m*60 + h*3600) // calculate number of seconds
        / 86400 // divide by seconds in a day
    : // else
        [24, 60, 60]. // array of hours, minutes, seconds
        map(y=> // map each with function
            ('0' + // prepend with string zero
                ~~(x *= y) // multiply x by y and floor it
                % 60 // get remainder
            ).slice(-2) // get last 2 digits
        ).join(':') // join resulting array with colons
\$\endgroup\$
  • \$\begingroup\$ 24:00:00 produces 1 but the inverse is not true \$\endgroup\$ – rink.attendant.6 Jul 9 '15 at 16:26
  • \$\begingroup\$ @rink.attendant.6 fixed \$\endgroup\$ – nderscore Jul 9 '15 at 16:49
2
\$\begingroup\$

Python 3: 143 Bytes

i,k,l,m=input(),60,86400,float
if'.'in i:i=m(i)*l;m=(3*':%02d'%(i/k/k,i/k%k,i%k))[1:]
else:a,b,c=map(m,i.split(':'));m=(a*k*k+b*k+c)/l
print(m)

Same byte count as the python 2 solution but it seems we took different approaches to the maths.

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

Julia, 152 143 142 bytes

Well, I updated my approach to be less "Julian," as they say, for the sake of golfing. For a better (though less concise) approach, see the revision history.

x->(t=[3600,60,1];d=86400;typeof(x)<:String?dot(int(split(x,":")),t)/d:(x*=d;o="";for i=t q,x=x÷i,x%i;o*=lpad(int(q),2,0)*":"end;o[1:end-1]))

This creates an unnamed function that accepts a string or a 64-bit floating point number and returns a 64-bit floating point number or string, respectively. To call it, give it a name, e.g. f=x->....

Ungolfed + explanation:

function f(x)
    # Construct a vector of the number of seconds in an hour,
    # minute, and second
    t = [3600, 60, 1]

    # Store the number of seconds in 24 hours
    d = 86400

    # Does the type of x inherit from the type String?
    if typeof(x) <: String
        # Compute the total number of observed seconds as the
        # dot product of the time split into a vector with the
        # number of seconds in an hour, minute, and second
        s = dot(int(split(x, ":")), t)

        # Get the proportion of the day by dividing this by
        # the number of seconds in 24 hours
        s / d
    else
        # Convert x to the number of observed seconds
        x *= d

        # Initialize an output string
        o = ""

        # Loop over the number of seconds in each time unit
        for i in t
            # Set q to be the quotient and x to be the remainder
            # from x divided by i
            q, x = divrem(x, i)

            # Append q to o, padded with zeroes as necessary
            o *= lpad(int(q), 2, 0) * ":"
        end

        # o has a trailing :, so return everything up to that
        o[1:end-1]
    end
end

Examples:

julia> f("23:42:12")
0.9876388888888888

julia> f(0.9876388888888888)
"23:42:12"

julia> f(f("23:42:12"))
"23:42:12"
\$\endgroup\$
2
\$\begingroup\$

C, 137 bytes

Full C program. Takes input on stdin and outputs on stdout.

main(c){float a,b;scanf("%f:%f:%d",&a,&b,&c)<3?c=a*86400,printf("%02d:%02d:%02d",c/3600,c/60%60,c%60):printf("%f",a/24+b/1440+c/86400.);}

Ungolfed and commented:

int main() {
    // b is float to save a . on 1440
    float a,b;
    // c is int to implicitly cast floats
    int c;

    // If the input is hh:mm:ss it gets splitted into a, b, c
    // Three arguments are filled, so ret = 3
    // If the input is a float, it gets stored in a
    // scanf stops at the first semicolon and only fills a, so ret = 1
    int ret = scanf("%f:%f:%d", &a, &b, &c);

    if(ret < 3) {
        // Got a float, convert to time
        // c = number of seconds from 00:00:00
        c = a * 86400;
        printf("%02d:%02d:%02d", c/3600, c/60 % 60, c%60);
    }
    else {
        // a = hh, b = mm, c = ss
        // In one day there are:
        // 24 hours
        // 1440 minutes
        // 86400 seconds
        printf("%f", a/24 + b/1440 + c/86400.);
    }
}
\$\endgroup\$
  • \$\begingroup\$ Very clear use of scanf and %f \$\endgroup\$ – some user Jul 11 '15 at 16:19
  • \$\begingroup\$ D'oh! I meant "clever". \$\endgroup\$ – some user Jul 12 '15 at 4:28
2
\$\begingroup\$

J, 85 bytes

Results:

T '12:00:00'
0.5

T 0.5
12 0 0

T '12:34:56'
0.524259

T 0.524259
12 34 56

T=:3 :'a=.86400 if.1=#y do.>.(24 60 60#:y*a)else.a%~+/3600 60 1*".y#~#:192 24 3 end.'

Total 85

\$\endgroup\$
  • \$\begingroup\$ Welcome to the site! I edited your post so that the code will be displayed as code. As for an online link, the best one I know of is TIO. I would give you a link, but I'm not experienced with J, so I don't know the right way to invoke it. Also, this seems to be 91 bytes when you include the first and last lines. Is this correct? \$\endgroup\$ – DJMcMayhem Mar 8 '17 at 20:18
  • \$\begingroup\$ Thanks for your help! The program [a=... to end.] Is 77. The title is 10. The terminator is 1, so that makes 88. With three line feeds that makes 91! I'll work on it :o) \$\endgroup\$ – Richard Donovan Mar 8 '17 at 20:47
  • \$\begingroup\$ Now down to an 85 byte one-liner! \$\endgroup\$ – Richard Donovan Mar 9 '17 at 6:35
1
\$\begingroup\$

Javascript, 194 192 190 188 bytes

function(z){if(isNaN(z)){x=z.split(':');return x[0]/24+x[1]/1440+x[2]/86400}h=(z*24)|0;h%=24;m=(z*1440)|0;m%=60;s=(z*86400)|0;s%=60;return""+(h>9?'':0)+h+':'+(m>9?'':0)+m+':'+(s>9?'':0)+s}
\$\endgroup\$
1
\$\begingroup\$

JavaScript ES6, 98 130 bytes

s=>s==+s?'246060'.replace(/../g,l=>':'+('0'+~~(s*=+l)%60).slice(-2)).slice(1):s.split`:`.reduce((a,b)=>+b+(+a)*60)*1/864e2;f(0.5);
\$\endgroup\$
  • \$\begingroup\$ Unfortunately, time related functions (such as "Date" and "toTimeString") are not allowed in this challenge. Otherwise, it's a much more concise way of doing it =) \$\endgroup\$ – Mwr247 Jul 9 '15 at 19:53
  • \$\begingroup\$ @Mwr247 oh did not see that, I'll be fixing this then \$\endgroup\$ – Downgoat Jul 9 '15 at 19:53
1
\$\begingroup\$

C, 156 152 bytes

I thought it is going to be easy for C. But still ended up quite large. :(

n,m=60;d(char*s){strchr(s,58)?printf("%f",(float)(atoi(s)*m*m+atoi(s+3)*m+atoi(s+6))/m/m/24):printf("%02d:%02d:%02d",(n=atof(s)*m*m*24)/m/m,n/m%m,n%m);}

Test Program:

#include <stdio.h>
#include <stdlib.h>

int n,m=60;
d(char*s)
{
    strchr(s,':') ? 
        printf("%f",(float)(atoi(s)*m*m+atoi(s+3)*m+atoi(s+6))/m/m/24):
        printf("%02d:%02d:%02d",(n=atof(s)*m*m*24)/m/m,n/m%m,n%m);
}

int main()
{
    d("01:23:45");
    printf("\n");
    d("02:57:46");
    printf("\n");
    d("23:42:12");
    printf("\n");
    d("12:00:00");
    printf("\n");
    d("0.5");
    printf("\n");
    d("0.05816");
    printf("\n");
    d("0");
    printf("\n");
    d("1");
    printf("\n");
    return 0;
}

Output:

0.058160
0.123449
0.987639
0.500000
12:00:00
01:23:45
00:00:00
24:00:00
\$\endgroup\$
1
\$\begingroup\$

PHP, 70 69 bytes

<?=strpos($t=$argv[1],58)?strtotime($t)/86400:date("H:i:s",$t*86400);

takes input from command line argument, prints to STDOUT:

If input contains a colon, convert to unix time and divide by (seconds per day),
else multply numeric value with (seconds per day) and format unix time to hh:mm:ss.

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

Perl, 109 108 101 + 6 (-plaF: flag) = 107 bytes

$_=$#F?($F[0]*60+$F[1]+$F[2]/60)/1440:sprintf"%02d:%02d:%02d",$h=$_*24,$m=($h-int$h)*60,($m-int$m)*60

Using:

perl -plaF: -e '$_=$#F?($F[0]*60+$F[1]+$F[2]/60)/1440:sprintf"%02d:%02d:%02d",$h=$_*24,$m=($h-int$h)*60,($m-int$m)*60' <<< 01:23:45

Try it on Ideone.

\$\endgroup\$
0
\$\begingroup\$

Excel, 178 bytes

=IF(LEFT(A1,2)="0.",TEXT(FLOOR(A1*24,1),"00")&":"&TEXT(MOD(FLOOR(A1*1440,1),60),"00")&":"&TEXT(MOD(FLOOR(A1*86400,1),60),"00"),((LEFT(A1,2)*60+MID(A1,4,2))*60+RIGHT(A1,2))/86400)
\$\endgroup\$

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.