Reputation: 48630
I am trying to convert UTC timestamps to UTC Dates, modify those dates, and return the UTC timestamp of the selected date/time. Since the Ext.form.field.Date
and Ext.form.field.Time
fields use local time internally, I have to add 5 hours to the time and make sure the date stays the same, to retrieve the timestamp, I have to reverse the process and get the ISO timestamp (without milliseconds).
Everything seems to work OK, but when I select 2015-01-01T01:15:00Z
the date field is set to the following day (01/02/2015
; See Figure #1). I am not sure where I am failing to convert the date correctly?
Figure #1: Date is set to next day.
After fiddling around a bit, It looks like I got the date correct, but now the time field complains (See Figure #2). Looks like the value that was set is removed from the time drop-down. This is extremely confusing.
setValue: function (value) {
// ...
if (value != null && Ext.isDate(value)) {
var timeDate = Ext.clone(value);
timeDate.setFullYear(1970, 0, 1);
me.lookupReference('dateField').setValue(
Ext.Date.add(value, Ext.Date.MILLI, timeDate.getTime()));
me.lookupReference('timeField').setValue(me.convertFromUTC(timeDate));
}
// ...
}
Figure #2: Time field validation fails.
You can access a demo of the following code at the following sites:
Ext.define('DateTime.components.DateTimeField', {
extend: 'Ext.container.Container',
mixins: ['Ext.form.field.Field'],
alias: 'widget.datetimefield',
config: {
dateConfig: {},
timeConfig: {},
utcValue: true,
hideDate: false
},
referenceHolder: true,
layout: {
type: 'hbox'
},
initComponent: function () {
var me = this,
today = new Date(),
dateConfig = me.dateConfig,
timeConfig = me.timeConfig;
me.items = [Ext.apply({
xtype: 'datefield',
reference: 'dateField',
fieldLabel: 'Date',
value: today,
ignoreOnSubmit: true,
listeners: {
change: function (field, newValue, oldValue) {
me.fireEvent('dateFieldChange', field, newValue, oldValue);
}
}
}, dateConfig),
Ext.apply({
xtype: 'timefield',
reference: 'timeField',
format: 'H:i',
value: '00:00',
minValue: '00:00',
maxValue: '24:00',
increment: 15,
padding: '0 0 0 10',
width: 80,
ignoreOnSubmit: true
}, timeConfig)];
me.callParent();
},
afterRender: function () {
var me = this;
if (me.hideData) {
me.lookupReference('dateField').hide();
}
me.callParent();
},
getValue: function () {
var me = this,
dateValue = me.getDate(),
timeValue = me.getTime();
if (dateValue != null && timeValue != null) {
dateValue = Ext.Date.add(dateValue, Ext.Date.MILLI, timeValue);
}
return this.convertToUTC(dateValue);
},
setValue: function (value) {
var me = this;
if (value == null) {
return;
}
if (Ext.isString(value)) {
value = Ext.Date.parse(value, 'c');
}
// Debug
console.log('Parsed Date: ' + value);
if (value != null && Ext.isDate(value)) {
var timeDate = Ext.clone(value);
timeDate.setFullYear(1970, 0, 1);
timeDate = me.convertFromUTC(timeDate);
me.lookupReference('dateField').setValue(
Ext.Date.add(value, Ext.Date.MILLI, timeDate.getTime()));
me.lookupReference('timeField').setValue(timeDate);
}
},
getInputId: function () {
return null;
},
getTime: function () {
var me = this,
timeValue = me.lookupReference('timeField').getValue();
timeValue.setFullYear(1970, 0, 1);
timeValue = this.convertToUTC(timeValue, this.getDate());
return timeValue.getTime();
},
getDate: function () {
return this.lookupReference('dateField').getValue();
},
setDate: function (value) {
this.lookupReference('dateField').setValue(value);
},
convertToUTC: function (date, dateOffset) {
if (dateOffset == null) {
dateOffset = date;
}
if (this.utcValue) {
return Ext.Date.subtract(date, Ext.Date.MINUTE, dateOffset.getTimezoneOffset());
}
return date;
},
convertFromUTC: function (date, dateOffset) {
if (dateOffset == null) {
dateOffset = date;
}
if (this.utcValue) {
return Ext.Date.add(date, Ext.Date.MINUTE, dateOffset.getTimezoneOffset());
}
return date;
}
});
//////////////////////////////////////////////////////////
// Requires
//////////////////////////////////////////////////////////
Ext.require(['*']);
//////////////////////////////////////////////////////////
// Data
//////////////////////////////////////////////////////////
var timestampData = [
['2014-02-28T08:45:00Z'],
['2015-01-01T01:15:00Z'],
['2014-12-31T11:30:00Z']
];
//////////////////////////////////////////////////////////
// Models
//////////////////////////////////////////////////////////
Ext.define('DateTime.model.Timestamp', {
extend: 'Ext.data.Model',
fields: ['timestamp']
});
//////////////////////////////////////////////////////////
// Stores
//////////////////////////////////////////////////////////
Ext.define('DateTime.store.Timestamp', {
extend : 'Ext.data.ArrayStore',
model: 'DateTime.model.Timestamp',
autoLoad: true,
autoSync: true,
proxy: {
type: 'memory'
}
});
//////////////////////////////////////////////////////////
// Mixins
//////////////////////////////////////////////////////////
Ext.define('DateTime.mixin.CommonUtils', {
dateToISOString: function (date) {
var pad = function (number) {
return ('00' + number).slice(-2);
};
return date.getUTCFullYear() + '-'
+ pad(date.getUTCMonth() + 1) + '-'
+ pad(date.getUTCDate()) + 'T'
+ pad(date.getUTCHours()) + ':'
+ pad(date.getUTCMinutes()) + ':'
+ pad(date.getUTCSeconds()) + 'Z';
}
});
//////////////////////////////////////////////////////////
// Components
//////////////////////////////////////////////////////////
Ext.define('DateTime.components.DateTimeField', {
extend: 'Ext.container.Container',
mixins: ['Ext.form.field.Field'],
alias: 'widget.datetimefield',
config: {
dateConfig: {},
timeConfig: {},
utcValue: true,
hideDate: false
},
referenceHolder: true,
layout: {
type: 'hbox'
},
initComponent: function () {
var me = this,
today = new Date(),
dateConfig = me.dateConfig,
timeConfig = me.timeConfig;
me.items = [Ext.apply({
xtype: 'datefield',
reference: 'dateField',
fieldLabel: 'Date',
value: today,
ignoreOnSubmit: true,
listeners: {
change: function (field, newValue, oldValue) {
me.fireEvent('dateFieldChange', field, newValue, oldValue);
}
}
}, dateConfig),
Ext.apply({
xtype: 'timefield',
reference: 'timeField',
format: 'H:i',
value: '00:00',
minValue: '00:00',
maxValue: '24:00',
increment: 15,
padding: '0 0 0 10',
width: 80,
ignoreOnSubmit: true
}, timeConfig)];
me.callParent();
},
afterRender: function () {
var me = this;
if (me.hideData) {
me.lookupReference('dateField').hide();
}
me.callParent();
},
getValue: function () {
var me = this,
dateValue = me.getDate(),
timeValue = me.getTime();
if (dateValue != null && timeValue != null) {
dateValue = Ext.Date.add(dateValue, Ext.Date.MILLI, timeValue);
}
return this.convertToUTC(dateValue);
},
setValue: function (value) {
var me = this;
if (value == null) {
return;
}
if (Ext.isString(value)) {
value = Ext.Date.parse(value, 'c');
}
// Debug
console.log('Parsed Date: ' + value);
if (value != null && Ext.isDate(value)) {
var timeDate = Ext.clone(value);
timeDate.setFullYear(1970, 0, 1);
timeDate = me.convertFromUTC(timeDate);
me.lookupReference('dateField').setValue(
Ext.Date.add(value, Ext.Date.MILLI, timeDate.getTime()));
me.lookupReference('timeField').setValue(timeDate);
}
},
getInputId: function () {
return null;
},
getTime: function () {
var me = this,
timeValue = me.lookupReference('timeField').getValue();
timeValue.setFullYear(1970, 0, 1);
timeValue = this.convertToUTC(timeValue, this.getDate());
return timeValue.getTime();
},
getDate: function () {
return this.lookupReference('dateField').getValue();
},
setDate: function (value) {
this.lookupReference('dateField').setValue(value);
},
convertToUTC: function (date, dateOffset) {
if (dateOffset == null) {
dateOffset = date;
}
if (this.utcValue) {
return Ext.Date.subtract(date, Ext.Date.MINUTE, dateOffset.getTimezoneOffset());
}
return date;
},
convertFromUTC: function (date, dateOffset) {
if (dateOffset == null) {
dateOffset = date;
}
if (this.utcValue) {
return Ext.Date.add(date, Ext.Date.MINUTE, dateOffset.getTimezoneOffset());
}
return date;
}
});
//////////////////////////////////////////////////////////
// Views
//////////////////////////////////////////////////////////
Ext.define('DateTime.view.MainView', {
extend: 'Ext.panel.Panel',
xtype: 'mainView',
alias: 'widget.mainview',
mixins: {
utils: 'DateTime.mixin.CommonUtils'
},
title: 'Date-Time Field Example',
referenceHolder: true,
layout: {
type: 'border',
//padding: 5
},
initComponent: function () {
var me = this;
me.items = [{
region: 'north',
xtype: 'grid',
itemId: 'timestampList',
store: Ext.create('DateTime.store.Timestamp', {
data : timestampData
}),
cls: 'timestamp-list',
multiSelect: false,
hideHeaders : true,
viewConfig: {
emptyText: 'No images to display'
},
columns: [{
dataIndex: 'timestamp',
text: 'Timestamp',
flex: 1
}]
}, {
region: 'center',
xtype: 'panel',
layout: {
type: 'vbox'
},
bodyPadding: 8,
items: [{
xtype: 'container',
layout: 'hbox',
margin: '8 0 0 0',
items: [{
xtype: 'textfield',
reference: 'txtTimestampIn',
fieldLabel: 'Timestamp In'
}, {
xtype: 'button',
itemId: 'btnSetTime',
text: 'Set Time',
margin: '0 0 0 12'
}]
}, {
xtype: 'container',
layout: 'hbox',
margin: '8 0 0 0',
items: [{
xtype: 'datetimefield',
name: 'startDate',
itemId: 'startDate',
reference: 'startDate',
dateConfig: {
fieldLabel: 'Start'
},
listeners: {
afterrender: {
fn: me.dateTimeField_onComplete,
scope: me
}
}
}, {
xtype: 'button',
itemId: 'btnExportTime',
text: 'Get Time',
margin: '0 0 0 12'
}]
}, {
xtype: 'textfield',
reference: 'txtTimestampOut',
fieldLabel: 'Timestamp Out',
margin: '8 0 0 0'
}]
}],
me.callParent();
},
dateTimeField_onComplete: function (field, eOpts) {
var me = this,
timestamp = timestampData[0][0];
field.setValue(timestamp);
}
});
////////////////////////////////////////////////////////////
// Controllers
////////////////////////////////////////////////////////////
Ext.define('DateTime.controller.MainController', {
extend: 'Ext.app.Controller',
views: ['DateTime.view.MainView'],
mixins: {
utils: 'DateTime.mixin.CommonUtils'
},
refs: [{
ref: 'mainView',
selector: 'mainView'
}],
init: function () {
var me = this;
me.control({
'#startDate': {
dateFieldChange: me.handleDateChange
},
'#btnSetTime': {
click: me.handleSetTime
},
'#btnExportTime': {
click: me.handleExportTime
},
'#timestampList' : {
selectionchange: me.handleChangeTimestamp
}
});
},
handleDateChange: function (field, newValue, oldValue) {
// Do nothing...
},
handleSetTime: function (button, e, eOpts) {
var me = this,
view = me.getMainView(),
timestampIn = view.lookupReference('txtTimestampIn');
view.lookupReference('startDate').setValue(timestampIn.getValue());
me.handleExportTime();
},
handleExportTime: function (button, e, eOpts) {
var me = this,
toISOStr = me.mixins.utils.dateToISOString,
view = me.getMainView(),
timestampIn = view.lookupReference('txtTimestampOut');
timestampIn.setValue(toISOStr(view.lookupReference('startDate').getValue()));
},
handleChangeTimestamp: function(grid, selected, eOpts) {
var me = this,
view = me.getMainView(),
timestamp = selected[0].data.timestamp;
view.lookupReference('txtTimestampIn').setValue(timestamp);
me.handleSetTime();
}
});
//////////////////////////////////////////////////////////
// Applications
//////////////////////////////////////////////////////////
Ext.define('DateTime.app.DateTimeApp', {
extend: 'Ext.app.Application',
name: 'DateTimeApp',
controllers: ['DateTime.controller.MainController'],
launch: function () {
Ext.create('Ext.Viewport', {
layout: 'fit',
flex: 1,
items: [{
xtype: 'mainview'
}]
});
}
});
//////////////////////////////////////////////////////////
// Startup
//////////////////////////////////////////////////////////
Ext.onReady(function () {
Ext.application('DateTime.app.DateTimeApp');
});
Upvotes: 0
Views: 4279
Reputation: 48630
Looks like I figured it out, although there is still an issue. The selected time does not appear in the dropdown... It just does not appear which is very strange. If anyone has a suggestion, comment below, or add your own answer.
Anyways, If anyone needs a UTC date-time field, here it is. I cleaned it up a bit. I completely refactored the code from my question so that timezone information is irrelevant. Date are explicitly converted from UTC to local and vice versa. This way, there is no need to calculate anything.
Ext.define('DateTime.components.DateTimeField', {
extend: 'Ext.container.Container',
mixins: ['Ext.form.field.Field'],
alias: 'widget.datetimefield',
config: {
dateConfig: {},
timeConfig: {},
utcValue: true,
hideDate: false
},
referenceHolder: true,
layout: {
type: 'hbox'
},
initComponent: function () {
var me = this,
today = new Date(),
dateConfig = me.dateConfig,
timeConfig = me.timeConfig;
me.items = [Ext.apply({
xtype: 'datefield',
reference: 'dateField',
fieldLabel: 'Date',
value: today,
listeners: {
change: function (field, newValue, oldValue) {
me.fireEvent('dateFieldChange', field, newValue, oldValue);
}
}
}, dateConfig),
Ext.apply({
xtype: 'timefield',
reference: 'timeField',
format: 'H:i',
value: '00:00',
maxValue: '24:00',
increment: 15,
padding: '0 0 0 10',
width: 80
}, timeConfig)];
me.callParent();
},
afterRender: function () {
var me = this;
if (me.hideDate) {
me.getDateField().hide();
}
me.callParent();
},
getValue: function () {
var me = this;
var dateTime = me.combineDateTime(me.getDate(), me.getTime());
if (me.utcValue) {
return me.localToUtc(dateTime);
}
return dateTime;
},
setValue: function(value) {
var me = this;
if (value == null) {
return;
}
if (Ext.isString(value)) {
value = Ext.Date.parse(value, 'c');
}
if (value != null && Ext.isDate(value)) {
var timeDate = Ext.clone(value);
if (me.utcValue) {
timeDate = this.utcToLocal(timeDate);
}
me.setDate(timeDate);
me.setTime(timeDate);
}
},
getTimeField : function() {
return this.lookupReference('timeField');
},
getDateField : function() {
return this.lookupReference('dateField');
},
getTime: function() {
return this.getTimeField().getValue();
},
setTime: function(value) {
this.getTimeField().setValue(value);
},
getDate: function() {
return this.getDateField().getValue();
},
setDate: function(value) {
this.getDateField().setValue(value);
},
combineDateTime : function(date, time) {
var year = date.getFullYear();
var month = date.getMonth();
var day = date.getDate();
var hour = time.getHours();
var minute = time.getMinutes();
var second = time.getSeconds();
return new Date(year, month, day, hour, minute, second, 0);
},
utcToLocal : function(utcDate) {
var year = utcDate.getUTCFullYear();
var month = utcDate.getUTCMonth();
var day = utcDate.getUTCDate();
var hour = utcDate.getUTCHours();
var minute = utcDate.getUTCMinutes();
var second = utcDate.getUTCSeconds();
return new Date(year, month, day, hour, minute, second, 0);
},
localToUtc : function(localDate) {
var year = localDate.getFullYear();
var month = localDate.getMonth();
var day = localDate.getDate();
var hour = localDate.getHours();
var minute = localDate.getMinutes();
var second = localDate.getSeconds();
return new Date(Date.UTC(year, month, day, hour, minute, second, 0));
}
});
Upvotes: 1