14

I'm trying to serialize datetime in an API, but I don't want milliseconds. What I want is here: https://en.wikipedia.org/wiki/ISO_8601 - "2015-09-14T17:51:31+00:00"

tz = pytz.timezone('Asia/Taipei') dt = datetime.datetime.now() loc_dt = tz.localize(dt) 

Try A:

loc_dt.isoformat() >> '2015-09-17T10:46:15.767000+08:00' 

Try B:

loc_dt.strftime("%Y-%m-%dT%H:%M:%S%z") >> '2015-09-17T10:46:15+0800' 

The latter one is almost perfect except it's missing the colon in the timezone part. How can I solve this without string manipulation (deleting milliseconds or adding colon)?

5
  • I'd prefer a format string solution, because that's what I can easily plop in to the serializer. I don't see anything helpful on the strftime format string page though. Commented Sep 17, 2015 at 18:05
  • ISO8601 allows second fragments. Try removing the fragment before formatting the string Commented Sep 18, 2015 at 7:39
  • unrelated: you code may fail during DST transitions, use loc_dt = datetime.now(tz) instead. Commented Sep 18, 2015 at 19:05
  • @J.F.Sebastian What happens, how does it fail? Sometimes what I localize is not datetime.now(), but an existing datetime object. Commented Sep 18, 2015 at 22:06
  • @CsabaToth: it may return a wrong time for ambiguous local times. If you want to disambiguate existing local times then you need additional info e.g., Parsing of Ordered Timestamps in Local Time (to UTC) While Observing Daylight Saving Time Commented Sep 18, 2015 at 22:12

2 Answers 2

18

You can replace the microseconds with 0 and use isoformat:

import pytz from datetime import datetime tz = pytz.timezone('Asia/Taipei') dt = datetime.now() loc_dt = tz.localize(dt).replace(microsecond=0) print loc_dt.isoformat() 2015-09-17T19:12:33+08:00 

If you want to keep loc_dt as is do the replacing when you output:

loc_dt = tz.localize(dt) print loc_dt.replace(microsecond=0).isoformat() 

As commented you would be better passing the tz to datetime.now:

 dt = datetime.now(tz) 

The reasons are discussed in pep-0495, you might also want to add an assert to catch any bugs when doing the replace:

 ssert loc_dt.resolution >= timedelta(microsecond=0) 
Sign up to request clarification or add additional context in comments.

17 Comments

Wow, that's weird from a parser point of view. If the microsecond is 0 for whatever reason, it is omitted completely form the output? If the minute or second is zero, it's not omitted.
yes, if you replace the milliseconds with 0 then your datetime would becme datetime.datetime(2015, 9, 17, 12, 33, 46, tzinfo=<DstTzInfo 'Asia/Taipei' CST+8:00:00 STD>) so calling isoformat just works with what is there
tz.localize(datetime.now()) may fail; use datetime.now(tz) instead.
add assert datetime.resolution == timedelta(microseconds=1) otherwise your code may fail.
|
2

Since python 3.6, datetime.isoformat accepts a timespec keyword to pick a precision. This argument gives the smallest time unit you want to be included in the output:

>>> loc_dt.isoformat() '2022-10-21T19:59:59.991999+08:00' >>> loc_dt.isoformat(timespec='seconds') '2022-10-21T19:59:59+08:00' >>> loc_dt.isoformat(timespec='milliseconds') '2022-10-21T19:59:59.991+08:00' 

Notice how the time is truncated and not rounded.

You can also use timespec to remove seconds/minutes:

>>> loc_dt.isoformat(timespec='minutes') '2022-10-21T19:59+08:00' >>> loc_dt.isoformat(timespec='hours') '2022-10-21T19+08:00' 

This all assume you ran the following setup script beforehand:

from datetime import datetime import pytz tz = pytz.timezone('Asia/Taipei') dt = datetime.now() loc_dt = tz.localize(dt) 

Also note that this works without timezone:

>>> from datetime import datetime >>> now = datetime.now() >>> now.isoformat(timespec='minutes') >>> '2022-10-21T19:59' 

1 Comment

This is useful for Python 3.x. The original question was for 2.7, but that's EOL for a while now.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.