[ADD] formats support to date and datetime converters

bzr revid: xmo@openerp.com-20131202073313-tu79esduu5bvyaai
This commit is contained in:
Xavier Morel 2013-12-02 08:33:13 +01:00
parent c457c2197a
commit 228938ccba
4 changed files with 149 additions and 8 deletions

View File

@ -627,14 +627,21 @@ class DateConverter(osv.AbstractModel):
def value_to_html(self, cr, uid, value, column, options=None, context=None):
if not value: return ''
lang = self.user_lang(cr, uid, context=context)
out_format = lang.date_format.encode('utf-8')
locale = babel.Locale.parse(lang.code)
if isinstance(value, basestring):
value = datetime.datetime.strptime(
value, openerp.tools.DEFAULT_SERVER_DATE_FORMAT)
return value.strftime(out_format)
if options and 'format' in options:
pattern = options['format']
else:
strftime_pattern = lang.date_format
pattern = openerp.tools.posix_to_ldml(strftime_pattern, locale=locale)
return babel.dates.format_datetime(
value, format=pattern,
locale=locale)
class DateTimeConverter(osv.AbstractModel):
_name = 'ir.qweb.field.datetime'
@ -643,17 +650,21 @@ class DateTimeConverter(osv.AbstractModel):
def value_to_html(self, cr, uid, value, column, options=None, context=None):
if not value: return ''
lang = self.user_lang(cr, uid, context=context)
out_format = (u"%s %s" % (lang.date_format, lang.time_format)).encode('utf-8')
locale = babel.Locale.parse(lang.code)
if isinstance(value, basestring):
value = datetime.datetime.strptime(
value, openerp.tools.DEFAULT_SERVER_DATETIME_FORMAT)
value = column.context_timestamp(
cr, uid, timestamp=value, context=context)
return value.strftime(out_format)
if options and 'format' in options:
pattern = options['format']
else:
strftime_pattern = (u"%s %s" % (lang.date_format, lang.time_format))
pattern = openerp.tools.posix_to_ldml(strftime_pattern, locale=locale)
return babel.dates.format_datetime(value, format=pattern, locale=locale)
class TextConverter(osv.AbstractModel):
_name = 'ir.qweb.field.text'

View File

@ -304,6 +304,22 @@ class TestDatetimeExport(TestBasicExport):
# default lang/format is US
self.assertEqual(value, '05/03/2011 00:12:13')
def test_custom_format(self):
converter = self.get_converter('datetime')
converter2 = self.get_converter('date')
opts = {'format': 'MMMM d'}
value = converter('2011-03-02 11:12:13', options=opts)
value2 = converter2('2001-03-02', options=opts)
self.assertEqual(
value,
'March 2'
)
self.assertEqual(
value2,
'March 2'
)
class TestDurationExport(TestBasicExport):
def setUp(self):
super(TestDurationExport, self).setUp()

View File

@ -1,7 +1,12 @@
# This test can be run stand-alone with something like:
# > PYTHONPATH=. python2 openerp/tests/test_misc.py
import datetime
import locale
import unittest2
import babel
import babel.dates
from ..tools import misc
@ -35,5 +40,44 @@ class test_countingstream(unittest2.TestCase):
self.assertIsNone(next(s, None))
self.assertEqual(s.index, 0)
lname, _ = locale.getdefaultlocale()
class TestPosixToBabel(unittest2.TestCase):
def setUp(self):
super(TestPosixToBabel, self).setUp()
self.d = datetime.datetime(2007, 9, 8, 4, 5, 1)
def assert_eq(self, fmt, d=None):
if d is None: d = self.d
ldml_format = misc.posix_to_ldml(fmt, locale=babel.Locale.parse(lname))
self.assertEqual(
d.strftime(fmt),
babel.dates.format_datetime(d, format=ldml_format),
"%r resulted in a different result than %r for %s" % (
ldml_format, fmt, d))
def test_empty(self):
self.assert_eq("")
def test_literal(self):
self.assert_eq("Raw test string")
def test_mixed(self):
self.assert_eq("m:%m d:%d y:%y")
self.assert_eq("m:%m d:%d y:%y H:%H M:%M S:%S")
def test_escape(self):
self.assert_eq("%%m:%m %%d:%d %%y:%y")
def test_xX(self):
self.assert_eq('%x %X')
def test_various_examples(self):
self.assert_eq("%x - %I:%M%p")
self.assert_eq('%Y-%m-%dT%H:%M:%S')
self.assert_eq("%Y-%j")
self.assert_eq("%a, %d %b %Y %H:%M:%S")
self.assert_eq("%a, %b %d %I:%M.%S")
if __name__ == '__main__':
unittest2.main()

View File

@ -819,6 +819,76 @@ DATETIME_FORMATS_MAP = {
'%Z': '',
}
POSIX_TO_LDML = {
'a': 'E',
'A': 'EEEE',
'b': 'MMM',
'B': 'MMMM',
#'c': '',
'd': 'dd',
'H': 'HH',
'I': 'hh',
'j': 'DDD',
'm': 'MM',
'M': 'mm',
'p': 'a',
'S': 'ss',
'U': 'w',
'w': 'e',
'W': 'w',
'y': 'yy',
'Y': 'yyyy',
# see comments above, and babel's format_datetime assumes an UTC timezone
# for naive datetime objects
#'z': 'Z',
#'Z': 'z',
}
def posix_to_ldml(fmt, locale):
""" Converts a posix/strftime pattern into an LDML date format pattern.
:param fmt: non-extended C89/C90 strftime pattern
:param locale: babel locale used for locale-specific conversions (e.g. %x and %X)
:return: unicode
"""
buf = []
pc = False
quoted = []
for c in fmt:
# LDML date format patterns uses letters, so letters must be quoted
if not pc and c.isalpha():
quoted.append(c if c != "'" else "''")
continue
if quoted:
buf.append("'")
buf.append(''.join(quoted))
buf.append("'")
quoted = []
if pc:
if c == '%': # escaped percent
buf.append('%')
elif c == 'x': # date format, short seems to match
buf.append(locale.date_formats['short'].pattern)
elif c == 'X': # time format, seems to include seconds. short does not
buf.append(locale.time_formats['medium'].pattern)
else: # look up format char in static mapping
buf.append(POSIX_TO_LDML[c])
pc = False
elif c == '%':
pc = True
else:
buf.append(c)
# flush anything remaining in quoted buffer
if quoted:
buf.append("'")
buf.append(''.join(quoted))
buf.append("'")
return ''.join(buf)
def server_to_local_timestamp(src_tstamp_str, src_format, dst_format, dst_tz_name,
tz_offset=True, ignore_unparsable_time=True):
"""