Get the Olson TZ name for the local timezone? - python

How do I get the Olson timezone name (such as Australia/Sydney) corresponding to the value given by C's localtime call?
This is the value overridden via TZ, by symlinking /etc/localtime, or setting a TIMEZONE variable in time-related system configuration files.

I think best bet is to go thru all pytz timezones and check which one matches local timezone, each pytz timezone object contains info about utcoffset and tzname like CDT, EST, same info about local time can be obtained from time.timezone/altzone and time.tzname, and I think that is enough to correctly match local timezone in pytz database e.g.
import time
import pytz
import datetime
local_names = []
if time.daylight:
local_offset = time.altzone
localtz = time.tzname[1]
else:
local_offset = time.timezone
localtz = time.tzname[0]
local_offset = datetime.timedelta(seconds=-local_offset)
for name in pytz.all_timezones:
timezone = pytz.timezone(name)
if not hasattr(timezone, '_tzinfos'):
continue#skip, if some timezone doesn't have info
# go thru tzinfo and see if short name like EDT and offset matches
for (utcoffset, daylight, tzname), _ in timezone._tzinfos.iteritems():
if utcoffset == local_offset and tzname == localtz:
local_names.append(name)
print local_names
output:
['America/Atikokan', 'America/Bahia_Banderas',
'America/Bahia_Banderas', 'America/Belize', 'America/Cambridge_Bay',
'America/Cancun', 'America/Chicago', 'America/Chihuahua',
'America/Coral_Harbour', 'America/Costa_Rica', 'America/El_Salvador',
'America/Fort_Wayne', 'America/Guatemala',
'America/Indiana/Indianapolis', 'America/Indiana/Knox',
'America/Indiana/Marengo', 'America/Indiana/Marengo',
'America/Indiana/Petersburg', 'America/Indiana/Tell_City',
'America/Indiana/Vevay', 'America/Indiana/Vincennes',
'America/Indiana/Winamac', 'America/Indianapolis', 'America/Iqaluit',
'America/Kentucky/Louisville', 'America/Kentucky/Louisville',
'America/Kentucky/Monticello', 'America/Knox_IN',
'America/Louisville', 'America/Louisville', 'America/Managua',
'America/Matamoros', 'America/Menominee', 'America/Merida',
'America/Mexico_City', 'America/Monterrey',
'America/North_Dakota/Beulah', 'America/North_Dakota/Center',
'America/North_Dakota/New_Salem', 'America/Ojinaga',
'America/Pangnirtung', 'America/Rainy_River', 'America/Rankin_Inlet',
'America/Resolute', 'America/Resolute', 'America/Tegucigalpa',
'America/Winnipeg', 'CST6CDT', 'Canada/Central', 'Mexico/General',
'US/Central', 'US/East-Indiana', 'US/Indiana-Starke']
In production you can create such a mapping beforehand and save it instead of iterating always.
Testing script after changing timezone:
$ export TZ='Australia/Sydney'
$ python get_tz_names.py
['Antarctica/Macquarie', 'Australia/ACT', 'Australia/Brisbane',
'Australia/Canberra', 'Australia/Currie', 'Australia/Hobart',
'Australia/Lindeman', 'Australia/Melbourne', 'Australia/NSW',
'Australia/Queensland', 'Australia/Sydney', 'Australia/Tasmania',
'Australia/Victoria']

This is kind of cheating, I know, but getting from '/etc/localtime' doesn't work for you?
Like following:
>>> import os
>>> '/'.join(os.readlink('/etc/localtime').split('/')[-2:])
'Australia/Sydney'
Hope it helps.
Edit: I liked #A.H.'s idea, in case '/etc/localtime' isn't a symlink. Translating that into Python:
#!/usr/bin/env python
from hashlib import sha224
import os
def get_current_olsonname():
tzfile = open('/etc/localtime')
tzfile_digest = sha224(tzfile.read()).hexdigest()
tzfile.close()
for root, dirs, filenames in os.walk("/usr/share/zoneinfo/"):
for filename in filenames:
fullname = os.path.join(root, filename)
f = open(fullname)
digest = sha224(f.read()).hexdigest()
if digest == tzfile_digest:
return '/'.join((fullname.split('/'))[-2:])
f.close()
return None
if __name__ == '__main__':
print get_current_olsonname()

