1

The following code returns a string of the current time to the second:

return("{:%Y%m%dT%H%M%S%}".format(datetime.datetime.utcnow()))

How do I return a string of the current time to the hundredth (0.01) of a second?

I tried the following but I couldn't find a way to display only 2 decimal places of the fractional second.

return("{:%Y%m%dT%H%M%S%f}".format(datetime.datetime.utcnow()))
DenaliHardtail
  • 27,362
  • 56
  • 154
  • 233
  • 4
    Note that `return datetime.datetime.utcnow().strftime("%Y%m%dT%H%M%S%f")` is a neater way to write the same thing. – jonrsharpe Feb 20 '15 at 16:50

2 Answers2

4

You're almost there. Simply add [:-4] to the end of what you're currently trying to remove digits so that microseconds are no longer visible, only hundredths of a second. As mentioned in the comments by jonrsharpe, I would rather reformat like this though:

return datetime.datetime.utcnow().strftime("%Y%m%dT%H%M%S%f")[:-4]

All this is doing is slicing (see formatting here) your entire return from strftime to get your desired precision. Were you to want say milliseconds, you would replace [:-4] with [:-3] for instance.

EDIT: It was brought up in the comments you may want correct rounding of the hundredth place. If that's the case, I am sure there is an easier way to do this but you could implement something like this. This will produce an incorrect value for an extreme value 995xxx at the end of your DateTime as mentioned in the comments, but if an extreme value is really of concern to you you should probably use an alternative method like Jarek's solution.

now = datetime.datetime.utcnow().strftime("%Y%m%dT%H%M%S%f")
return  str( now[:-9] + str(round(int(now[-9:-3]), -1))[:-1])
Community
  • 1
  • 1
HavelTheGreat
  • 3,299
  • 2
  • 15
  • 34
  • If you slice the format string, rather than the return from `strftime`, you will get very different results! – jonrsharpe Feb 20 '15 at 16:57
  • 1
    Technically, this doesn't round the hundredths digit properly. E.g. 0.945361 s will be displayed as 0.94 rather than 0.95. Rounding wasn't mentioned by OP but maybe worth noting. – Jarek Piórkowski Feb 20 '15 at 18:36
  • @Elizion I get a trailing dot on your example, using [:-3] rather than [:-1] in the final slice seems to fix that. I've added my own take though I'm afraid I devolved into code golf... – Jarek Piórkowski Feb 20 '15 at 20:11
  • @JarekPiórkowski Are you sure you used the most recent edit? It produces no dot for me. – HavelTheGreat Feb 20 '15 at 20:15
  • @Elizion I just realized this for my initial code as well, but you'll run into problems with rounding on extreme values. E.g. for (2015, 2, 28, 23, 59, 59, 999999) the output is 20150228T23596000. Can't really get around it without going to a proper timedelta-based solution but that doesn't fit in two lines... – Jarek Piórkowski Feb 20 '15 at 20:54
  • 1
    @JarekPiórkowski Ah correct, for 995xxx my solution wouldn't work. Good catch. I doubt that's really of concern to the OP but +1 to your answer for being very particular. – HavelTheGreat Feb 20 '15 at 20:56
3

Here's a (AFAICT) correct solution for rounding if someone is particular about that. The problem with string-based solutions is rounding ends up being a huge pain because what if your date is (2015, 02, 28, 23, 59, 59, 999900)?

def round_microseconds(date, digits_to_show):
    fraction = date.microsecond / 1000000.0
    rounded = round(fraction, digits_to_show)

    if rounded < 1:
        # format the number to have the required amount of decimal digits,
        # this is important in case the rounded number is 0
        hundredths = '{:.{prec}f}'.format(rounded, prec=digits_to_show)
        hundredths = hundredths[2:]  # trim off "0."
    else:
        # round up by adding a second to the datetime
        date = date + datetime.timedelta(seconds=1)
        hundredths = '0' * digits_to_show

    return date.strftime("%Y%m%dT%H%M%S") + hundredths

Test results:

print round_microseconds(datetime.datetime(2015, 02, 28, 23, 59, 59, 999900), 3)
print round_microseconds(datetime.datetime(2015, 02, 28, 23, 59, 59, 999999), 6)
print round_microseconds(datetime.datetime(2015, 02, 28, 23, 59, 59, 5600), 2)
print round_microseconds(datetime.datetime(2015, 02, 28, 23, 59, 59, 5600), 3)
print round_microseconds(datetime.datetime(2015, 02, 28, 23, 59, 59, 5600), 4)
print round_microseconds(datetime.datetime(2015, 02, 28, 23, 59, 59, 5), 3)
print round_microseconds(datetime.datetime(2015, 02, 28, 23, 59, 59, 5), 5)
print round_microseconds(datetime.datetime(2015, 02, 28, 23, 59, 59, 5), 6)

20150301T000000000
20150228T235959999999
20150228T23595901
20150228T235959006
20150228T2359590056
20150228T235959000
20150228T23595900001
20150228T235959000005

Older, simpler solution but with a bug: it will fail when microseconds is 995000 or higher, so 0.5% of the time:

  now = datetime.datetime.utcnow()
  hundredths = str(round(now.microsecond, -4))[:2]
  return now.strftime("%Y%m%dT%H%M%S") + hundredths

How it works:

  1. say now is datetime.datetime(2015, 2, 20, 19, 32, 48, 875912)
  2. now.microsecond is 875912
  3. round(now.microsecond, -4) is 880000.0. Use -4 because microsecond is in millionths so it has 6 digits, to get result to 2 digits you need to round off last 4 digits.
  4. str(round(now.microsecond, -4))[:2] to get only the first two digits of that number-as-a-string, so '88'. Note that 2 is 6-4, so the initial number of digits minus the digits you've rounded off.
  5. Then concatenate it with standard formatting of now.strftime("%Y%m%dT%H%M%S")

This can be made a bit more general like so:

  digits_to_show = 3
  now = datetime.datetime.utcnow()
  subsecond = str(round(now.microsecond, digits_to_show - 6))[:digits_to_show]
  return now.strftime("%Y%m%dT%H%M%S") + subsecond