diff --git a/addons/web/static/src/js/openerpframework.js b/addons/web/static/src/js/openerpframework.js index 22ca70ebbf7..71f45899f5f 100644 --- a/addons/web/static/src/js/openerpframework.js +++ b/addons/web/static/src/js/openerpframework.js @@ -823,13 +823,33 @@ var genericJsonRpc = function(fct_name, params, fct) { }); }; +/** + * Replacer function for JSON.stringify, serializes Date objects to UTC + * datetime in the OpenERP Server format. + * + * However, if a serialized value has a toJSON method that method is called + * *before* the replacer is invoked. Date#toJSON exists, and thus the value + * passed to the replacer is a string, the original Date has to be fetched + * on the parent object (which is provided as the replacer's context). + * + * @param {String} k + * @param {Object} v + * @returns {Object} + */ +function date_to_utc(k, v) { + var value = this[k]; + if (!(value instanceof Date)) { return v; } + + return openerp.datetime_to_str(value); +} + openerp.jsonRpc = function(url, fct_name, params, settings) { return genericJsonRpc(fct_name, params, function(data) { return $.ajax(url, _.extend({}, settings, { url: url, dataType: 'json', type: 'POST', - data: JSON.stringify(data), + data: JSON.stringify(data, date_to_utc), contentType: 'application/json' })); }); @@ -838,7 +858,7 @@ openerp.jsonRpc = function(url, fct_name, params, settings) { openerp.jsonpRpc = function(url, fct_name, params, settings) { settings = settings || {}; return genericJsonRpc(fct_name, params, function(data) { - var payload_str = JSON.stringify(data); + var payload_str = JSON.stringify(data, date_to_utc); var payload_url = $.param({r:payload_str}); var force2step = settings.force2step || false; delete settings.force2step; diff --git a/addons/web/static/src/js/pyeval.js b/addons/web/static/src/js/pyeval.js index b10c175c683..d5499a028b7 100644 --- a/addons/web/static/src/js/pyeval.js +++ b/addons/web/static/src/js/pyeval.js @@ -382,13 +382,28 @@ this[key] = asJS(args[key]); } }, + replace: function () { + var args = py.PY_parseArgs(arguments, [ + ['year', py.None], ['month', py.None], ['day', py.None], + ['hour', py.None], ['minute', py.None], ['second', py.None], + ['microsecond', py.None] // FIXME: tzinfo, can't use None as valid input + ]); + var params = {}; + for(var key in args) { + if (!args.hasOwnProperty(key)) { continue; } + + var arg = args[key]; + params[key] = (arg === py.None ? this[key] : asJS(arg)); + } + return py.PY_call(datetime.datetime, params); + }, strftime: function () { var self = this; var args = py.PY_parseArgs(arguments, 'format'); return py.str.fromJSON(args.format.toJSON() .replace(/%([A-Za-z])/g, function (m, c) { switch (c) { - case 'Y': return self.year; + case 'Y': return _.str.sprintf('%04d', self.year); case 'm': return _.str.sprintf('%02d', self.month); case 'd': return _.str.sprintf('%02d', self.day); case 'H': return _.str.sprintf('%02d', self.hour); @@ -399,6 +414,17 @@ })); }, now: py.classmethod.fromJSON(function () { + var d = new Date; + return py.PY_call(datetime.datetime, [ + d.getFullYear(), d.getMonth() + 1, d.getDate(), + d.getHours(), d.getMinutes(), d.getSeconds(), + d.getMilliseconds() * 1000]); + }), + today: py.classmethod.fromJSON(function () { + var dt_class = py.PY_getAttr(datetime, 'datetime'); + return py.PY_call(py.PY_getAttr(dt_class, 'now')); + }), + utcnow: py.classmethod.fromJSON(function () { var d = new Date(); return py.PY_call(datetime.datetime, [d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate(), @@ -415,7 +441,17 @@ py.PY_getAttr(args.time, 'minute'), py.PY_getAttr(args.time, 'second') ]); - }) + }), + toJSON: function () { + return new Date( + this.year, + this.month - 1, + this.day, + this.hour, + this.minute, + this.second, + this.microsecond / 1000); + }, }); datetime.date = py.type('date', null, { __init__: function () { @@ -470,7 +506,12 @@ }, fromJSON: function (year, month, day) { return py.PY_call(datetime.date, [year, month, day]); - } + }, + today: py.classmethod.fromJSON(function () { + var d = new Date; + return py.PY_call(datetime.date, [ + d.getFullYear(), d.getMonth() + 1, d.getDate()]); + }), }); /** Returns the current local date, which means the date on the client (which can be different @@ -501,7 +542,7 @@ time.strftime = py.PY_def.fromJSON(function () { var args = py.PY_parseArgs(arguments, 'format'); var dt_class = py.PY_getAttr(datetime, 'datetime'); - var d = py.PY_call(py.PY_getAttr(dt_class, 'now')); + var d = py.PY_call(py.PY_getAttr(dt_class, 'utcnow')); return py.PY_call(py.PY_getAttr(d, 'strftime'), [args.format]); }); diff --git a/addons/web/static/test/evals.js b/addons/web/static/test/evals.js index 840ad051aca..13ac0f3f3b7 100644 --- a/addons/web/static/test/evals.js +++ b/addons/web/static/test/evals.js @@ -265,6 +265,41 @@ openerp.testing.section('eval.types', { instance.web.pyeval.context()), "2012-02-14 23:59:59"); }); + test('datetime.tojson', function (instance) { + var result = py.eval( + 'datetime.datetime(2012, 2, 15, 1, 7, 31)', + instance.web.pyeval.context()); + ok(result instanceof Date); + equal(result.getFullYear(), 2012); + equal(result.getMonth(), 1); + equal(result.getDate(), 15); + equal(result.getHours(), 1); + equal(result.getMinutes(), 7); + equal(result.getSeconds(), 31); + }); + test('datetime.combine', function (instance) { + var result = py.eval( + 'datetime.datetime.combine(datetime.date(2012, 2, 15),' + + ' datetime.time(1, 7, 13))' + + ' .strftime("%Y-%m-%d %H:%M:%S")', + instance.web.pyeval.context()); + equal(result, "2012-02-15 01:07:13"); + + result = py.eval( + 'datetime.datetime.combine(datetime.date(2012, 2, 15),' + + ' datetime.time())' + + ' .strftime("%Y-%m-%d %H:%M:%S")', + instance.web.pyeval.context()); + equal(result, '2012-02-15 00:00:00'); + }); + test('datetime.replace', function (instance) { + var result = py.eval( + 'datetime.datetime(2012, 2, 15, 1, 7, 13)' + + ' .replace(hour=0, minute=0, second=0)' + + ' .strftime("%Y-%m-%d %H:%M:%S")', + instance.web.pyeval.context()); + equal(result, "2012-02-15 00:00:00"); + }); }); openerp.testing.section('eval.edc', { dependencies: ['web.data'],