One problem is that there are multiple "pretty names" , like "Australia/Sydney" , which point to the same time zone (e.g. CST).
So you will need to get all the possible names for the local time zone, and then select the name you like.
e.g.: for Australia, there are 5 time zones, but way more time zone identifiers:
"Australia/Lord_Howe", "Australia/Hobart", "Australia/Currie",
"Australia/Melbourne", "Australia/Sydney", "Australia/Broken_Hill",
"Australia/Brisbane", "Australia/Lindeman", "Australia/Adelaide",
"Australia/Darwin", "Australia/Perth", "Australia/Eucla"
you should check if there is a library which wraps TZinfo , to handle the time zone API.
e.g.: for Python, check the pytz library:
http://pytz.sourceforge.net/
and
http://pypi.python.org/pypi/pytz/
in Python you can do:
from pytz import timezone
import pytz
In [56]: pytz.country_timezones('AU')
Out[56]:
[u'Australia/Lord_Howe',
u'Australia/Hobart',
u'Australia/Currie',
u'Australia/Melbourne',
u'Australia/Sydney',
u'Australia/Broken_Hill',
u'Australia/Brisbane',
u'Australia/Lindeman',
u'Australia/Adelaide',
u'Australia/Darwin',
u'Australia/Perth',
u'Australia/Eucla']
but the API for Python seems to be pretty limited, e.g. it doesn't seem to have a call like Ruby's all_linked_zone_names -- which can find all the synonym names for a given time zone.

If evaluating /etc/localtime is OK for you, the following trick might work - after translating it to python:
> md5sum /etc/localtime
abcdefabcdefabcdefabcdefabcdefab /etc/localtime
> find /usr/share/zoneinfo -type f |xargs md5sum | grep abcdefabcdefabcdefabcdefabcdefab
abcdefabcdefabcdefabcdefabcdefab /usr/share/zoneinfo/Europe/London
abcdefabcdefabcdefabcdefabcdefab /usr/share/zoneinfo/posix/Europe/London
...
The duplicates could be filtered using only the official region names "Europe", "America" ... If there are still duplicates, you could take the shortest name :-)

Install pytz
import pytz
import time
#import locale
import urllib2
yourOlsonTZ = None
#yourCountryCode = locale.getdefaultlocale()[0].split('_')[1]
yourCountryCode = urllib2.urlopen('http://api.hostip.info/country.php').read()
for olsonTZ in [pytz.timezone(olsonTZ) for olsonTZ in pytz.all_timezones]:
if (olsonTZ._tzname in time.tzname) and (str(olsonTZ) in pytz.country_timezones[yourCountryCode]):
yourOlsonTZ = olsonTZ
break
print yourOlsonTZ
This code will take a best-guess crack at your Olson Timezone based both on your Timezone Name (as according to Python's time module), and your Country Code (as according to Python's locale module the hostip.info project, which references your IP address and geolocates you accordingly).
For example, simply matching the Timzone Names could result in America/Moncton, America/Montreal, or America/New_York for EST (GMT-5). If your country is the US, however, it will limit the answer to America/New_York.
However, if your country is Canada, the script will simply default to the topmost Canadian result (America/Moncton). If there is a way to further refine this, please feel free to leave suggestions in comments.

The tzlocal module for Python is aimed at exactly this problem. It produces consistent results under both Linux and Windows, properly converting from Windows time zone ids to Olson using the CLDR mappings.

This will get you the time zone name, according to what's in the TZ variable, or localtime file if unset:
#! /usr/bin/env python
import time
time.tzset
print time.tzname

Here's another posibility, using PyICU instead; which is working for my purposes:
>>> from PyICU import ICUtzinfo
>>> from datetime import datetime
>>> datetime(2012, 1, 1, 12, 30, 18).replace(tzinfo=ICUtzinfo.getDefault()).isoformat()
'2012-01-01T12:30:18-05:00'
>>> datetime(2012, 6, 1, 12, 30, 18).replace(tzinfo=ICUtzinfo.getDefault()).isoformat()
'2012-06-01T12:30:18-04:00'
Here it is interpreting niave datetimes (as would be returned by a database query) in the local timezone.

