Given a pair of str objects representing an ISO 8601 time and time zone:
time_str = '09:30'
time_zone_str = 'America/New_York'
How can these 2 strings be parsed into a time (not datetime) object?
Note: It's obviously possible to split the time_str by ':' and use the time constructor but then the parsing would be a little tricky to count the number of elements in the resulting list to know the resolution (minute, second, microsecond) of the str. This is because ISO 8601 allows for different representations:
time_str_short = '09:30'
time_str_long = '09:30:00'
Thank you in advance for your consideration and response.
The answer to "can I do this?" (with a timezone) is both yes and no. Firstly let's convert the string to a time object. As one commenter mentioned, you can do this in python 3.7 with the fromisoformat method:
from datetime import time
time.fromisoformat("09:30")
If you are not using 3.7, you can do this by creating a datetime and then converting to a time object:
from datetime import datetime, time
as_time = datetime.datetime.strptime("09:00", "%H:%M").time()
Now to deal with the timezone. As the timezone is a name, we can use the very convenient pytz module to convert it to a tzinfo object:
pytz.timezone('America/New_York')
At this point you're probably tempted to just pass it to the time constructor as the tzinfo argument, but unfortunately that does not work with pytz:
Unfortunately using the tzinfo argument of the standard datetime constructors ‘’does not work’’ with pytz for many timezones.
~ http://pytz.sourceforge.net/
So we will have to use the localize method of the newly created tzinfo object. But unfortunately we will still not be able to successfully localize the time object with this timezone. The reason for this is that pytz needs to know the date in order to determine if this timezone is in daylight savings time or not. As we have not provided the date, achieving this is quite impossible and you will get odd results like:
>>> pytz.timezone('America/New_York').localize(as_dt).isoformat()
'1900-01-01T09:00:00-04:56'
Note the -04:56 offset, which is gibberish. There are a few options for getting to what you ultimately want.
One option is to assume the time is a time today:
as_time = datetime.datetime.strptime("09:00", "%H:%M").time()
tz = pytz.timezone('America/New_York')
local_time = tz.localize(datetime.datetime.now().replace(hour=as_time.hour, minute=as_time.minute))
The other option is to use naive timezone offsets rather than timezone names:
from datetime import timezone, timedelta
naive_tz = timezone(timedelta(hours=5))
datetime.time(9, 30).replace(tz_info=naive_tz)
But I would not recommend this method as it's quite brittle and would require some intermediate steps to derive from the TZ location name that are non-trivial.
A timezone without a date is meaningless, so no, you can't use both to produce a time object. While the standard library time object does support having a tzinfo attribute, the 'timezone' object is not really a timezone, but merely a time offset.
A timezone is more than just an offset from UTC. Timezone offsets are date-dependent, and because such details as the Daylight Savings winter / summer time distinction is partly the result of political decisions, what dates the timezone offset changes is also dependent on the year.
To be explicit, America/New_York is a timezone, not a time offset. The exact offset from UTC depends on the date; it'll be minus 4 hours in summer, 5 hours in winter!
So for a timezone such as America/New_York, you need to pick a date too. If you don't care about the date, pick a fixed date so your offset is at least consistent. If you are converting a lot of time stamps, store the timezone offset once as a timedelta(), then use that timedelta to shift time() objects to the right offset.
To parse just a timestring, pretend there is a date attached by using the datetime.strptime() method, then extract the time object:
from datetime import datetime
try:
timeobject = datetime.strptime(time_str, '%H:%M').time()
except ValueError:
# input includes seconds, perhaps
timeobject = datetime.strptime(time_str, '%H:%M:%S').time()
To update the time given a timezone, get a timezone database that supports your timezone string first; the pytz library is regularly updated.
from pytz import timezone
timezone = pytz.timezone(time_zone_str)
How you use it depends on what you are trying to do. If the input time is not in UTC, you can simply attach the timezone to a datetime() object with the datetime.combine()method, after which you can move it to the UTC timezone:
dt_in_timezone = datetime.combine(datetime.now(), timeobject, timezone)
utc_timeobject = dt_in_timezone.astimezone(pytz.UTC).time()
This assumes that 'today' is good enough to determine the correct offset.
If your time is a UTC timestamp, combine it with the UTC timezone, then use the pytz timezone; effectively the reverse:
dt_in_utc = datetime.combine(datetime.now(), timeobject, pytz.UTC)
timeobject_in_timezone = dt_in_timezone.astimezone(timezone).time()
To store just the offset for bulk application, pass in a reference date to the timezone.utcoffset() method:
utc_offset = timezone.utcoffset(datetime.now())
after which you can add this to any datetime object as needed to move from UTC to local time, or subtract it to go from local to UTC. Note that I said datetime, as time objects also don't support timedelta arithmetic; a timedelta can be larger than the number of seconds left in the day or the number of seconds since midnight, after all, so adding or subtracting could shift days as well as the time:
# new time after shifting
(datetime.combine(datetime.now(), timeobject) + utc_offset).time()
For completion sake, you can't pass in a pytz timezone to a time object; it just doesn't have any effect on the time. The timezone object returns None for the UTC offset in that case, because it can't give any meaningful answer without a date:
>>> from datetime import time
>>> from pytz import timezone
>>> tz = timezone('America/New_York')
>>> time_with_zone = time(12, 34, tzinfo=tz)
>>> time_with_zone
datetime.time(12, 34, tzinfo=<DstTzInfo 'America/New_York' LMT-1 day, 19:04:00 STD>)
>>> time_with_zone.utcoffset()
>>> time_with_zone.utcoffset() is None
True
>>> tz.utcoffset(None) is None # what time_with_zone.utcoffset() does under the hood
None
So for all intents an purposes, time_with_zone is just another naive time object as the tzinfo object attached doesn't actually have any effect.
Moreover, because there is no date to determine the correct timezone information, pytz selects the earliest known 'New York' timezone, and that's not exactly a recent one; look closely at that tzinfo representation:
tzinfo=<DstTzInfo 'America/New_York' LMT-1 day, 19:04:00 STD>
^^^^^^^^^^^^^^^^^^^^^^^
That's the timezone introduced in 1883 by the railroads; the 'modern' EST timezone was not introduced until the 20th century. This is why timezone objects are usually passed in a date when determining the offset:
>>> tz.utcoffset(datetime(1883, 6, 28))
datetime.timedelta(-1, 68640)
>>> tz.utcoffset(datetime(1918, 6, 28))
datetime.timedelta(-1, 72000)
Hope it works for you,
import datetime
# Hello World program in Python
print "Hello World!\n"
time_str = '09:30'
time_zone_str = 'America/New_York'
s = "I am looking for a course in Paris!"
print(s)
print(datetime.datetime.strptime(time_str, '%H:%M').time())
print(datetime.time(3, 55))
Thanks
Related
I'm trying to create HTTP endpoints:
one that returns posts to a user that were created in a given month in the requestor's time zone.
another one that gets the months possible for post*.
Examples
(1) get posts in month of requestor's timezone
(2) get possible months for posts
For example if the user made posts in Sept-November but none in December then Jan onward it wouldn't return December.
But it takes the time zone in "PST" format, because it does a SQL query.
Problems
Unfortunately pytz, the library I'm using for getting all posts from a month, only accepts time zone in the format "US/Pacific".
Questions
What is the format or string representation "US/Pacific" called ?
How can I convert the string formats "PST", "UCT" or "CST" to their respective formats like "US/Pacific", etc. in Python ?
What's the name for this format like "US/Pacific" ?
Is there a sort of dictionary that maps "PST" to "US/Pacific" ?
Time zone terminology
How to define, represent or refer to a time zone? Some terms:
UTC time offset (e.g. "UTC-08:00")
in relation with ISO 8601 date/time format: time zone designator (e.g. "Z" or "-08")
time zone: canonical name
time zone: abbreviation
(Canonical) names
The spelled-out time zone names or (tz names) like "US/Pacific" or "Europe/Paris" are also called canonical names of time zone. They are used as key in the IANA time zone database. In RFC 6557 they are referred to as "time zone names". Wikipedia claims about them:
The primary, preferred zone name.
See also:
ECMA: 6.4Time Zone Names
Abbreviations
The alphabetic string literals like "UTC", "PST" are abbreviations of time zone.
Conversion between time zones
Usually the conversion between time zones is done by modifying the offsets of UTC which are represented in ISO 8601, time zone designators like "-0800" (PST) which is 8 hours subtracted from "+0000" (UTC).
See also:
Daylight saving time and time zone best practices
Converting using pytz timezone
To convert a given date-time from UTC to the target time zone (e.g. "US/Pacific") use astimezone(tz) on the source date-time instance:
import datetime
from pytz import timezone, utc
utc_time = datetime.datetime.utcnow()
pst_tz = timezone('US/Pacific')
pst_time = utc_time.replace(tzinfo=utc).astimezone(pst_tz)
Note:
the time-zone tz is built using pytz's tzinfo API, e.g. with timezone('PST8PDT') for PST or timezone('US/Central') for CST
the .replace() is optional and resets the time zone of given date-time to default UTC.
Surprisingly: The "PST" abbreviation is not found in pytz.all_timezones. Most similar are (evaluated in REPL):
>>> import pytz
>>> pytz.timezone('PST8PDT')
<DstTzInfo 'PST8PDT' PST-1 day, 16:00:00 STD>
>>> pytz.timezone('US/Pacific')
<DstTzInfo 'US/Pacific' LMT-1 day, 16:07:00 STD>
>>> pytz.timezone('US/Central')
<DstTzInfo 'US/Central' LMT-1 day, 18:09:00 STD>
See also:
pytz - Converting UTC and timezone to local time
Is there a list of Pytz Timezones?
Converting using zoneinfo (since 3.9)
Adjusted from MrFuppes answer to "How do I use timezones with a datetime object in python?":
from datetime import datetime, timezone
from zoneinfo import ZoneInfo
utc_time = datetime(2012,11,10,9,0,0, tzinfo=timezone.utc)
cst_tz = ZoneInfo("US/Central")
cst_time = utc_time.astimezone(cst_tz)
# safely use `replace` to get the same wall time in a different tz:
pst_time = cst_time.replace(tzinfo=ZoneInfo("US/Pacific"))
print(utc_time.isoformat())
print(cst_time.isoformat())
print(pst_time.isoformat())
(above code is not tested!)
See also:
new Python module zoneinfo — IANA time zone support
Paul Ganssle (2021): Stop using utcnow and utcfromtimestamp, blog article from the maintainer of python-dateutil
import pytz
from datetime import datetime # timezone
print('The supported tz:', pytz.all_timezones, '\n')
# Show date-time for different timezone/country
# current date and time of NY
datetime_NY = datetime.now(pytz.timezone('America/New_York'))
print("NY:", datetime_NY.strftime("%m/%d/%Y, %H:%M:%S"))
# NY: 07/28/2021, 05:49:41
# Timezone Conversion
# Any timezone to UTC
NY_to_utc = datetime_NY.astimezone(pytz.utc)
print("NY_to_utc: ", NY_to_utc.strftime("%m/%d/%Y, %H:%M:%S"))
# NY_to_utc: 07/28/2021, 09:49:41
Naive and Aware datetime Refer this article for dealing with timezone
Show date-time for different timezone/country
Timezone Conversion
Timezone unaware/naive to Timezone aware
Issue with replace
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'))
I have a datetime in utc time zone, for example:
utc_time = datetime.datetime.utcnow()
And a pytz timezone object:
tz = timezone('America/St_Johns')
What is the proper way to convert utc_time to the given timezone?
I think I got it:
pytz.utc.localize(utc_time, is_dst=None).astimezone(tz)
This line first converts the naive (time zone unaware) utc_time datetime object to a datetime object that contains a timezone (UTC). Then it uses the astimezone function to adjust the time according to the requested time zone.
It's the exact purpose of fromutc function:
tz.fromutc(utc_time)
(astimezone function calls fromutc under the hood, but tries to convert to UTC first, which is unneeded in your case)
I agree with Tzach's answer. Just wanted to include that the is_dst parameter is not required:
pytz.utc.localize(datetime.utcnow()).astimezone(tz)
That code converts the current UTC time to a timezone aware current datetime.
Whereas the code below converts the current UTC time to a timezone aware datetime which is not necessarily current. The timezone is just appended into the UTC time value.
tz.localize(datetime.utcnow())
May I recommend to use arrow? If I understood the question:
>>> import arrow
>>> utc = arrow.utcnow()
>>> utc
<Arrow [2014-08-12T13:01:28.071624+00:00]>
>>> local = utc.to("America/St_Johns")
>>> local
<Arrow [2014-08-12T10:31:28.071624-02:30]>
You can also use
tz.fromutc(utc_time)
Another very easy way:
Because utcnow method returns a naive object, so you have to convert the naive object into aware object. Using replace method you can convert a naive object into aware object. Then you can use the astimezone method to create new datetime object in a different time zone.
from datetime import datetime
import pytz
utc_time = datetime.utcnow()
tz = pytz.timezone('America/St_Johns')
utc_time =utc_time.replace(tzinfo=pytz.UTC) #replace method
st_john_time=utc_time.astimezone(tz) #astimezone method
print(st_john_time)
You can also use the sample below, I use it for similar task
tz = pytz.timezone('America/St_Johns')
time_difference=tz.utcoffset(utc_time).total_seconds() #time difference between UTC and local timezones in 5:30:00 format
utc_time = date + timedelta(0,time_difference)
It works fast and you don't need to import additional libraries.
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
This question already has answers here:
How to make a timezone aware datetime object
(15 answers)
Closed 6 years ago.
I've got a datetime which has no timezone information. I'm now getting the timezone info and would like to add the timezone into the existed datetime instance, how can I do?
d = datetime.datetime.now()
tz = pytz.timezone('Asia/Taipei')
How to add the timezone info tz into datetime a
Use tz.localize(d) to localize the instance. From the documentation:
The first is to use the localize() method provided by the pytz library. This is used to localize a naive datetime (datetime with no timezone information):
>>> loc_dt = eastern.localize(datetime(2002, 10, 27, 6, 0, 0))
>>> print(loc_dt.strftime(fmt))
2002-10-27 06:00:00 EST-0500
If you don't use tz.localize(), but use datetime.replace(), chances are that a historical offset is used instead; tz.localize() will pick the right offset in effect for the given date. The US Eastern timezone DST start and end dates have changed over time, for example.
When you try to localize a datetime value that is ambiguous because it straddles the transition period from summer to winter time or vice-versa, the timezone will be consulted to see if the resulting datetime object should have .dst() return True or False. You can override the default for the timezone with the is_dst keyword argument for .localize():
dt = tz.localize(naive, is_dst=True)
or even switch off the choice altogether by setting is_dst=None. In that case, or in the rare cases there is no default set for a timezone, an ambiguous datetime value would lead to a AmbiguousTimeError exception being raised. The is_dst flag is only consulted for datetime values that are ambiguous and is ignored otherwise.
To go back the other way, turn a timezone-aware object back to a naive object, use .replace(tzinfo=None):
naivedt = awaredt.replace(tzinfo=None)
If you know that your original datetime was "measured" in the time zone you are trying to add to it, you could (but probably shouldn't) use replace rather than localize.
# d = datetime.datetime.now()
# tz = pytz.timezone('Asia/Taipei')
d = d.replace(tzinfo=tz)
I can imagine 2 times when this might make sense (the second one happened to me):
Your server locale is set to the incorrect time zone and you are trying to correct a datetime instance by making it aware of this incorrect timezone (and presumably later localizing it to the "correct" time zone so the values of now() match up to other times you are comparing it to (your watch, perhaps)
You want to "tag" a time instance (NOT a datetime) with a time zone (tzinfo) attribute so that attribute can be used later to form a full datetime instance.