* eTemplate2/all apps: fixing various timezone related issues

backport of Nathan and mine commits r47919, r48102, r48133, r48142, r48163, r48166 using now ISO time strings instead of timestamps for communication between client and server
This commit is contained in:
Ralf Becker 2014-08-21 18:46:11 +00:00
parent 843b12b357
commit ba254be4fc
3 changed files with 103 additions and 28 deletions

View File

@ -1244,7 +1244,6 @@ app.classes.calendar = AppJS.extend(
var alarm_date = this.et2.getWidgetById('new_alarm[date]');
var alarm_options = _widget || this.et2.getWidgetById('new_alarm[options]');
var start = this.et2.getWidgetById('start');
var date = 0;
if (alarm_date && alarm_options
&& start)
@ -1260,7 +1259,8 @@ app.classes.calendar = AppJS.extend(
var startDate = start.get_value();
if (startDate)
{
date = startDate - parseInt(alarm_options.get_value());
var date = new Date(startDate);
date.setTime(date.getTime() - 1000 * parseInt(alarm_options.get_value()));
alarm_date.set_value(date);
}
}

View File

@ -30,9 +30,10 @@
*
* @todo validation of date-duration
*
* @info beforeSendToClient is no longer neccessary, in order to handle date/time conversion, for this widget
* as we are handling both timestamp and string date/time formats on client side
*
* @info Communication between client and server is always done as a string in ISO8601/W3C
* format ("Y-m-d\TH:i:sP"). If the application specifies a different format
* for the field, the conversion is done as needed understand what the application
* sends, and to give the application what it wants when the form is submitted.
*/
class etemplate_widget_date extends etemplate_widget_transformer
{
@ -48,9 +49,48 @@ class etemplate_widget_date extends etemplate_widget_transformer
protected $legacy_options = 'dataformat,mode';
/**
* Convert the provided date into the format needed for unambiguous communication
* with browsers (Javascript). We use W3C format to avoid timestamp issues.
*/
public function beforeSendToClient($cname)
{
if($this->type == 'date-houronly')
{
return parent::beforeSendToClient($cname);
}
$form_name = self::form_name($cname, $this->id);
$value =& self::get_array(self::$request->content, $form_name, false, true);
if($this->type != 'date-duration' && $value)
{
// string with formatting letters like for php's date() method
if ($this->attrs['dataformat'] && !is_numeric($value))
{
$date = date_create_from_format($this->attrs['dataformat'], $value, new DateTimeZone('UTC'));
}
else
{
$date = new egw_time((int)$value, new DateTimeZone('UTC'));
}
if($date)
{
// Set timezone to UTC so javascript doesn't add/subtract anything
$date->setTimezone(new DateTimeZone('UTC'));
$value = $date->format(egw_time::W3C);
}
}
}
/**
* Validate input
*
* For dates (except duration), it is always a full timestamp in W3C format,
* which we then convert to the format the application is expecting. This can
* be either a unix timestamp, just a date, just time, or whatever is
* specified in the template.
*
* @param string $cname current namespace
* @param array $expand values for keys 'c', 'row', 'c_', 'row_', 'cont'
* @param array $content
@ -78,12 +118,21 @@ class etemplate_widget_date extends etemplate_widget_transformer
{
$valid = (string)$value === '' ? '' : (int)$value;
}
if($value)
{
$date = new egw_time($value);
}
if(!$value)
{
// Not null, blank
$value = '';
}
elseif (empty($this->attrs['dataformat'])) // integer timestamp
{
$valid = (int)$value;
$valid = $date->format('ts');
}
// string with formatting letters like for php's date() method
elseif (($valid = date($this->attrs['dataformat'], $value)))
elseif (($valid = $date->format($this->attrs['dataformat'])))
{
// Nothing to do here
}
@ -92,6 +141,7 @@ class etemplate_widget_date extends etemplate_widget_transformer
// this is not really a user error, but one of the clientside engine
self::set_validation_error($form_name,lang("'%1' is not a valid date !!!", $value).' '.$this->dataformat);
}
//error_log("$this : ($valid)" . egw_time::to($valid));
}
}
}

View File

@ -23,6 +23,9 @@
/**
* Class which implements the "date" XET-Tag
*
* Dates are passed to the server in ISO8601 format ("Y-m-d\TH:i:sP"), and data_format is
* handled server-side.
*
* @augments et2_inputWidget
*/
var et2_date = et2_inputWidget.extend(
@ -35,7 +38,7 @@ var et2_date = et2_inputWidget.extend(
"ignore": false
},
"data_format": {
"ignore": false,
"ignore": true,
"description": "Date/Time format. Can be set as an options to date widget",
"default": ''
}
@ -52,7 +55,7 @@ var et2_date = et2_inputWidget.extend(
this._super.apply(this, arguments);
this.date = new Date();
this.date.setHours(0);
this.date.setUTCHours(0);
this.date.setMinutes(0);
this.date.setSeconds(0);
this.input = null;
@ -121,6 +124,11 @@ var et2_date = et2_inputWidget.extend(
return;
}
// Check for full timestamp
if(typeof _value == 'string' && _value.match(/(\d{4})-(\d{2})-(\d{2})T(\d{2})\:(\d{2})\:(\d{2})[+-](\d{2})\:(\d{2})/))
{
_value = new Date(_value);
}
// Handle just time as a string in the form H:i
if(typeof _value == 'string' && isNaN(_value))
{
@ -146,7 +154,8 @@ var et2_date = et2_inputWidget.extend(
return;
}
this.set_validation_error(false);
this.date.setHours(parsed.hour);
// Avoid javascript timezone offset, hour is in 'user time'
this.date.setUTCHours(parsed.hour);
this.date.setMinutes(parsed.minute);
this.input_date.val(_value);
if(old_value !== this.getValue())
@ -157,7 +166,7 @@ var et2_date = et2_inputWidget.extend(
this.change(this.input_date);
}
}
this._oldValue = _value;
this._oldValue = this.date.toJSON();
return;
default:
// Parse customfields's date with storage data_format to date object
@ -173,7 +182,6 @@ var et2_date = et2_inputWidget.extend(
var DTformat = this.options.data_format.split(' ');
var parsed = jQuery.datepicker.parseDateTime(this.egw().dateTimeFormat(DTformat[0]),this.egw().dateTimeFormat(DTformat[1]), _value);
}
this.date = new Date(parsed);
}
else // Parse other date widgets date with timepicker date/time format to date onject
{
@ -184,9 +192,13 @@ var et2_date = et2_inputWidget.extend(
this.set_validation_error(this.egw().lang("%1' han an invalid format !!!",_value));
return;
}
this.date = new Date(parsed);
}
// Update local variable, but remove the timezone offset that
// javascript adds when we parse
if(parsed)
{
this.date = new Date(parsed.valueOf() - parsed.getTimezoneOffset() * 60000);
}
this.set_validation_error(false);
}
@ -202,17 +214,21 @@ var et2_date = et2_inputWidget.extend(
// Update input - popups do, but framework doesn't
_value = '';
// Add timezone offset back in, or formatDate will lose those hours
var formatDate = new Date(this.date.valueOf() + this.date.getTimezoneOffset() * 60 * 1000);
if(this._type != 'date-timeonly')
{
_value = jQuery.datepicker.formatDate(this.input_date.datepicker("option","dateFormat"),this.date);
_value = jQuery.datepicker.formatDate(this.input_date.datepicker("option","dateFormat"),
formatDate
);
}
if(this._type != 'date')
{
if(this._type != 'date-timeonly') _value += ' ';
_value += jQuery.datepicker.formatTime(this.input_date.datepicker("option","timeFormat"),{
hour: this.date.getHours(),
minute: this.date.getMinutes(),
hour: formatDate.getHours(),
minute: formatDate.getMinutes(),
seconds: 0,
timezone: 0
});
@ -240,11 +256,13 @@ var et2_date = et2_inputWidget.extend(
}
else if (this._type == 'date')
{
this.date.setHours(12);
this.date.setUTCHours(0);
this.date.setUTCMinutes(0);
}
// Convert to timestamp - no seconds
this.date.setSeconds(0,0);
return Math.round(this.date.valueOf() / 1000);
return this.date.toJSON();
}
});
et2_register_widget(et2_date, ["date", "date-time", "date-timeonly"]);
@ -643,6 +661,13 @@ var et2_date_ro = et2_valueWidget.extend([et2_IDetachedDOM],
jQuery.datepicker.parseDateTime(this.egw().preference('dateformat'),this.egw().preference('timeformat') == '24' ? 'H:i' : 'g:i a', _value);
var text = new Date(parsed);
// Update local variable, but remove the timezone offset that javascript adds
if(parsed)
{
this.date = new Date(text.valueOf() - (text.getTimezoneOffset()*60*1000));
}
// JS dates use milliseconds
this.date.setTime(text.valueOf());
}