From e9143a0ba8b64eb4ce4cbb411c9a80b2e88fb0f8 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 15 Feb 2012 14:37:48 +0100 Subject: [PATCH] [FIX] reports: formatLang() should render datetime values in appropriate timezone lp bug: https://launchpad.net/bugs/932170 fixed bzr revid: odo@openerp.com-20120215133748-2iodxq0z1vqhyz13 --- openerp/osv/fields.py | 39 +++++++++++++++++++++++++++++++++++++-- openerp/report/report_sxw.py | 33 ++++++++++++++++++--------------- 2 files changed, 55 insertions(+), 17 deletions(-) diff --git a/openerp/osv/fields.py b/openerp/osv/fields.py index 43dca72..7c1761a 100644 --- a/openerp/osv/fields.py +++ b/openerp/osv/fields.py @@ -284,14 +284,19 @@ class date(_column): :param datetime timestamp: optional datetime value to use instead of the current date and time (must be a datetime, regular dates can't be converted - between timezones.)""" + between timezones.) + :param dict context: the 'tz' key in the context should give the + name of the User/Client timezone (otherwise + UTC is used) + :rtype: str + """ today = timestamp or DT.datetime.now() context_today = None if context and context.get('tz'): try: utc = pytz.timezone('UTC') context_tz = pytz.timezone(context['tz']) - utc_today = utc.localize(today, is_dst=False) + utc_today = utc.localize(today, is_dst=False) # UTC = no DST context_today = utc_today.astimezone(context_tz) except Exception: _logger.debug("failed to compute context/client-specific today date, " @@ -312,6 +317,36 @@ class datetime(_column): return DT.datetime.now().strftime( tools.DEFAULT_SERVER_DATETIME_FORMAT) + @staticmethod + def context_timestamp(cr, uid, timestamp, context=None): + """Returns the given timestamp converted to the client's timezone. + This method is *not* meant for use as a _defaults initializer, + because datetime fields are automatically converted upon + display on client side. For _defaults you :meth:`fields.datetime.now` + should be used instead. + + :param datetime timestamp: naive datetime value (expressed in UTC) + to be converted to the client timezone + :param dict context: the 'tz' key in the context should give the + name of the User/Client timezone (otherwise + UTC is used) + :rtype: datetime + :return: timestamp converted to timezone-aware datetime in context + timezone + """ + assert isinstance(timestamp, DT.datetime), 'Datetime instance expected' + if context and context.get('tz'): + try: + utc = pytz.timezone('UTC') + context_tz = pytz.timezone(context['tz']) + utc_timestamp = utc.localize(timestamp, is_dst=False) # UTC = no DST + return utc_timestamp.astimezone(context_tz) + except Exception: + _logger.debug("failed to compute context/client-specific timestamp, " + "using the UTC value", + exc_info=True) + return timestamp + class time(_column): _type = 'time' _deprecated = True diff --git a/openerp/report/report_sxw.py b/openerp/report/report_sxw.py index a1ceae9..106144f 100644 --- a/openerp/report/report_sxw.py +++ b/openerp/report/report_sxw.py @@ -33,15 +33,12 @@ import openerp.pooler as pooler import openerp.tools as tools import zipfile import common -from openerp.osv.fields import float as float_class, function as function_class +from openerp.osv.fields import float as float_field, function as function_field, datetime as datetime_field from openerp.tools.translate import _ +from openerp.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT _logger = logging.getLogger(__name__) -DT_FORMAT = '%Y-%m-%d' -DHM_FORMAT = '%Y-%m-%d %H:%M:%S' -HM_FORMAT = '%H:%M:%S' - rml_parents = { 'tr':1, 'li':1, @@ -69,7 +66,7 @@ rml2sxw = { 'para': 'p', } -def get_date_length(date_format=DT_FORMAT): +def get_date_length(date_format=DEFAULT_SERVER_DATE_FORMAT): return len((datetime.now()).strftime(date_format)) class _format(object): @@ -110,7 +107,7 @@ class _date_format(str, _format): def __str__(self): if self.val: if getattr(self,'name', None): - date = datetime.strptime(self.name[:get_date_length()], DT_FORMAT) + date = datetime.strptime(self.name[:get_date_length()], DEFAULT_SERVER_DATE_FORMAT) return date.strftime(str(self.lang_obj.date_format)) return self.val @@ -121,7 +118,7 @@ class _dttime_format(str, _format): def __str__(self): if self.val and getattr(self,'name', None): - return datetime.strptime(self.name, DHM_FORMAT)\ + return datetime.strptime(self.name, DEFAULT_SERVER_DATETIME_FORMAT)\ .strftime("%s %s"%(str(self.lang_obj.date_format), str(self.lang_obj.time_format))) return self.val @@ -264,7 +261,7 @@ class rml_parse(object): else: d = res_digits(self.cr)[1] elif (hasattr(obj, '_field') and\ - isinstance(obj._field, (float_class, function_class)) and\ + isinstance(obj._field, (float_field, function_field)) and\ obj._field.digits): d = obj._field.digits[1] or DEFAULT_DIGITS return d @@ -295,16 +292,22 @@ class rml_parse(object): return '' date_format = self.lang_dict['date_format'] - parse_format = DT_FORMAT + parse_format = DEFAULT_SERVER_DATE_FORMAT if date_time: - value=value.split('.')[0] + value = value.split('.')[0] date_format = date_format + " " + self.lang_dict['time_format'] - parse_format = DHM_FORMAT - if not isinstance(value, time.struct_time): - return time.strftime(date_format, time.strptime(value[:get_date_length(parse_format)], parse_format)) - + parse_format = DEFAULT_SERVER_DATETIME_FORMAT + if isinstance(value, basestring): + date = datetime.strptime(value[:get_date_length(parse_format)], parse_format) + elif isinstance(value, time.struct_time): + date = datetime(*value[:6]) else: date = datetime(*value.timetuple()[:6]) + if date_time: + # Convert datetime values to the expected client/context timezone + date = datetime_field.context_timestamp(self.cr, self.uid, + timestamp=date, + context=self.localcontext) return date.strftime(date_format) res = self.lang_dict['lang_obj'].format('%.' + str(digits) + 'f', value, grouping=grouping, monetary=monetary) -- 1.7.10.4