I prefer following a slightly better than poking around _xxx values
import time, pytz, os
cur_name=time.tzname
cur_TZ=os.environ.get("TZ")
def is_current(name):
os.environ["TZ"]=name
time.tzset()
return time.tzname==cur_name
print "Possible choices:", filter(is_current, pytz.all_timezones)
# optional tz restore
if cur_TZ is None: del os.environ["TZ"]
else: os.environ["TZ"]=cur_TZ
time.tzset()

I changed tcurvelo's script to find the right form of time zone (Continent/..../City), in most of cases, but return all of them if fails
#!/usr/bin/env python
from hashlib import sha224
import os
from os import listdir
from os.path import join, isfile, isdir
infoDir = '/usr/share/zoneinfo/'
def get_current_olsonname():
result = []
tzfile_digest = sha224(open('/etc/localtime').read()).hexdigest()
test_match = lambda filepath: sha224(open(filepath).read()).hexdigest() == tzfile_digest
def walk_over(dirpath):
for root, dirs, filenames in os.walk(dirpath):
for fname in filenames:
fpath = join(root, fname)
if test_match(fpath):
result.append(tuple(root.split('/')[4:]+[fname]))
for dname in listdir(infoDir):
if dname in ('posix', 'right', 'SystemV', 'Etc'):
continue
dpath = join(infoDir, dname)
if not isdir(dpath):
continue
walk_over(dpath)
if not result:
walk_over(join(infoDir))
return result
if __name__ == '__main__':
print get_current_olsonname()

This JavaScript project attempts to solve the same issue in the browser client-side. It works by playing "twenty questions" with the locale, asking for the UTC offset of certain past times (to test for summer time boundaries, etc.) and using those results to deduce what the local time zone must be. I am not aware of any equivalent Python package unfortunately, so if someone wanted to use this solution it would have to be ported to Python.
While this formula will require updating every time (at worst) the TZ database is updated, a combination of this algorithm and the solution proposed by Anurag Uniyal (keeping only possibilities returned by both methods) sounds to me like the surest way to compute the effective local timezone. As long as there is some difference between the UTC offset of at least one local time in any two time zones, such a system can correctly choose between them.

Related

localize date time in python

Overview I receive the timestamp from server_x, my application is on server_y, both are in different regions, my application calls server_x api and receives json which has timestamp, now i need to perform some calculation on server_y, for that i need to make sure that the timestamp i receive from server_x could be used to covert the local datetime of server_y , so both are in sync
I want to convert datetime.now() to the timezone I receive from server for e.g., UTC-07:00
Current solution, I pass server_timestamp to the function and then I pass its zone info to the datetime.now
Server_timestamp = "2020-04-04T10:24:49.000-0700"
dt = datetime.strptime(Server_timestamp, "%Y-%m-%dT%H:%M:%S.%f%z")
convert_local = datetime.now(dt.tzinfo)
Problem:
I need to save the timezone of the server in db and then use that instead of passing server_timestamp everytime, the tzinfo gives a type datetime.timezone = UTC-07:00, after storing this string how can I use it to change the localtime.
Here's a function that utilizes the datetime library to convert a datetime object from one timezone to another:
from datetime import datetime
import pytz
def convert_tz(dt, current_tz, out_tz):
return dt.replace(tzinfo=current_tz).astimezone(tz=out_tz)
now = datetime.now(tz=pytz.timezone('US/Eastern'))
convert = datetime.now().astimezone().tzinfo
print(now)
print(utc_to_local(now, now.tzinfo, convert))
Output:
2020-05-10 17:02:44.245703-04:00
2020-05-10 16:02:44.245703-05:00
I used the pytz library for demonstration purposes. For you, to get the server's timezone, use the line datetime.now().astimezone().tzinfo.
I implemented a solution.
I now save the last 5 char of the timestamp in the db "-0700".
time_zone = query_from_db
tz = datetime.strptime(time_zone, "%z")
datetime_now = datetime.now(tz.tzinfo)

Dealing with timezone dates in MongoDB and pymongo

