482

I am trying to subtract one date value from the value of datetime.datetime.today() to calculate how long ago something was. But it complains:

TypeError: can't subtract offset-naive and offset-aware datetimes

The return value from datetime.datetime.today() doesn't seem to be "timezone aware", while my other date value is. How do I get a return value from datetime.datetime.today() that is timezone aware?

The ideal solution would be for it to automatically know the timezone.

Right now, it's giving me the time in local time, which happens to be PST, i.e. UTC - 8 hours. Worst case, is there a way I can manually enter a timezone value into the datetime object returned by datetime.datetime.today() and set it to UTC-8?

mindthief
  • 12,755
  • 14
  • 57
  • 61
  • related: [How do I get the UTC time of “midnight” for a given timezone?](http://stackoverflow.com/questions/373370/how-do-i-get-the-utc-time-of-midnight-for-a-given-timezone) – jfs Feb 10 '13 at 11:14
  • 50
    Seems like we can use `datetime.now().astimezone()` since Python 3.6 – johnchen902 Jul 28 '17 at 07:02
  • 1
    Thanks @johnchen902. Your comment has been converted to an answer here: https://stackoverflow.com/a/49059780/247696 – Flimm Sep 16 '20 at 07:53
  • 2
    @johnchen's answer autotomatically gets the answer in the local time zone. To get the same answer as far back as Python 3.3: use `from datetime import datetime, timezone; datetime.now(timezone.utc).astimezone()` – FutureNerd Jan 16 '21 at 06:23
  • 3
    Note that `datetime.date` objects can't have an associated time zone, only `datetime.datetime` objects can. So the question is about `datetime.datetime.today`, and not about `datetime.date.today`, which are different. I've edited the question to make this slightly clearer. – Flimm Jun 04 '21 at 08:39

19 Answers19

494

In the standard library, there is no cross-platform way to create aware timezones without creating your own timezone class. (Edit: Python 3.9 introduces zoneinfo in the standard library which does provide this functionality.)

On Windows, there's win32timezone.utcnow(), but that's part of pywin32. I would rather suggest to use the pytz library, which has a constantly updated database of most timezones.

Working with local timezones can be very tricky (see "Further reading" links below), so you may rather want to use UTC throughout your application, especially for arithmetic operations like calculating the difference between two time points.

You can get the current date/time like so:

import pytz
from datetime import datetime
datetime.utcnow().replace(tzinfo=pytz.utc)

Mind that datetime.today() and datetime.now() return the local time, not the UTC time, so applying .replace(tzinfo=pytz.utc) to them would not be correct.

Another nice way to do it is:

datetime.now(pytz.utc)

which is a bit shorter and does the same.


Further reading/watching why to prefer UTC in many cases:

Flimm
  • 136,138
  • 45
  • 251
  • 267
AndiDog
  • 68,631
  • 21
  • 159
  • 205
  • 92
    How about `datetime.now(pytz.utc)` instead of `datetime.utcnow().replace(tzinfo = pytz.utc)` ? – eumiro Dec 25 '10 at 15:24
  • @eumiro: Yes, that works too. I'll add it to the answer. thanks – AndiDog Dec 25 '10 at 16:19
  • I ended up removing timezone awareness in the aware date by doing `d1.replace(tzinfo=None)`, and then using `datetime.utcnow()` to get the current UTC time, and then I was able to subtract the two times correctly. Though, it looks like pytz is pretty easy to use. I will likely move to this in the near future so that I can have more flexibility in my application. Thanks! :) – mindthief Dec 26 '10 at 01:10
  • @mindthief: Actually, removing timezone awareness doesn't make much sense. Python is able to subtract two aware datetime instances. Removing timezone information might give you wrong results if you're working with multiple different timezones. – AndiDog Dec 26 '10 at 10:27
  • yep I made sure I was using only UTC times :). Though of course, having them as aware is certainly preferable and I'll likely move to this soon. This is just what I did to get it working before I saw your answer. – mindthief Dec 27 '10 at 05:52
  • 7
    `now(utc)` doesn't return today (unless it is midnight in UTC), it returns the current time in UTC. You need also [`.replace(hour=0, minute=0, ...)` to get the beginning of the day (like `datetime.today()`)](http://stackoverflow.com/a/25421145/4279) – jfs Aug 21 '14 at 08:05
  • 1
    The [docs](https://docs.python.org/3/library/datetime.html#datetime.datetime.today) say that `today()` returns the current time, not midnight. If there is a use case where midnight is required, yes, the replacement needs to be done accordingly. Since the original question was about datetime difference, I don't think that midnight is required. – AndiDog Aug 21 '14 at 13:42
  • 1
    @AndiDog: My comment implies that I thought (incorrectly) that `datetime.today()` is `combine(date.today(), time())`. `datetime` has both `.now()` and `.today()` methods that (as you've correctly pointed out) return (almost) the same thing. There is no `date.now()` method. `date` and `datetime` objects are not interchangeable. Using a datetime object instead of a `date` object can produce subtle bugs; I don't see any reason for `datetime.today()` to exist if it is a near duplicate of `datetime.now()`. – jfs Aug 24 '14 at 23:28
  • 13
    Adding to this answer, if you happen to be using django, always use `timezone.now()` instead of `datetime.now()` since it will use UTC automatically if `USE_TZ = True`. `timezone` is locating at `django.utils.timezone`, documentation: https://docs.djangoproject.com/en/1.11/topics/i18n/timezones/ – Ryan Aug 04 '17 at 03:05
  • @MrE, your edit is nonsense and the result is not *"WRONG"* (sic). It makes perfect sense that if I'm working in Los Angeles time and I add 90 days to a datetime whose time is 10PM, then I get a datetime whose time is 10PM, even if I cross a daylight savings boundary. That's the *entire point* of working in localised time; it's the result that makes sense if, for instance, I'm generating meeting times for a club in Los Angeles. Are there circumstances in which this is undesirable? Certainly. But if I'm doing arithmetic with localised timezones, it's because I *explicitly want* this behaviour. – Mark Amery Jan 30 '18 at 16:14
  • @AndiDog If I were you, I'd roll back MrE's edit; as I note in my comment above, I think it makes no sense at all and simply shows precisely the behaviour you'd expect while declaring it *"WRONG"* without explanation. But it's your answer, and you're still active here, so I'll leave the choice to you. – Mark Amery Jan 30 '18 at 16:16
  • @MarkAmery maybe it is not 'WRONG' in your use case, adding days. but try adding hours, and usually that's not what you want. So yes it depends on the use case. My edit was to point out the difference between using simple timedelta and passing through UTC. You have both methods here. Use the one that suits you. – MrE Jan 30 '18 at 16:24
  • @MarkAmery i edited the answer so it is more clear. I hope that suits you better – MrE Jan 30 '18 at 16:28
  • 1
    I've replaced the whole block added and redacted over again because 1) it contains invalid Python prompt syntax, 2) non-repeatable input values (now) and 3) you only describe one case of where timezone-aware-vs-UTC matters while there are too many existing sources on the Internet to which we could refer for great explanations of the whole context and more problem cases. Ultimately, OP only asked how to create a timezone-aware datetime object, so a short hint/link is preferred instead of having >60% of the answer dedicated to another problem. – AndiDog Jan 31 '18 at 21:12
  • The "What every developer should know about time" link now redirects to windward's homepage, was the content the same as this one? http://blogs.windwardreports.com/davidt/2009/11/what-every-developer-should-know-about-time.html – Adam Parkin Oct 05 '18 at 16:56
  • 1
    @AdamParkin thanks for the heads-up! Since your proposed blog article only partially matches the content, I've replaced it with a web archive link. – AndiDog Oct 05 '18 at 20:26
  • 9
    In python3 you can simply use `datetime.now(timezone.utc)` – alias51 Jul 09 '20 at 21:25
  • 1
    In 3.9+, this is moved into the Std Lib See @zimm's answer below! – Marc Aug 18 '21 at 16:00
  • Please update this answer to correctly put stdlib zoneinfo first and foremost. – wim May 26 '22 at 21:45
190

Get the current time, in a specific timezone:

import datetime
import pytz
my_date = datetime.datetime.now(pytz.timezone('US/Pacific'))

Remember to install pytz first.

Flimm
  • 136,138
  • 45
  • 251
  • 267
philfreo
  • 41,941
  • 26
  • 128
  • 141
  • 3
    See [this](https://github.com/newvem/pytz/blob/f137ff00112a9682bc4e4945067b3b88f158d010/pytz/tzinfo.py#L192-L193). – wim Jun 06 '17 at 16:58
  • 2
    you should NOT use localized time except for output. Many things go wrong when using timezone based datetime: a simple timedelta does not take daylight saving into account unless you are in UTC time to begin with. Always use timezone aware based on UTC. convert to local timezone on output when needed. – MrE Sep 13 '17 at 18:36
  • 9
    To reiterate a disagreement with @MrE that I've previously voiced in the comments on the accepted answer: there are perfectly valid reasons to work with localised datetimes and *"you should NOT use localized time except for output"* is overly broad advice. Suppose you're adding 1 day to a datetime a few hours before a daylight savings boundary at which the clocks go back by an hour. What result do you want? If you think the time should be the same, use localised datetimes. If you think it should be an hour earlier, use UTC or timezone-naive datetimes. Which makes sense is domain-dependent. – Mark Amery Jan 31 '18 at 14:42
  • 1
    @MarkAmery as much as I can agree that you may want to ADD or SUBTRACT a number of days or hours and not care about timezone issues (like your example), this comment pertains to passing timezone corrected time to a client. Since Python is primarily used for back end processes, it passes times to a client. The server should always pass date/time in UTC and the client should convert it to its own local date/time/timezone, otherwise bad things happen: just check the output of `datetime.datetime(2016, 11, 5, 9, 43, 45, tzinfo=pytz.timezone('US/Pacific'))` and see if that's what you expected – MrE Jan 31 '18 at 22:46
  • 3
    *"The server should always pass date/time in UTC and the client should convert it to its own local date/time/timezone"* - no, this isn't universally true. Sometimes using the client's timezone is inappropriate and the appropriate timezone needs to be transmitted as part of the data. If, as a Londoner, I view the meeting times of a San Francisco chess club on their website, I should see them in San Francisco time, not in London time. – Mark Amery Jan 31 '18 at 22:57
  • @MrE (As for `datetime.datetime(2016, 11, 5, 9, 43, 45, tzinfo=pytz.timezone('US/Pacific'))`, yeah, that's a horrible trap of Python's `datetime` API. For anyone wondering WTF is up with the result, the answer is that the `tzinfo` argument to the `datetime` constructor is [just plain broken](https://stackoverflow.com/a/24856814/1709587) for `pytz` timezones; you need to use the timezones' `.localize()` method to create timezone-aware datetimes instead.) – Mark Amery Jan 31 '18 at 23:06
  • "If, as a Londoner, I view the meeting times of a San Francisco chess club on their website, I should see them in San Francisco time, not in London time." Then don't use timezones, UTC or anything, you just need naive date times. – MrE Jan 31 '18 at 23:10
  • In 3.9+, this is moved into the Std Lib See @zimm's answer below! – Marc Aug 18 '21 at 16:00
176

In Python 3.2+: datetime.timezone.utc:

The standard library makes it much easier to specify UTC as the time zone:

>>> import datetime
>>> datetime.datetime.now(datetime.timezone.utc)
datetime.datetime(2020, 11, 27, 14, 34, 34, 74823, tzinfo=datetime.timezone.utc)

You can also get a datetime that includes the local time offset using astimezone:

>>> datetime.datetime.now(datetime.timezone.utc).astimezone()
datetime.datetime(2020, 11, 27, 15, 34, 34, 74823, tzinfo=datetime.timezone(datetime.timedelta(seconds=3600), 'CET'))

(In Python 3.6+, you can shorten the last line to: datetime.datetime.now().astimezone())

If you want a solution that uses only the standard library and that works in both Python 2 and Python 3, see jfs' answer.

In Python 3.9+: zoneinfo to use the IANA time zone database:

In Python 3.9, you can specify particular time zones using the standard library, using zoneinfo, like this:

>>> from zoneinfo import ZoneInfo
>>> datetime.datetime.now(ZoneInfo("America/Los_Angeles"))
datetime.datetime(2020, 11, 27, 6, 34, 34, 74823, tzinfo=zoneinfo.ZoneInfo(key='America/Los_Angeles'))

zoneinfo gets its database of time zones from the operating system, or from the first-party PyPI package tzdata if available.

Flimm
  • 136,138
  • 45
  • 251
  • 267
  • 1
    There's also `datetime.datetime.now(tz=datetime.timezone(datetime.timedelta(seconds=3600)))` which can utilize `timedelta` to specify the `tz` parameter to `now()`. – Torxed Feb 20 '23 at 16:20
  • 2
    @Torxed That's fascinating. That would give you a fixed offset time zone. I would prefer to use a time zone linked to a location or political area like `America/Los_Angeles`, as the latter takes into account daylight saving time. – Flimm Feb 21 '23 at 11:29
  • I guess you could use [.tzname](https://docs.python.org/3/library/datetime.html#datetime.tzinfo.tzname) together with [.dst](https://docs.python.org/3/library/datetime.html#datetime.tzinfo.dst) for that and feed it to `tz=`. There's an example of a [custom class](https://docs.python.org/3/library/datetime.html#examples-of-usage-time) too. But yea, `ZoneInfo` is neat, just thought I'd point people to a builtin that does the same thing *(more or less *in*conveniently)* – Torxed Feb 21 '23 at 20:42
45

A one-liner using only the standard library works starting with Python 3.3. You can get a local timezone aware datetime object using astimezone (as suggested by johnchen902):

from datetime import datetime, timezone

aware_local_now = datetime.now(timezone.utc).astimezone()

print(aware_local_now)
# 2020-03-03 09:51:38.570162+01:00

print(repr(aware_local_now))
# datetime.datetime(2020, 3, 3, 9, 51, 38, 570162, tzinfo=datetime.timezone(datetime.timedelta(0, 3600), 'CET'))
Mihai Capotă
  • 2,271
  • 2
  • 32
  • 24
  • 2
    The documentation is a big help, found here: https://docs.python.org/3.8/library/datetime.html#datetime.datetime.astimezone . This incredibly basic piece of functionality is buried deep in an obscure paragraph in the docs, so that this stackoverflow answer is effectively the only place on the entire internet with this information. In the documentation, it can also be seen that, since Python 3.6, `datetime.now()` can be called without any arguments and return the correct local result (naive `datetime`s are presumed to be in the local time zone). – atimholt Feb 08 '20 at 03:25
  • 2
    Passing `timezone.utc` to `now` is not needed here, you can just run `datetime.datetime.now().astimezone()` – Flimm Sep 16 '20 at 07:55
  • 1
    @Flimm, it is needed if you want to support Python <3.6. – Mihai Capotă Sep 16 '20 at 21:34
  • **str(datetime.now(timezone.utc).astimezone())** -> **'2020-09-18 19:19:33.508176+05:30'** | Is there any way I get the date object with **UTC** timezone? It's always giving the timezone as **IST**. I don't want to use any other python library like **pytz** – Saurav Kumar Sep 18 '20 at 13:51
  • 4
    @SauravKumar, that is not the original question. Try `datetime.now(timezone.utc)`. – Mihai Capotă Sep 18 '20 at 17:07
21

Here's a stdlib solution that works on both Python 2 and 3:

from datetime import datetime

now = datetime.now(utc) # Timezone-aware datetime.utcnow()
today = datetime(now.year, now.month, now.day, tzinfo=utc) # Midnight

where today is an aware datetime instance representing the beginning of the day (midnight) in UTC and utc is a tzinfo object (example from the documentation):

from datetime import tzinfo, timedelta

ZERO = timedelta(0)

class UTC(tzinfo):
    def utcoffset(self, dt):
        return ZERO

    def tzname(self, dt):
        return "UTC"

    def dst(self, dt):
        return ZERO

utc = UTC()

Related: performance comparison of several ways to get midnight (start of a day) for a given UTC time. Note: it is more complex, to get midnight for a time zone with a non-fixed UTC offset.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
jfs
  • 399,953
  • 195
  • 994
  • 1,670
  • In 3.9+, this is moved into the Std Lib See @zimm's answer below! – Marc Aug 18 '21 at 16:00
  • @Marc: utc timezone is available in stdlib since Python 3.2 and [almost] functional local timezone since Python 3.3. Python 3.9 introduces zoneinfo (many other timezones) into stdlib [that is great] but you don't need it in this case. See [@Flimm's answer](https://stackoverflow.com/a/39168818/4279). – jfs Aug 18 '21 at 18:27
20

Another method to construct time zone aware datetime object representing current time:

import datetime
import pytz

pytz.utc.localize( datetime.datetime.utcnow() )  

You can install pytz from PyPI by running:

$ pipenv install pytz
Flimm
  • 136,138
  • 45
  • 251
  • 267
Dariusz Walczak
  • 4,848
  • 5
  • 36
  • 39
  • note that `pytz.utc` and `pytz.UTC` are both defined (and are the same) – drevicko Feb 28 '13 at 23:36
  • 3
    This answer is better than the accepted one since it is more universal: `replace()`ing timezone is generally error-prone in most other uses, while `localize()`ing is the preferred way of assigning timezone to naive timestamps. – Antony Hatchkins Sep 17 '14 at 04:59
  • @AntonyHatchkins: `.localize()` method fails for ambiguous local times (non-utc input). [@philfreo's answer that uses `.now(pytz_timezone)`](http://stackoverflow.com/a/16660476/4279) continues to work in such cases. – jfs Apr 18 '15 at 18:40
  • As specified in python docs, `.now(pytz_timezone)` does exactly the same as `localize(utcnow)` - first it generates current time in UTC, then it assigns it a timezone: "<...>In this case the result is equivalent to `tz.fromutc(datetime.utcnow().replace(tzinfo=tz))`". Both answers are correct and work always. – Antony Hatchkins Apr 19 '15 at 08:20
  • 1
    The only naive (non-utc) time that can be safely made timezone aware is _now_ : the underlying system is supposed to know the UTC value and `pytz` through OLSON db is supposed to know how to convert it to any timezone in the world. Making any other naive (non-utc) time timezone aware is difficult because of the ambiguity during daylight saving shifts. That's not a problem of `.localize` (feeding it a `is_dst` value makes it work for any date). That's an inherent problem of daylight saving practice. – Antony Hatchkins Apr 19 '15 at 08:36
  • @AntonyHatchkins **wrong.** Ask a separate question if don't understand why `.localize(now)` fails. – jfs Apr 20 '15 at 18:31
  • or ask a separate question if you don't understand why `localize` is better than `replace` for non-utc dates. – Antony Hatchkins Apr 21 '15 at 10:01
  • @AntonyHatchkins: use `@..` if you want me to be notified about your comments as I do. What is wrong: 1. `pytz.utc.localize()` is better than `.replace(tzinfo=pytz.utc)` -- **wrong** 2. `non_utc_tz.localize(utcnow)` is better than `.now(non_utc_tz)` -- **wrong**. Look at your [first](http://stackoverflow.com/questions/4530069/python-how-to-get-a-value-of-datetime-today-that-is-timezone-aware#comment40506068_14796941) and [2nd](http://stackoverflow.com/questions/4530069/python-how-to-get-a-value-of-datetime-today-that-is-timezone-aware#comment47588748_14796941) comments. – jfs Apr 24 '15 at 00:07
  • @J.F. Sebastian __wrong__. `localize` doesn't fail for ambiguous timedates (non-utc input) - if you give it correct `is_dst` parameter. Look at your [first](http://stackoverflow.com/questions/4530069/python-how-to-get-a-value-of-datetime-today-that-is-timezone-aware/14796941?noredirect=1#comment47577143_14796941) comment. `pytz.utc.localize()` does exactly the same as `.replace(tzinfo=pytz.utc)` - this is not my point. If you repeat one and the same incorrect thing million times it wont become more correct just because of that. – Antony Hatchkins Apr 24 '15 at 08:57
  • Also, `non_utc_tz.localize(utcnow)` does exactly the same thing as `.replace(tzinfo=pytz.utc)`. I see no point in arguing about that. – Antony Hatchkins Apr 24 '15 at 09:01
  • @AntonyHatchkins: I'll remind you: the quesiton is "timezone-aware datetime.today()". Fact: `datetime.now(tz)` works without specifying `is_dst` parameter. Fact: `.replace(tzinfo=pytz.utc)` works for UTC timezone. Fact: `tz.localize(now)` may fail for non-utc timezones. You can disambiguate using `is_dst` (for some timezones, it works only in recent versions) but how do you find out `is_dst` programmatically anyway? Conslusion: `.now(tz)` is a better option than `tz.localize(now, is_dst=is_dst)` here (you could use `.replace(tzinfo=pytz.utc)` for performance reasons if your profiler says so). – jfs Apr 24 '15 at 09:30
  • @J.F. Sebastian: I'll remind you: I didn't suggest `non_utc_tz.localize(non_utc_now, is_dst=is_dst)`. That's what you say. Fact: your conclusion doesn't have anything to do with what I said. – Antony Hatchkins Apr 29 '15 at 17:26
  • Let me repeat my point: Both `replace` and `localize` can be used for assigning a time zone to a naive timestamp. Both work identically when the operation is unambiguous. But if the assignment is ambiguous `localize` is better because it will warn you about it through raising an exception which you can catch and then re-run it with the correct is_dst value if you know it (you can ask an end-user for example) or arbitrary value giving you 50:50 chance of the right guess. `Replace` on the other hand will give the same 50:50 chance of the right guess but it wont inform you about the ambiguity. – Antony Hatchkins Apr 29 '15 at 17:28
  • Otherwise I agree with all of your Facts. And I also find `datetime.now(pytz.utc)` the best way to do it. – Antony Hatchkins Apr 29 '15 at 17:38
  • Plz review my [new answer](http://stackoverflow.com/a/29951134/237105) to this question. – Antony Hatchkins Apr 29 '15 at 18:03
  • @AntonyHatchkins: **wrong**. `.replace()` may fail for any timezone that had a different utc offset in the past even if the result is unambiguous. Always use `.localize()` instead of `.replace()` for timezones with a non-fixed utc offset: [Datetime Timezone conversion using pytz](http://stackoverflow.com/q/27531718/4279) – jfs Mar 27 '16 at 17:10
  • @J.F. Sebastian **at last** you got my my point ("localize()ing is the preferred way of assigning timezone to naive timestamps"). Yeah, I agree, **you have been wrong** from the very beginning. One year is really not so long for such a formidable question ;) – Antony Hatchkins Mar 29 '16 at 16:33
  • @AntonyHatchkins: **wrong**. Your point: [*".now(pytz_timezone) does exactly the same as localize(utcnow)"*](http://stackoverflow.com/questions/4530069/python-how-to-get-a-value-of-datetime-today-that-is-timezone-aware/14796941?noredirect=1#comment47588748_14796941) is still wrong. Reread my comments carefully this time. – jfs Mar 29 '16 at 16:45
  • @J.F. Sebastian you can't admit **you've been wrong** all the time? Calm down, reread my comments carefully and stop spamming me. – Antony Hatchkins Mar 29 '16 at 19:30
  • @AntonyHatchkins: you can't provide a single statement from my comments that is wrong. If you continue to post incorrect statements; I might continue to refute them. It looks like it is pointless in your case but I might continue to do it so that you wouldn't misled other people. – jfs Mar 29 '16 at 20:12
  • @J.F. Sebastian You seem to like long-term conversations. I'll answer you in a year. Noone will read a conversation this long and this pointless anyway. Didn't I ask you to stop **spamming** me? – Antony Hatchkins Mar 29 '16 at 20:30
  • In 3.9+, this is moved into the Std Lib See @zimm's answer below! – Marc Aug 18 '21 at 16:00
10

If you are using Django, you can set dates non-tz aware (only UTC).

Comment the following line in settings.py:

USE_TZ = True
laffuste
  • 16,287
  • 8
  • 84
  • 91
  • 9
    where did you see django mentioned in this question? – vonPetrushev Jul 30 '13 at 15:22
  • 3
    Some kind soul deleted my previous comment-apology here, so again: shame on me, wrong answer as the question is not Django-specific. I left it because it might help some users anyway but I will delete it when the score approaches 0. If this answer is inappropriate, feel free to downvote. – laffuste Jan 14 '20 at 03:26
10

Use dateutil as described in Python datetime.datetime.now() that is timezone aware:

from dateutil.tz import tzlocal
# Get the current date/time with the timezone.
now = datetime.datetime.now(tzlocal())
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
G. Führ
  • 401
  • 3
  • 8
  • 2
    See [this answer](http://stackoverflow.com/questions/13218506/how-to-get-system-timezone-setting-and-pass-it-to-pytz-timezone) by J.F. Sebastian for a situation where this gives incorrect result. – Antony Hatchkins Apr 29 '15 at 18:01
  • 2
    I think the error in the other post is only relevant in specific use cases. The `tzlocal()` function still is one of the simplest solutions and should definitely be mentioned here. – user8162 Oct 08 '18 at 15:28
  • 1
    In 3.9+, this is moved into the Std Lib See @zimm's answer below! – Marc Aug 18 '21 at 15:59
9

Here is one way to generate it with the stdlib:

import time
from datetime import datetime

FORMAT='%Y-%m-%dT%H:%M:%S%z'
date=datetime.strptime(time.strftime(FORMAT, time.localtime()),FORMAT)

date will store the local date and the offset from UTC, not the date at UTC timezone, so you can use this solution if you need to identify which timezone the date is generated at. In this example and in my local timezone:

date
datetime.datetime(2017, 8, 1, 12, 15, 44, tzinfo=datetime.timezone(datetime.timedelta(0, 7200)))

date.tzname()
'UTC+02:00'

The key is adding the %z directive to the representation FORMAT, to indicate the UTC offset of the generated time struct. Other representation formats can be consulted in the datetime module docs

If you need the date at the UTC timezone, you can replace time.localtime() with time.gmtime()

date=datetime.strptime(time.strftime(FORMAT, time.gmtime()),FORMAT)

date    
datetime.datetime(2017, 8, 1, 10, 23, 51, tzinfo=datetime.timezone.utc)

date.tzname()
'UTC'

Edit

This works only on python3. The z directive is not available on python 2 _strptime.py code

jcazor
  • 123
  • 1
  • 7
  • ValueError: 'z' is a bad directive in format '%Y-%m-%dT%H:%M:%S%z' – jno Jun 22 '17 at 10:16
  • You are on python 2, right? Unfortunately, It seems the z directive is not available on python 2. [_strptime.py code](https://github.com/python/cpython/blob/2.7/Lib/_strptime.py#L194) – jcazor Jun 23 '17 at 21:03
8

It should be emphasized that since Python 3.6, you only need the standard lib to get a timezone aware datetime object that represents local time (the setting of your OS). Using astimezone()

import datetime

datetime.datetime(2010, 12, 25, 10, 59).astimezone()
# e.g.
# datetime.datetime(2010, 12, 25, 10, 59, tzinfo=datetime.timezone(datetime.timedelta(seconds=3600), 'Mitteleuropäische Zeit'))

datetime.datetime(2010, 12, 25, 12, 59).astimezone().isoformat()
# e.g.
# '2010-12-25T12:59:00+01:00'

# I'm on CET/CEST

(see @johnchen902's comment).

Note there's a small caveat though, don't expect any "DST-awareness" from a timedelta timezone.

FObersteiner
  • 22,500
  • 8
  • 42
  • 72
5

pytz is a Python library that allows accurate and cross platform timezone calculations using Python 2.3 or higher.

With the stdlib, this is not possible.

See a similar question on SO.

Community
  • 1
  • 1
user225312
  • 126,773
  • 69
  • 172
  • 181
3

Here is a solution using a readable timezone and that works with today():

from pytz import timezone

datetime.now(timezone('Europe/Berlin'))
datetime.now(timezone('Europe/Berlin')).today()

You can list all timezones as follows:

import pytz

pytz.all_timezones
pytz.common_timezones # or
WJA
  • 6,676
  • 16
  • 85
  • 152
1

Getting a timezone-aware date in utc timezone is enough for date subtraction to work.

But if you want a timezone-aware date in your current time zone, tzlocal is the way to go:

from tzlocal import get_localzone  # pip install tzlocal
from datetime import datetime
datetime.now(get_localzone())

PS dateutil has a similar function (dateutil.tz.tzlocal). But inspite of sharing the name it has a completely different code base, which as noted by J.F. Sebastian can give wrong results.

Community
  • 1
  • 1
Antony Hatchkins
  • 31,947
  • 10
  • 111
  • 111
  • python is usually used on the server. Local time zone on a server is usually pointless and should always be set to UTC. Setting datetime tzinfo this way fails in some cases. better use UTC, then localize to the wanted timezone only on output. any timedelta computation for example doesn't consider daylight saving, so these should be done in UTC, then localized. – MrE Sep 13 '17 at 18:34
  • @MrE Wrong, offtopic, examples? – Antony Hatchkins Sep 15 '17 at 05:02
  • try using a datetime object localized in a timezone that observes daylight saving, add a number of days to change daylight saving state, and you'll see that operating on datetime objects in localized timezone fails and won't respect daylight saving. Hence my comment that you should ALWAYS do any datetime operation in UTC time. – MrE Sep 15 '17 at 05:07
  • the point being: don't do this, do your operations in UTC, and then use datetime.astimezone(timezone) to convert to the localtime zone on output. – MrE Sep 15 '17 at 05:10
  • In 3.9+, this is moved into the Std Lib See @zimm's answer below! – Marc Aug 18 '21 at 15:59
1

Another alternative, in my mind a better one, is using Pendulum instead of pytz. Consider the following simple code:

>>> import pendulum

>>> dt = pendulum.now().to_iso8601_string()
>>> print (dt)
2018-03-27T13:59:49+03:00
>>>

To install Pendulum and see their documentation, go here. It have tons of options (like simple ISO8601, RFC3339 and many others format support), better performance and tend to yield simpler code.

ng10
  • 1,660
  • 1
  • 15
  • 19
  • not sure why the vote down here, this code is working in multiple programs that run 7/24 for me :). not that i mind other opinion, but please say why it is not working for you, to allow me to check it. Thanks in advance – ng10 Mar 16 '19 at 17:06
  • this is a great suggestion, in this messy field like date time manipulation readability and ease of use comes first, IMO. – Loki Oct 13 '20 at 20:00
1

Especially for non-UTC timezones:

The only timezone that has its own method is timezone.utc, but you can fudge a timezone with any UTC offset if you need to by using timedelta & timezone, and forcing it using .replace.

In [1]: from datetime import datetime, timezone, timedelta

In [2]: def force_timezone(dt, utc_offset=0):
   ...:     return dt.replace(tzinfo=timezone(timedelta(hours=utc_offset)))
   ...:

In [3]: dt = datetime(2011,8,15,8,15,12,0)

In [4]: str(dt)
Out[4]: '2011-08-15 08:15:12'

In [5]: str(force_timezone(dt, -8))
Out[5]: '2011-08-15 08:15:12-08:00'

Using timezone(timedelta(hours=n)) as the time zone is the real silver bullet here, and it has lots of other useful applications.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
tmck-code
  • 2,710
  • 1
  • 10
  • 7
0

Tyler from 'howchoo' made a really great article that helped me get a better idea of the Datetime Objects, link below

Working with Datetime

essentially, I just added the following to the end of both my datetime objects

.replace(tzinfo=pytz.utc)

Example:

import pytz
import datetime from datetime

date = datetime.now().replace(tzinfo=pytz.utc)
Jose
  • 370
  • 1
  • 4
  • 14
-1

If you get current time and date in python then import date and time,pytz package in python after you will get current date and time like as..

from datetime import datetime
import pytz
import time
str(datetime.strftime(datetime.now(pytz.utc),"%Y-%m-%d %H:%M:%S%t"))
-1

Use the timezone as shown below for a timezone-aware date time. The default is UTC:

from django.utils import timezone
today = timezone.now()
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
-1

try pnp_datetime, all the time been used and returned is with timezone, and will not cause any offset-naive and offset-aware issues.

>>> from pnp_datetime.pnp_datetime import Pnp_Datetime
>>>
>>> Pnp_Datetime.utcnow()
datetime.datetime(2020, 6, 5, 12, 26, 18, 958779, tzinfo=<UTC>)