From 5b7ade9db180b8a038c2a9944aba85d1ffd220ea Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 22 Jan 2014 16:19:11 +0100 Subject: [PATCH] [ADD] ways to correctly handle local datetimes in e.g. domains * change datetime.now() to generate user-local naive datetimes * add datetime.utcnow() behaving as the old now() * add date.today() generating a user-local date (for the current day) * add datetime.replace() to replace any specific attribute of the datetime object (except tzinfo, for now) * datetime.toJSON() now returns the equivalent javascript Date object (warning: uses the datetime attributes directly, since datetimes are naive if they were created with utcnow() the Date result is going to be complete nonsense). With the previous commit datetime.now() generates a user-local now() which is converted to the correct UTC datetime when sent to the server. This means it becomes possible to generate datetime bounds for the user's local today with either datetime.datetime.now().replace(hour=0, minute=0, second=0) or datetime.datetime.combine( datetime.date.today(), datetime.time()) and once send over JSON-RPC the server will get the local datetime in UTC to the server's format. nb: user-local means "in the timezone of the user's browser" in this context. bzr revid: xmo@openerp.com-20140122151911-akn1nr6e739eg92s --- addons/web/static/src/js/pyeval.js | 49 +++++++++++++++++++++++++++--- addons/web/static/test/evals.js | 35 +++++++++++++++++++++ 2 files changed, 80 insertions(+), 4 deletions(-) diff --git a/addons/web/static/src/js/pyeval.js b/addons/web/static/src/js/pyeval.js index 0b0e9725af9..7da0f3d6f32 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'],