[FIX] Made timezone detect more robust and added a config option to override it.
Stay backwards-compatible by using time.tzname[0] as we did before if no config option is provided, but only if it is a valid timezone (in the pytz sense). If no config option is set and time.tzname does't work, fallback, in this order, to: - environment variable 'TZ' - /etc/timezone on Unix platforms - Windows registry on win32 systems - if nothing works, default to UTC (because we MUST provide a valid timezone to clients) lp bug: https://launchpad.net/bugs/389544 fixed bzr revid: odo@openerp.com-20100105152312-fgayeaks12ed3no8
This commit is contained in:
@@ -443,7 +443,10 @@ GNU Public Licence.
|
||||
return info
|
||||
|
||||
def exp_timezone_get(self, db, login, password):
|
||||
return time.tzname[0]
|
||||
#timezone detection is safe in multithread, so lazy init is ok here
|
||||
if (not tools.config['timezone']):
|
||||
tools.config['timezone'] = tools.misc.detect_server_timezone()
|
||||
return tools.config['timezone']
|
||||
|
||||
|
||||
def exp_get_available_updates(self, contract_id, contract_password):
|
||||
|
||||
+15
-2
@@ -77,10 +77,11 @@ class configmanager(object):
|
||||
'secure' : False,
|
||||
'syslog' : False,
|
||||
'log_level': logging.INFO,
|
||||
'assert_exit_level': logging.WARNING, # level above which a failed assert will be raise
|
||||
'assert_exit_level': logging.WARNING, # level above which a failed assert will be raised
|
||||
'cache_timeout': 100000,
|
||||
'login_message': False,
|
||||
'list_db' : True,
|
||||
'timezone' : False, # to override the default TZ
|
||||
}
|
||||
|
||||
self.misc = {}
|
||||
@@ -111,6 +112,7 @@ class configmanager(object):
|
||||
help="update a module (use \"all\" for all modules)")
|
||||
parser.add_option("--cache-timeout", dest="cache_timeout",
|
||||
help="set the timeout for the cache system", default=100000, type="int")
|
||||
parser.add_option("-t", "--timezone", dest="timezone", help="specify reference timezone for the server (e.g. Europe/Brussels")
|
||||
|
||||
# stops the server from launching after initialization
|
||||
parser.add_option("--stop-after-init", action="store_true", dest="stop_after_init", default=False,
|
||||
@@ -227,7 +229,7 @@ class configmanager(object):
|
||||
'db_port', 'list_db', 'logfile', 'pidfile', 'smtp_port', 'cache_timeout',
|
||||
'email_from', 'smtp_server', 'smtp_user', 'smtp_password', 'price_accuracy',
|
||||
'netinterface', 'netport', 'db_maxconn', 'import_partial', 'addons_path',
|
||||
'netrpc', 'xmlrpc', 'syslog', 'without_demo']
|
||||
'netrpc', 'xmlrpc', 'syslog', 'without_demo', 'timezone',]
|
||||
|
||||
if hasSSL:
|
||||
keys.extend(['smtp_ssl', 'secure_cert_file', 'secure_pkey_file'])
|
||||
@@ -266,6 +268,17 @@ class configmanager(object):
|
||||
self.options['translate_modules'] = opt.translate_modules and map(lambda m: m.strip(), opt.translate_modules.split(',')) or ['all']
|
||||
self.options['translate_modules'].sort()
|
||||
|
||||
if self.options['timezone']:
|
||||
# If an explicit TZ was provided in the config, make sure it is known
|
||||
try:
|
||||
import pytz
|
||||
tz = pytz.timezone(self.options['timezone'])
|
||||
except pytz.UnknownTimeZoneError:
|
||||
die(True, "The specified timezone (%s) is invalid" % self.options['timezone'])
|
||||
except:
|
||||
# If pytz is missing, don't check the provided TZ, it will be ignored anyway.
|
||||
pass
|
||||
|
||||
if opt.pg_path:
|
||||
self.options['pg_path'] = opt.pg_path
|
||||
|
||||
|
||||
@@ -1174,6 +1174,89 @@ def detect_ip_addr():
|
||||
ip_addr = 'localhost'
|
||||
return ip_addr
|
||||
|
||||
# RATIONALE BEHIND TIMESTAMP CALCULATIONS AND TIMEZONE MANAGEMENT:
|
||||
# The server side never does any timestamp calculation, always
|
||||
# sends them in a naive (timezone agnostic) format supposed to be
|
||||
# expressed within the server timezone, and expects the clients to
|
||||
# provide timestamps in the server timezone as well.
|
||||
# It stores all timestamps in the database in naive format as well,
|
||||
# which also expresses the time in the server timezone.
|
||||
# For this reason the server makes its timezone name available via the
|
||||
# common/timezone_get() rpc method, which clients need to read
|
||||
# to know the appropriate time offset to use when reading/writing
|
||||
# times.
|
||||
def get_win32_timezone():
|
||||
"""Attempt to return the "standard name" of the current timezone on a win32 system.
|
||||
@return: the standard name of the current win32 timezone, or False if it cannot be found.
|
||||
"""
|
||||
res = False
|
||||
if (sys.platform == "win32"):
|
||||
try:
|
||||
import _winreg
|
||||
hklm = _winreg.ConnectRegistry(None,_winreg.HKEY_LOCAL_MACHINE)
|
||||
current_tz_key = _winreg.OpenKey(hklm, r"SYSTEM\CurrentControlSet\Control\TimeZoneInformation", 0,_winreg.KEY_ALL_ACCESS)
|
||||
res = str(_winreg.QueryValueEx(current_tz_key,"StandardName")[0]) # [0] is value, [1] is type code
|
||||
_winreg.CloseKey(current_tz_key)
|
||||
_winreg.CloseKey(hklm)
|
||||
except:
|
||||
pass
|
||||
return res
|
||||
|
||||
def detect_server_timezone():
|
||||
"""Attempt to detect the timezone to use on the server side.
|
||||
Defaults to UTC if no working timezone can be found.
|
||||
@return: the timezone identifier as expected by pytz.timezone.
|
||||
"""
|
||||
import time
|
||||
import netsvc
|
||||
try:
|
||||
import pytz
|
||||
except:
|
||||
netsvc.Logger().notifyChannel("detect_server_timezone", netsvc.LOG_WARNING,
|
||||
"Python pytz module is not available. Timezone will be set to UTC by default.")
|
||||
return 'UTC'
|
||||
|
||||
# Option 1: the configuration option (did not exist before, so no backwards compatibility issue)
|
||||
# Option 2: to be backwards compatible with 5.0 or earlier, the value from time.tzname[0], but only if it is known to pytz
|
||||
# Option 3: the environment variable TZ
|
||||
sources = [ (config['timezone'], 'OpenERP configuration'),
|
||||
(time.tzname[0], 'time.tzname'),
|
||||
(os.environ.get('TZ',False),'TZ environment variable'), ]
|
||||
# Option 4: OS-specific: /etc/timezone on Unix
|
||||
if (os.path.exists("/etc/timezone")):
|
||||
tz_value = False
|
||||
try:
|
||||
f = open("/etc/timezone")
|
||||
tz_value = f.read(128).strip()
|
||||
except:
|
||||
pass
|
||||
finally:
|
||||
f.close()
|
||||
sources.append((tz_value,"/etc/timezone file"))
|
||||
# Option 5: timezone info from registry on Win32
|
||||
if (sys.platform == "win32"):
|
||||
# Timezone info is stored in windows registry.
|
||||
# However this is not likely to work very well as the standard name
|
||||
# of timezones in windows is rarely something that is known to pytz.
|
||||
# But that's ok, it is always possible to use a config option to set
|
||||
# it explicitly.
|
||||
sources.append((get_win32_tz(),"Windows Registry"))
|
||||
|
||||
for (value,source) in sources:
|
||||
if value:
|
||||
try:
|
||||
tz = pytz.timezone(value)
|
||||
netsvc.Logger().notifyChannel("detect_server_timezone", netsvc.LOG_INFO,
|
||||
"Using timezone %s obtained from %s." % (tz.zone,source))
|
||||
return value
|
||||
except pytz.UnknownTimeZoneError:
|
||||
netsvc.Logger().notifyChannel("detect_server_timezone", netsvc.LOG_WARNING,
|
||||
"The timezone specified in %s (%s) is invalid, ignoring it." % (source,value))
|
||||
|
||||
netsvc.Logger().notifyChannel("detect_server_timezone", netsvc.LOG_WARNING,
|
||||
"No valid timezone could be detected, using default UTC timezone. You can specify it explicitly with option 'timezone' in the server configuration.")
|
||||
return 'UTC'
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import doctest
|
||||
|
||||
Reference in New Issue
Block a user