Convert "PST" to "US/Pacific" for use with Pytz - python

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

Related

How can I convey text descriptions of location time zones to UTC based in Python?

I need to be able to convert a time zone stored as a string that is region based either to a UTC time zone or a common time zone across locations. For example, “Canada/Vancouver” and “Americas/Los_Angeles” should both resolve to “US/Pacific”. This solution should also work for other time zones like “Canada/Toronto” and “AmericA/New_York” to “US/Eastern”, also extending to time zones for other locations like Mexico, etc.
I have no idea how to do this or even think about this. I could convert it to a UTC-7 but that doesn’t handle PST vs PDT shifts.
Can someone help?
Edit: after reading the comments and answer I realized that my question wasn’t clear enough.
I have a set of phone numbers, and I use the “phonenumbers” package to get the time zone out in the newer format for each number, but I want to count the number of unique phone numbers by the old region time zone naming convention. Hence I want to convert to newer “Continent/City” time zones to “Country/Region” time zones. . The UTC was just me trying to think of a way to convert the region/city formats into a common name.
time zones as from the IANA database refer to regions in a geographical sense. UTC on the other hand is not a time zone, it is universal (not specific to a region).
For a time zone, you can have an offset from UTC (like UTC-8 for 8 hours behind UTC).
A certain date/time in a given time zone has a specific UTC offset, as derived from the rules for that time zone (when to apply DST etc.).
The other way around, a certain UTC offset can apply in multiple time zones at given date/time, so mapping back needs a definition, otherwise it's ambiguous.
Regarding the naming of time zones, "Continent/City"-style time zone names are preferred. Old names like "US/Pacific" (as from before 1993) are kept in the database for backwards-compatibility - see also eggert-tz/backward.
Python >= 3.9 supports IANA time zones with the standard library via the zoneinfo module. Using that, you can create aware datetime objects easily and get their UTC offset, e.g. like
from datetime import datetime
from zoneinfo import ZoneInfo
tznames = ["America/Vancouver", "America/Los_Angeles",
"America/Toronto", "America/New_York", "Europe/Berlin"]
def timedelta_to_str(td):
hours, seconds = divmod(td.total_seconds(), 3600)
return f"{int(hours):+}:{int(seconds/60):02d}"
now = datetime.now().replace(microsecond=0)
for z in tznames:
local_now = now.astimezone(ZoneInfo(z))
print(f"now in zone {z}:\n\t{local_now.isoformat(' ', timespec='seconds')}, "
f"UTC offset: {timedelta_to_str(local_now.utcoffset())} hours\n")
# or also e.g. print(f"local time {z}:\n\t{local_now}, UTC offset: {local_now.strftime('%z')}\n")
# now in zone America/Vancouver:
# 2022-01-12 06:30:08-08:00, UTC offset: -08:00 hours
# now in zone America/Los_Angeles:
# 2022-01-12 06:30:08-08:00, UTC offset: -08:00 hours
# now in zone America/Toronto:
# 2022-01-12 09:30:08-05:00, UTC offset: -05:00 hours
# now in zone America/New_York:
# 2022-01-12 09:30:08-05:00, UTC offset: -05:00 hours
# now in zone Europe/Berlin:
# 2022-01-12 15:30:08+01:00, UTC offset: +01:00 hours
see also on SO:
Python: datetime tzinfo time zone names documentation
Display the time in a different time zone
Format timedelta to string

str to time object in python 3

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

Python: How to convert unixtimestamp and timezone into datetime object?

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'))

How do you convert local time (PDT) to PST using python

