Related
When I print the unix epoch, with something like time.time() it seems to print the timestamp in my local timezone. Especially from what converters like: https://www.epochconverter.com tell me.
Does anyone know how I can print the UNIX Epoch/timestamp but actually in UTC time? Thank you so much.
There's no such thing as "UTC UNIX Epoch time". There's UNIX Epoch time, period. It's the same all over the world. It is timezone independent. As long as your computer's clock is set correctly, time.time() will give you the correct UNIX epoch time. datetime.datetime.now().timestamp() will give you the same UNIX Epoch time. You don't need to convert anything and there's nothing to convert.
Now, to address the comment by Deepak, which I think is important:
I do get different responses for datetime.now() vs datetime.utcnow() corresponding to my local time vs UTC. Accordingly, the seconds elapsed has a difference of 19800s (+5:30 for my time zone) for me.
This is a known pitfall with datetime.utcnow and is in fact addressed in the manual:
Warning: Because naive datetime objects are treated by many datetime methods as local times, it is preferred to use aware datetimes to represent times in UTC. As such, the recommended way to create an object representing the current time in UTC is by calling datetime.now(timezone.utc).
To unpack this a bit:
When you call datetime.now(), it returns you a naïve datetime object (meaning it has no tzinfo timezone attached) for your current local time:
>>> datetime.now()
datetime.datetime(2021, 4, 16, 9, 50, 3, 571235)
The time represents my current wall clock time, but it does not know what timezone it's in. So this timestamp could really represent about two dozen different absolute timestamps on this planet, depending on what timezone you take it as.
When using this object's timestamp() method to convert it to an absolute UNIX timestamp, some timezone must be assumed. Python will assume your computer's local timezone for the conversion. Which is fine for datetime.now(), because this timestamp was created with my local time anyway:
>>> datetime.now().timestamp()
1618559403.571235
This is the correct UNIX timestamp at the time of writing. Not "for my local timezone", but globally. At the time of writing, it's 1618559403 everywhere in the world.
Now, if you use datetime.utcnow(), it will give you a naïve timestamp for the current UTC time:
>>> datetime.utcnow()
datetime.datetime(2021, 4, 16, 7, 50, 3, 571235)
Note how it's off by 2 hours from the previous datetime.now(), which is fine, because that's the current local time in the UTC timezone. But since this timestamp is still naïve, when converting it to a UNIX timestamp, Python must assume some timezone, and will assume my local timezone. So it's giving me the UNIX timestamp for 7:50:03 of my local time:
>>> datetime.utcnow().timestamp()
1618552203.571235
Which is not the current UNIX time. It's off by two hours. It's the UNIX time of two hours ago.
If we'd do this properly, by creating an aware timestamp and taking its UNIX time, we'd get the correct UNIX time:
>>> datetime.now(timezone.utc)
datetime.datetime(2021, 4, 16, 7, 50, 3, 571235, tzinfo=datetime.timezone.utc)
>>> datetime.now(timezone.utc).timestamp()
1618559403.571235
Note how the human readable time is the same as we got for datetime.utcnow(), but the UNIX timestamp is correctly the one we got for datetime.now().timestamp(). Which is also the same as we'd get for time.time():
>>> from time import time
>>> time()
1618559403.571235
When pasting this into https://www.epochconverter.com, which you reference:
GMT: Friday, 16 April 2021 07:50:03.571
Your time zone: Friday, 16 April 2021 09:50:03.571 GMT+02:00 DST
Which is correct. In UTC/GMT, this UNIX timestamp represents 7:50am, and in my local timezone it's 9:50am.
If we take the incorrect naïve datetime.utcnow() timestamp 1618552203.571235, the result is:
GMT: Friday, 16 April 2021 05:50:03.571
Your time zone: Friday, 16 April 2021 07:50:03.571 GMT+02:00 DST
Which is incorrect. That was 2 hours ago, not now.
I have a csv file with the datetime in unixtimestamp format with milliseconds and timezone information in milliseconds as well. I want to convert this into a more usable datetime format for further processing.
For example, the time is 1437323953822 and timezone is -14400000.
I can convert the timestamp into a datetime by using
datetime.datetime.fromtimestamp(1437323953822/1000)
But how do I now incorporate the timezone which is -4 UTC time from what I know.
(-14400000 / 1000 / 60 / 60) = -4
How do I use this timezone to get the actual time?
fromtimestamp can also take another parameter for the timezone, a subclass of tzinfo:
classmethod datetime.fromtimestamp(timestamp[, tz])
Return the local date and time corresponding to the POSIX timestamp,
such as is returned by time.time(). If optional argument tz is
None or not specified, the timestamp is converted to the platform’s
local date and time, and the returned datetime object is naive.
Else tz must be an instance of a class tzinfo subclass, and the
timestamp is converted to tz‘s time zone. In this case the result is
equivalent to
tz.fromutc(datetime.utcfromtimestamp(timestamp).replace(tzinfo=tz)).
fromtimestamp() already returns your local time i.e., you don't need to attach the utc offset if fromtimestamp() determines it correctly automatically:
#!/usr/bin/env python
from datetime import datetime
local_time = datetime.fromtimestamp(1437323953822 * 1e-3)
# -> datetime.datetime(2015, 7, 19, 12, 39, 13, 822000)
fromtimestamp() may fail in some cases e.g., if the local timezone had a different utc offset in the past and fromtimestamp() does not use a historical timezone database on a given platform (notably, Windows). In that case, construct the local time explicitly from utc time and the given utc offset:
#!/usr/bin/env python
from datetime import datetime, timedelta
utc_time = datetime(1970, 1, 1) + timedelta(milliseconds=1437323953822)
utc_offset = timedelta(milliseconds=-14400000)
local_time = utc_time + utc_offset
# -> datetime.datetime(2015, 7, 19, 12, 39, 13, 822000)
Python always expects POSIX Epoch and therefore it is ok to hardcode it. The explicit formula may be more precise (no rounding error) and it may accept a wider range of input timestamps (fromtimestamp() range depends on platform and may be narrower than the corresponding datetime range).
This question is old but I want to give a slightly more comprehensive answer.
About the unix timestamp:
The timestamp is the number of seconds (or milliseconds) elapsed since an absolute point in time, midnight of Jan 1 1970 in UTC time. (UTC is Greenwich Mean Time without Daylight Savings time adjustments.)
fromtimestamp does convert the unix timestamp to your platform's time. If you are working across different platforms, it is important to set the platform's timezone correctly. If you want it to be in UTC instead, then utcfromtimestamp should be used instead.
To answer OP's question directly, the following code will create a timezone based on the offset.
from datetime import datetime, timezone, timedelta
ts = int('1604750712')
tz = timezone(-timedelta(hours=4))
print(datetime.fromtimestamp(ts, tz).strftime('%Y-%m-%d %H:%M:%S'))
timezone object is an concrete class of tzinfo, I have initiated it with a negative offset of 4 hours from UTC.
from datetime import datetime
import pytz # pip install pytz
tz = pytz.timezone('Asia/Dubai')
ts = int('1604750712')
print(datetime.fromtimestamp(ts,tz).strftime('%d-%m-%Y %H:%M:%S'))
Ok let me first start by saying my timezone is CET/CEST. The exact moment it changes from CEST to CET (back from DST, which is GMT+2, to normal, which GMT+1, thus) is always the last Sunday of October at 3AM. In 2010 this was 31 October 3AM.
Now note the following:
>>> import datetime
>>> import pytz.reference
>>> local_tnz = pytz.reference.LocalTimezone()
>>> local_tnz.utcoffset(datetime.datetime(2010, 10, 31, 2, 12, 30))
datetime.timedelta(0, 3600)
This is wrong as explained above.
>>> local_tnz.utcoffset(datetime.datetime(2010, 10, 30, 2, 12, 30))
datetime.timedelta(0, 7200)
>>> local_tnz.utcoffset(datetime.datetime(2010, 10, 31, 2, 12, 30))
datetime.timedelta(0, 7200)
Now it is suddenly correct :/
I know there are several questions about this already, but the solution given is always "use localize", but my problem here is that the LocalTimezone does not provide that method.
In fact, I have several timestamps in milliseconds of which I need the utcoffset of the local timezone (not just mine, but of anyone using the program). One of these is 1288483950000 or Sun Oct 31 2010 02:12:30 GMT+0200 (CEST) in my timezone.
Currently I do the following to get the datetime object:
datetime.datetime.fromtimestamp(int(int(millis)/1E3))
and this to get the utcoffset in minutes:
-int(local_tnz.utcoffset(date).total_seconds()/60)
which, unfortunately, is wrong in many occasions :(.
Any ideas?
Note: I'm using python3.2.4, not that it should matter in this case.
EDIT:
Found the solution thanks to #JamesHolderness:
def datetimeFromMillis(millis):
return pytz.utc.localize(datetime.datetime.utcfromtimestamp(int(int(millis)/1E3)))
def getTimezoneOffset(date):
return -int(date.astimezone(local_tz).utcoffset().total_seconds()/60)
With local_tz equal to tzlocal.get_localzone() from the tzlocal module.
According to Wikipedia, the transition to and from Summer Time occurs at 01:00 UTC.
At 00:12 UTC you are still in Central European Summer Time (i.e. UTC+02:00), so the local time is 02:12.
At 01:12 UTC you are back in the standard Central European Time (i.e. UTC+01:00), so the local time is again 02:12.
When changing from Summer Time back to standard time, the local time goes from 02:59 back to 02:00 and the hour repeats itself. So when asking for the UTC offset of 02:12 (local time), the answer could truthfully be either +01:00 or +02:00 - it depends which version of 02:12 you are talking about.
On further investigation of the pytz library, I think your problem may be that you shouldn't be using the pytz.reference implementation, which may not deal with these ambiguities very well. Quoting from the comments in the source code:
Reference tzinfo implementations from the Python docs.
Used for testing against as they are only correct for the years
1987 to 2006. Do not use these for real code.
Working with ambiguous times in pytz
What you should be doing is constructing a timezone object for the appropriate timezone:
import pytz
cet = pytz.timezone('CET')
Then you can use the utcoffset method to calculate the UTC offset of a date/time in that timezone.
dt = datetime.datetime(2010, 10, 31, 2, 12, 30)
offset = cet.utcoffset(dt)
Note, that the above example will throw an AmbiguousTimeError exception, because it can't tell which of the two versions of 02:12:30 you meant. Fortunately pytz will let you specify whether you want the dst version or the standard version by setting the is_dst parameter. For example:
offset = cet.utcoffset(dt, is_dst = True)
Note that it doesn't harm to set this parameter on all calls to utcoffset, even if the time wouldn't be ambiguous. According to the documentation, it is only used during DST transition ambiguous periods to resolve that ambiguity.
How to deal with timestamps
As for dealing with timestamps, it's best you store them as UTC values for as long as possible, otherwise you potentially end up throwing away valuable information. So first convert to a UTC datetime with the datetime.utcfromtimestamp method.
dt = datetime.datetime.utcfromtimestamp(1288483950)
Then use pytz to localize the time as UTC, so the timezone is attached to the datetime object.
dt = pytz.utc.localize(dt)
Finally you can convert that UTC datetime into your local timezone, and obtain the timezone offset like this:
offset = dt.astimezone(cet).utcoffset()
Note that this set of calculations will produce the correct offsets for both 1288483950 and 1288487550, even though both timestamps are represented by 02:12:30 in the CET timezone.
Determining the local timezone
If you need to use the local timezone of your computer rather than a fixed timezone, you can't do that from pytz directly. You also can't just construct a pytz.timezone object using the timezone name from time.tzname, because the names won't always be recognised by pytz.
The solution is to use the tzlocal module - its sole purpose is to provide this missing functionality in pytz. You use it like this:
import tzlocal
local_tz = tzlocal.get_localzone()
The get_localzone() function returns a pytz.timezone object, so you should be able to use that value in all the places I've used the cet variable in the examples above.
Given a timestamp in milliseconds you can get the utc offset for the local timezone using only stdlib:
#!/usr/bin/env python
from datetime import datetime
millis = 1288483950000
ts = millis * 1e-3
# local time == (utc time + utc offset)
utc_offset = datetime.fromtimestamp(ts) - datetime.utcfromtimestamp(ts)
If we ignore time around leap seconds then there is no ambiguity or non-existent times.
It supports DST and changes of the utc offset for other reasons if OS maintains a historical timezone db e.g., it should work on Ubuntu for any past/present date but might break on Windows for past dates that used different utc offset.
Here's the same using tzlocal module that should work on *nix and Win32 systems:
#!/usr/bin/env python
from datetime import datetime
from tzlocal import get_localzone # pip install tzlocal
millis = 1288483950000
ts = millis * 1e-3
local_dt = datetime.fromtimestamp(ts, get_localzone())
utc_offset = local_dt.utcoffset()
See How to convert a python utc datetime to a local datetime using only python standard library?
To get the utc offset in minutes (Python 3.2+):
from datetime import timedelta
minutes = utc_offset / timedelta(minutes=1)
Don't use pytz.reference.LocalTimezone(), it is only for tests.
import pytz, datetime
tz = timezone('CET')
tz.utcoffset(datetime.datetime.now()).total_seconds()
7200.0
I have to deal in python with strings representing iso8601 timestamps.
My timestamps string are therefore in the following form:
timestamp = "2011-08-18T10:29:47+03:00"
Currently I'm converting them in python using:
timestamp = timestamp[:-6]
timestamp = datetime.datetime.strptime(timestamp, "%Y-%m-%dT%H:%M:%S")
But in this way I lose all the information about the time zone.
I have seen many examples here on s-o about timestamps and python, unfortunately no one was preserving the timezone as well, or just recover the time zone delay using:
delay = timestamp[-6:]
I have also tried:
timestamp = "2011-08-18T10:29:47+03:00"
timestamp = datetime.datetime.strptime(timestamp, "%Y-%m-%dT%H:%M:%S%z")
but it returned
ValueError: 'z' is a bad directive in format '%Y-%m-%dT%H:%M:%S%z'
Can you give some insight?
The python iso8601 module is built with a wonderful parse_date method that can handle timezone info :
>>> import iso8601
>>> iso8601.parse_date("2007-01-25T12:00:00Z")
datetime.datetime(2007, 1, 25, 12, 0, tzinfo=<iso8601.iso8601.Utc ...>)
>>> iso8601.parse_date("2011-08-18T10:29:47+03:00")
datetime.datetime(2011, 8, 18, 10, 29, 47, tzinfo=<FixedOffset '+03:00'>)
If you want to convert it in another timezone, use the astimezone(tz) method
If you need to get the UTC datetime you can use the utctimetuple() method.
You'll need to add an external module that provides timezone support; the pytz module provides you with the necessary timezone database.
You'll either need to parse the timezone by hand to construct a pytz timezone, or use a package like zc.iso8601 or iso8601 to do the parsing for you:
from zc.iso8601.parse import datetimetz
datetimetz(timestamp)
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?
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:
pytz documentation
What Every Developer Should Know About Time – development hints for many real-life use cases
The Problem with Time & Timezones - Computerphile – funny, eye-opening explanation about the complexity of working with timezones (video)
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.
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.
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'))
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.
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
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())
If you are using Django, you can set dates non-tz aware (only UTC).
Comment the following line in settings.py:
USE_TZ = True
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
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.
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.
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
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.
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.
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.
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)
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"))
Use the timezone as shown below for a timezone-aware date time. The default is UTC:
from django.utils import timezone
today = timezone.now()
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>)