I do not seem to be able to query records and get what I expect. For example I am searching
today = datetime.datetime.today()
past = today + timedelta(days=-200)
results = mongo.stuff.find({"date_added": {"gt": past}}, {"id":1})
I have the following date specified in MongoDB:
"date_added": {
"$date": "2016-04-19T18:47:54.101Z"
},
But I get no results! Is this down to the timezone which appears in the MongoDB date which is screwing things up.
This is just a typing error:
Try with the following code:
results = mongo.stuff.find({"date_added": {"$gt": past}}, {"id":1})
You forgot about the $ sign in $gt.
Use an aware datetime object (with timezone info).
# E.g. with UTC timezone :
import pytz
import datetime
today = datetime.datetime.today()
past = today + timedelta(days=-200)
pytc.utc.localize(past)
results = mongo.stuff.find({"date_added": {"gt": past}}, {"id":1})
To use a different timezone to localize, try something like pytz.timezone('US/Mountain')
P.S. you'll need pip install pytz

How can I specify the committed_date of a Commit using GitPython?

I would like to commit a file with a custom date.
So far I've created a Commit object, but I don't understand how to bind it to a repo.
from git import *
repo = Repo('path/to/repo')
comm = Commit(repo=repo, binsha=repo.head.commit.binsha, tree=repo.index.write_tree(), message='Test Message', committed_date=1357020000)
Thanks!
Well I found the solution.
I wasn't aware, but I had to provide all the parameters otherwise the Commit object would throw a BadObject exception when trying to serialize it.
from git import *
from time import (time, altzone)
import datetime
from cStringIO import StringIO
from gitdb import IStream
repo = Repo('path/to/repo')
message = 'Commit message'
tree = repo.index.write_tree()
parents = [ repo.head.commit ]
# Committer and Author
cr = repo.config_reader()
committer = Actor.committer(cr)
author = Actor.author(cr)
# Custom Date
time = int(datetime.date(2013, 1, 1).strftime('%s'))
offset = altzone
author_time, author_offset = time, offset
committer_time, committer_offset = time, offset
# UTF-8 Default
conf_encoding = 'UTF-8'
comm = Commit(repo, Commit.NULL_BIN_SHA, tree,
author, author_time, author_offset,
committer, committer_time, committer_offset,
message, parents, conf_encoding)
After creating the commit object, a proper SHA had to be placed, I didn't have a clue how this was done, but a little bit of research in the guts of GitPython source code got me the answer.
stream = StringIO()
new_commit._serialize(stream)
streamlen = stream.tell()
stream.seek(0)
istream = repo.odb.store(IStream(Commit.type, streamlen, stream))
new_commit.binsha = istream.binsha
Then set the commit as the HEAD commit
repo.head.set_commit(new_commit, logmsg="commit: %s" % message)
A simpler solution, which avoids having to manually create commit objects (And also manually create new indexes, if you wanted to add files too):
from git import Repo, Actor # GitPython
import git.exc as GitExceptions # GitPython
import os # Python Core
# Example values
respository_directory = "."
new_file_path = "MyNewFile"
action_date = str(datetime.date(2013, 1, 1))
message = "I am a commit log! See commit log run. Run! Commit log! Run!"
actor = Actor("Bob", "Bob#McTesterson.dev" )
# Open repository
try:
repo = Repo(respository_directory)
except GitExceptions.InvalidGitRepositoryError:
print "Error: %s isn't a git repo" % respository_directory
sys.exit(5)
# Set some environment variables, the repo.index commit function
# pays attention to these.
os.environ["GIT_AUTHOR_DATE"] = action_date
os.environ["GIT_COMMITTER_DATE"] = action_date
# Add your new file/s
repo.index.add([new_file_path])
# Do the commit thing.
repo.index.commit(message, author=actor, committer=actor)
You can set commit_date when making the commit.
r.index.commit(
"Initial Commit",
commit_date=datetime.date(2020, 7, 21).strftime('%Y-%m-%d %H:%M:%S')
)
I am also using specified date/time for committing purpose. In my case after a long research I have figured out, that you have to give the datetime not really in ISO format as GitPython suggests you should, nor in timestamp format. It is actually making delusion because of its own "parsing" that is should be a standard python format.
Nope. You have to give it in the format that git itself would understand.
For example:
# commit_date is your specified date in datetime format
commit_date_as_text = commit_date.strftime('%Y-%m-%d %H:%M:%S %z')
git_repository.index.commit(commit_message, author=commit_author, commit_date=commit_date_as_text)
List of accepted formats you can find for example in another topic on StackOverflow.

