[MERGE] [ADD] ways to correctly handle local datetimes in e.g. domains
* Alter datetime.now(), generate a local datetime (add utcnow() which generates a UTC datetime) * Implement datetime.replace() to manipulate local datetimes * Implement date.today(), generates a local date * Implement datetime.toJSON(), returns a javascript Date (assumes datetime attributes are local) * Add conversion hook in JSON and JSONP handlers, automatically converts a Date object to a UTC datetime formatted according to server formats Should allow the generation of correctly working (from the end-user's POV) [Today] filters, amongst other things. Eg: a local expression in a domain for 'Today 00:00:00' would now be expressed as 'datetime.datetime.now().replace(hour=0, minute=0, second=0)' (no .strftime) and will be converted to UTC when sent to the server bzr revid: mat@openerp.com-20140128133706-4yp610pp5w06tcia
This commit is contained in:
commit
802d64ca92
|
@ -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) {
|
openerp.jsonRpc = function(url, fct_name, params, settings) {
|
||||||
return genericJsonRpc(fct_name, params, function(data) {
|
return genericJsonRpc(fct_name, params, function(data) {
|
||||||
return $.ajax(url, _.extend({}, settings, {
|
return $.ajax(url, _.extend({}, settings, {
|
||||||
url: url,
|
url: url,
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
data: JSON.stringify(data),
|
data: JSON.stringify(data, date_to_utc),
|
||||||
contentType: 'application/json'
|
contentType: 'application/json'
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
@ -838,7 +858,7 @@ openerp.jsonRpc = function(url, fct_name, params, settings) {
|
||||||
openerp.jsonpRpc = function(url, fct_name, params, settings) {
|
openerp.jsonpRpc = function(url, fct_name, params, settings) {
|
||||||
settings = settings || {};
|
settings = settings || {};
|
||||||
return genericJsonRpc(fct_name, params, function(data) {
|
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 payload_url = $.param({r:payload_str});
|
||||||
var force2step = settings.force2step || false;
|
var force2step = settings.force2step || false;
|
||||||
delete settings.force2step;
|
delete settings.force2step;
|
||||||
|
|
|
@ -382,13 +382,28 @@
|
||||||
this[key] = asJS(args[key]);
|
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 () {
|
strftime: function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
var args = py.PY_parseArgs(arguments, 'format');
|
var args = py.PY_parseArgs(arguments, 'format');
|
||||||
return py.str.fromJSON(args.format.toJSON()
|
return py.str.fromJSON(args.format.toJSON()
|
||||||
.replace(/%([A-Za-z])/g, function (m, c) {
|
.replace(/%([A-Za-z])/g, function (m, c) {
|
||||||
switch (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 'm': return _.str.sprintf('%02d', self.month);
|
||||||
case 'd': return _.str.sprintf('%02d', self.day);
|
case 'd': return _.str.sprintf('%02d', self.day);
|
||||||
case 'H': return _.str.sprintf('%02d', self.hour);
|
case 'H': return _.str.sprintf('%02d', self.hour);
|
||||||
|
@ -399,6 +414,17 @@
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
now: py.classmethod.fromJSON(function () {
|
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();
|
var d = new Date();
|
||||||
return py.PY_call(datetime.datetime,
|
return py.PY_call(datetime.datetime,
|
||||||
[d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate(),
|
[d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate(),
|
||||||
|
@ -415,7 +441,17 @@
|
||||||
py.PY_getAttr(args.time, 'minute'),
|
py.PY_getAttr(args.time, 'minute'),
|
||||||
py.PY_getAttr(args.time, 'second')
|
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, {
|
datetime.date = py.type('date', null, {
|
||||||
__init__: function () {
|
__init__: function () {
|
||||||
|
@ -470,7 +506,12 @@
|
||||||
},
|
},
|
||||||
fromJSON: function (year, month, day) {
|
fromJSON: function (year, month, day) {
|
||||||
return py.PY_call(datetime.date, [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
|
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 () {
|
time.strftime = py.PY_def.fromJSON(function () {
|
||||||
var args = py.PY_parseArgs(arguments, 'format');
|
var args = py.PY_parseArgs(arguments, 'format');
|
||||||
var dt_class = py.PY_getAttr(datetime, 'datetime');
|
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]);
|
return py.PY_call(py.PY_getAttr(d, 'strftime'), [args.format]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -265,6 +265,41 @@ openerp.testing.section('eval.types', {
|
||||||
instance.web.pyeval.context()),
|
instance.web.pyeval.context()),
|
||||||
"2012-02-14 23:59:59");
|
"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', {
|
openerp.testing.section('eval.edc', {
|
||||||
dependencies: ['web.data'],
|
dependencies: ['web.data'],
|
||||||
|
|
Loading…
Reference in New Issue