Usually what I do if I want to convert time zone is the following
# local interpreted as pst to utc
utc = pytz.utc
pst = pytz.timezone('America/Los_Angeles')
start_time_str = time.strftime('%Y-%m-%d %H:%M:%S', \
time.localtime(start_time))
start_time_datetime = \
datetime.datetime.strptime(start_time_str, "%Y-%m-%d %H:%M:%S")
start_time_datetime = \
start_time_datetime.replace(tzinfo=pst).astimezone(utc)
Now I want to do similar stuff such that I want localtime convert to pst
localtime = datetime.datetime.fromtimestamp(time.mktime(
time.localtime()))
I am not exactly sure how would you achieve this
Any help would be appreciated
Two things:
The "America/Los_Angeles" identifier represents the entire Pacific time zone, including both PST and PDT. It will use the either -8 or -7 as the time zone offset depending on the specific date and time it is used on.
Calling replace(tzinfo=...) is a mistake. Use localize instead. See the pytz documentation. This is discussed in the introduction and in the very first example.
Your code contains unnecessary (calling strptime after strftime) or just wrong (.replace()) conversions.
To create an aware datetime object given "seconds since epoch" start_time (a value returned by time.time()):
#!/usr/bin/env python
from datetime import datetime
import pytz # $ pip install pytz
tz = pytz.timezone('America/Los_Angeles')
start_time_datetime = datetime.fromtimestamp(start_time, tz)
As #Matt Johnson mentions, 'America/Los_Angeles' timezone id produces time with PST or PDT tzname depending on the date. No conversion is necessary.
The last code example in your question has both unnecessary conversions and may fail in some cases. If the intent is to get the current time in the local timezone as an aware datetime object then you could use tzlocal module:
#!/usr/bin/env python
from datetime import datetime
from tzlocal import get_localzone # $ pip install tzlocal
local_timezone = get_localzone() # pytz timezone corresponding to the local time
current_localtime = datetime.now(local_timezone)
First, as a note, local time is either Standard Time or Daylight Savings Time -- it cannot be both simultaneously. I believe what you are really getting at is asking whether there is a way to tell if a time is either a Daylight Savings Time or not. In other words, given a specific day, is a particular time in Daylight Savings Time? Another common use case is converting tabular data pegged at a specific time offset to the current correct time.
Okay, I can think of three ways to do this.
(1) You can convert the datetime into a time object and look at the .tm_isdst property, similar to this answer. You can do a test on it to see if that property equals one and then subtract a timedelta of one hour.
>>> time.localtime()
time.struct_time(tm_year=2019, tm_mon=11, tm_mday=16, tm_hour=12,
tm_min=36, tm_sec=32, tm_wday=5, tm_yday=320, tm_isdst=0)
>>> time.localtime().tm_isdst
0
(2) Second way is to crawl month by month over noon on the fifteenth of each month and check if the offset varies. (Note that some areas do not implement DST.) The greater value indicates a skip ahead for Daylight Savings Time. (For instance, PDT is UTC -7 while PST is UTC -8.) From those values you can calculate the time difference. Then you can test your time by checking the offset to see if it is in Standard Time or Daylight Savings Time. Offest is checked this way:
>>> my_special_pdt_datetime.strftime('%z')
'-0700'
(3) And, maybe the easiest, is to calculate the existing offset by getting offset = datetime.datetime.utcnow() - datetime.datetime.now() and then comparing that to the known offset from pytz:
>>> pytz.timezone('US/Pacific').localize(datetime.datetime(2019,1,1)).strftime('%z')
'-0800'

Python / Mongoengine - Timezone missing when saved to database?

I'm having some trouble saving a date object to Mongo using MongoEngine. Here is my code:
print isodate
>>> 2014-07-01T20:00:00.000Z
import pytz
from dateutil import parser
tz = pytz.timezone('Europe/London')
start = parser.parse(isodate).replace(tzinfo=None)
start = tz.localize(start)
print start
>>> 2014-07-01 20:00:00+01:00
Localizing the date seems to work fine, but when saving to Mongo:
f = Fixture(
start=start
)
The following strangeness is happening when I look at the Mongo document created:
{
_id: ObjectId("53b1dfbde20b47102c824a8f"),
start: ISODate("2014-07-01T19:00:00Z")
}
Is there any reason why the time is off by two hours, and the timezone is no longer present?
I feel you misunderstood date time format. Refer to W3C Date and Time Formats:
Times are expressed in UTC (Coordinated Universal Time), with a special UTC designator ("Z").
Times are expressed in local time, together with a time zone offset in hours and minutes. A time zone offset of "+hh:mm" indicates that the date/time uses a local time zone which is "hh" hours and "mm" minutes ahead of UTC. A time zone offset of "-hh:mm" indicates that the date/time uses a local time zone which is "hh" hours and "mm" minutes behind UTC.
"2014-07-01T20:00:00.000Z" should equal to "2014-07-01 21:00:00+01:00". So it went wrong in localizing datetime, not in saving to Mongo.
If you want to convert "....T....Z" to local time, you can try this:
print isodate
>>> 2014-07-01T20:00:00.000Z
import pytz
from dateutil import parser
local_tz = pytz.timezone('Europe/London')
local_time = parser.parse(isodate).astimezone(local_tz)
print local_time
>>> 2014-07-01 21:00:00+01:00
If you need to perform date arithmetic on local times, do one more step (refer: pytz doc):
local_tz.normalize(local_time)
Actually you can directly save "....T....Z" ISODate into Mongo without converting to local time. Since it already contains timezone info, converting is unnecessary.

Categories