How do I get the current date and current time only respectively in Django?

I came across an interesting situation when using this class:
class Company(models.Model):
date = models.DateField()
time = models.TimeField()
c = Company(date=datetime.datetime.now(), time=datetime.datetime.now())
Django decides to use DATETIME_INPUT_FORMATS defined within the formats.py file.
Which makes sense, because I am passing in a datetime.now() to both fields.
I think I could make Django to use DATE_INPUT_FORMATS and TIME_INPUT_FORMATS respectively, if I passed in only the current date and current time in.
Something like this:
c = Company(date=datetime.date.now(), time=datetime.time.now())
But this obviously throws an exception as now doesn't exist like that. Is there a different way to achieve this?
For the date, you can use datetime.date.today() or datetime.datetime.now().date().
For the time, you can use datetime.datetime.now().time().
However, why have separate fields for these in the first place? Why not use a single DateTimeField?
You can always define helper functions on the model that return the .date() or .time() later if you only want one or the other.
import datetime
datetime.datetime.now().strftime ("%Y%m%d")
20151015
For the time
from time import gmtime, strftime
showtime = strftime("%Y-%m-%d %H:%M:%S", gmtime())
print showtime
2015-10-15 07:49:18
import datetime
datetime.date.today() # Returns 2018-01-15
datetime.datetime.now() # Returns 2018-01-15 09:00
import datetime
Current Date and time
print(datetime.datetime.now())
#2019-09-08 09:12:12.473393
Current date only
print(datetime.date.today())
#2019-09-08
Current year only
print(datetime.date.today().year)
#2019
Current month only
print(datetime.date.today().month)
#9
Current day only
print(datetime.date.today().day)
#8
A related info, to the question...
In django, use timezone.now() for the datetime field, as django supports timezone, it just returns datetime based on the USE TZ settings, or simply timezone 'aware' datetime objects
For a reference, I've got TIME_ZONE = 'Asia/Kolkata' and USE_TZ = True,
from django.utils import timezone
import datetime
print(timezone.now()) # The UTC time
print(timezone.localtime()) # timezone specified time,
print(datetime.datetime.now()) # default local time
# output
2020-12-11 09:13:32.430605+00:00
2020-12-11 14:43:32.430605+05:30 # IST is UTC+5:30
2020-12-11 14:43:32.510659
refer timezone settings and Internationalization and localization in django docs for more details.
Another way to get datetime UTC with milliseconds.
from datetime import datetime
datetime.utcnow().isoformat(sep='T', timespec='milliseconds') + 'Z'
2020-10-29T14:46:37.655Z

Python xmlrpclib.Fault while using NetDNA's API

I am trying to write a Python script that will list all of my Pull Zones. Everytime I run the script I get the following error:
xmlrpclib.Fault: <Fault 620: 'Method "pullzone.list" does not exist'>
The documentation for List Zones is here: http://support.netdna.com/api/#pullzone.listZones
Here is the script:
#! /usr/bin/python
from xmlrpclib import ServerProxy
from hashlib import sha256
from datetime import datetime, timedelta
from pytz import timezone
apiKey = 'sldjlskdjf'
apiUserId = '0000'
def pullzoneListZones():
global apiKey, apiUserId
date = datetime.now(timezone('America/Los_Angeles')).replace(microsecond=0).isoformat() # Must be 'America/Los_Angeles' always!
authString = sha256(date + ":" + apiKey + ":listZones").hexdigest()
sp = ServerProxy('http://api.netdna.com/xmlrpc/pullzone')
return sp.pullzone.list(apiUserId, authString, date)
print pullzoneListZones()
What am I missing? Thanks in advance. Disclaimer: I work for NetDNA but know one here knows Python.
Thanks in advance.
The method is wrongly named - it should be
sp.pullzone.listZones(apiUserId, authString, date)
See http://support.netdna.com/api/#Python for api names.

Categories

Resources