ExtJS:如何在自定义 DateTimeField 中使用 UTC 时间

ExtJS: How to utilize UTC time in custom DateTimeField

我正在尝试将 UTC 时间戳转换为 UTC 日期,修改这些日期,并 return selected date/time 的 UTC 时间戳。由于 Ext.form.field.DateExt.form.field.Time 字段在内部使用本地时间,我必须将时间增加 5 小时并确保日期保持不变,要检索时间戳,我必须反转过程并获得ISO 时间戳(不含毫秒)。

一切似乎都正常,但是当我 select 2015-01-01T01:15:00Z 日期字段设置为第二天时(01/02/2015;参见 图 #1).我不确定我在哪里没有正确转换日期?


图 #1: 日期设置为第二天。


编辑

经过一番调整后,我的日期似乎是正确的,但现在时间字段出现问题(参见 图 #2)。看起来设置的值已从时间下拉列表中删除。这非常令人困惑。

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));
    }
    // ...
}

图 #2: 时间字段验证失败。


您可以在以下站点访问以下代码的演示:

DateTime.components.DateTimeField

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');
});

看来我想通了,虽然还有一个问题。选择的时间没有出现在下拉列表中......它只是没有出现这很奇怪。如果有人有任何建议,请在下方发表评论,或添加您自己的答案。

无论如何,如果有人需要 UTC 日期时间字段,就在这里。我清理了一下。我完全重构了问题中的代码,因此时区信息无关紧要。日期明确地从 UTC 转换为本地时间,反之亦然。这样就不用计算了。

Ext.ux.form.field.DateTimeField

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

演示