Decimal Time of Day Conversion

15

1

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

Mwr247

Posted 2015-07-09T07:22:27.803

Reputation: 3 494

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). – None – 2015-07-09T16:53:06.293

1Do we have to handle leap seconds? so 23:59:60 is 1 second from the end of a 86401 second day? – Sparr – 2015-07-10T03:52:30.147

1@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 ;) – Mwr247 – 2015-07-13T14:24:29.123

1@MichaelT It'd be a programmers dream world =P – Mwr247 – 2015-07-13T14:39:58.420

1@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. – None – 2015-07-13T14:52:22.267

For those looking for loopholes, note that MicroSoft Excel and LibreOffice Calc already use floating point as their internal representations of dates and times! They both have scripting languages built-in. – None – 2015-07-13T16:01:33.580

Answers

6

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

Optimizer

Posted 2015-07-09T07:22:27.803

Reputation: 25 836

Heh, we have similar ideas – aditsu quit because SE is EVIL – 2015-07-09T14:36:18.763

@aditsu Oh!. Didn't see yours before updating mine and then was in a hurry to commute. – Optimizer – 2015-07-09T15:01:05.943

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 – aditsu quit because SE is EVIL – 2015-07-09T15:07:17.150

3

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.

Kade

Posted 2015-07-09T07:22:27.803

Reputation: 7 463

2

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

Richard Donovan

Posted 2015-07-09T07:22:27.803

Reputation: 87

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?

– James – 2017-03-08T20:18:14.057

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) – Richard Donovan – 2017-03-08T20:47:29.823

Now down to an 85 byte one-liner! – Richard Donovan – 2017-03-09T06:35:04.137

2

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

nderscore

Posted 2015-07-09T07:22:27.803

Reputation: 4 912

24:00:00 produces 1 but the inverse is not true – rink.attendant.6 – 2015-07-09T16:26:56.407

@rink.attendant.6 fixed – nderscore – 2015-07-09T16:49:39.550

2

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"

Alex A.

Posted 2015-07-09T07:22:27.803

Reputation: 23 761

2

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.

Serdalis

Posted 2015-07-09T07:22:27.803

Reputation: 141

2

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.);
    }
}

Andrea Biondo

Posted 2015-07-09T07:22:27.803

Reputation: 1 452

Very clear use of scanf and %f – some user – 2015-07-11T16:19:32.967

D'oh! I meant "clever". – some user – 2015-07-12T04:28:22.560

1

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.

Titus

Posted 2015-07-09T07:22:27.803

Reputation: 13 814

1

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.

Denis Ibaev

Posted 2015-07-09T07:22:27.803

Reputation: 876

1

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}

SuperJedi224

Posted 2015-07-09T07:22:27.803

Reputation: 11 342

1

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

Downgoat

Posted 2015-07-09T07:22:27.803

Reputation: 27 116

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 =) – Mwr247 – 2015-07-09T19:53:04.603

@Mwr247 oh did not see that, I'll be fixing this then – Downgoat – 2015-07-09T19:53:46.503

1

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

some user

Posted 2015-07-09T07:22:27.803

Reputation: 635

0

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)

Wernisch

Posted 2015-07-09T07:22:27.803

Reputation: 2 534