[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
This commit is contained in:
Xavier Morel 2014-01-22 16:19:11 +01:00
parent 36eedfab43
commit 5b7ade9db1
2 changed files with 80 additions and 4 deletions

View File

@ -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]);
});

View File

@ -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'],