+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.
+ """
+ try:
+ import pytz
+ except Exception:
+ 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 Exception:
+ 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_timezone(),"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'
+
+def get_server_timezone():
+ # timezone detection is safe in multithread, so lazy init is ok here
+ if (not config['timezone']):
+ config['timezone'] = detect_server_timezone()
+ return config['timezone']
+
+
+DEFAULT_SERVER_DATE_FORMAT = "%Y-%m-%d"
+DEFAULT_SERVER_TIME_FORMAT = "%H:%M:%S"
+DEFAULT_SERVER_DATETIME_FORMAT = "%s %s" % (
+ DEFAULT_SERVER_DATE_FORMAT,
+ DEFAULT_SERVER_TIME_FORMAT)
+
+# Python's strftime supports only the format directives
+# that are available on the platform's libc, so in order to
+# be cross-platform we map to the directives required by
+# the C standard (1989 version), always available on platforms
+# with a C standard implementation.
+DATETIME_FORMATS_MAP = {
+ '%C': '', # century
+ '%D': '%m/%d/%Y', # modified %y->%Y
+ '%e': '%d',
+ '%E': '', # special modifier
+ '%F': '%Y-%m-%d',
+ '%g': '%Y', # modified %y->%Y
+ '%G': '%Y',
+ '%h': '%b',
+ '%k': '%H',
+ '%l': '%I',
+ '%n': '\n',
+ '%O': '', # special modifier
+ '%P': '%p',
+ '%R': '%H:%M',
+ '%r': '%I:%M:%S %p',
+ '%s': '', #num of seconds since epoch
+ '%T': '%H:%M:%S',
+ '%t': ' ', # tab
+ '%u': ' %w',
+ '%V': '%W',
+ '%y': '%Y', # Even if %y works, it's ambiguous, so we should use %Y
+ '%+': '%Y-%m-%d %H:%M:%S',
+
+ # %Z is a special case that causes 2 problems at least:
+ # - the timezone names we use (in res_user.context_tz) come
+ # from pytz, but not all these names are recognized by
+ # strptime(), so we cannot convert in both directions
+ # when such a timezone is selected and %Z is in the format
+ # - %Z is replaced by an empty string in strftime() when
+ # there is not tzinfo in a datetime value (e.g when the user
+ # did not pick a context_tz). The resulting string does not
+ # parse back if the format requires %Z.
+ # As a consequence, we strip it completely from format strings.
+ # The user can always have a look at the context_tz in
+ # preferences to check the timezone.
+ '%z': '',
+ '%Z': '',
+}
+
+def server_to_local_timestamp(src_tstamp_str, src_format, dst_format, dst_tz_name,
+ tz_offset=True, ignore_unparsable_time=True):
+ """
+ Convert a source timestamp string into a destination timestamp string, attempting to apply the
+ correct offset if both the server and local timezone are recognized, or no
+ offset at all if they aren't or if tz_offset is false (i.e. assuming they are both in the same TZ).
+
+ WARNING: This method is here to allow formatting dates correctly for inclusion in strings where
+ the client would not be able to format/offset it correctly. DO NOT use it for returning
+ date fields directly, these are supposed to be handled by the client!!
+
+ @param src_tstamp_str: the str value containing the timestamp in the server timezone.
+ @param src_format: the format to use when parsing the server timestamp.
+ @param dst_format: the format to use when formatting the resulting timestamp for the local/client timezone.
+ @param dst_tz_name: name of the destination timezone (such as the 'tz' value of the client context)
+ @param ignore_unparsable_time: if True, return False if src_tstamp_str cannot be parsed
+ using src_format or formatted using dst_format.
+
+ @return: local/client formatted timestamp, expressed in the local/client timezone if possible
+ and if tz_offset is true, or src_tstamp_str if timezone offset could not be determined.
+ """
+ if not src_tstamp_str:
+ return False