Recently, one of my clients needed to have a date/time field to update/create their records. So I googled a bit and found a great sample/extension for my existing project.
Yes, it is Ext.ux.form.field.DateTime. However the existing extension does not have validation function so I just made a few changes on it.
/** * @auther Elvis Hsu * @class Ext.ux.form.field.DateTime * @extends Ext.form.FieldContainer * * inspired by http://www.sencha.com/forum/showthread.php?134345-Ext.ux.form.field.DateTime * This class is used for user input */ Ext.define('Ext.ux.form.field.DateTime', { extend:'Ext.form.FieldContainer', mixins: { field: 'Ext.form.field.Field' }, alias: 'widget.datetimefield', layout: 'hbox', height: 22, /** * set combine errors to true for showing errors */ combineErrors: true, /** * set msg target to side */ msgTarget :'side', /** * we want hbox layout */ layout: 'hbox', /** * set readonly to false */ readOnly: false, /** * @cfg {String} dateFormat * Convenience config for specifying the format of the date portion. * This value is overridden if format is specified in the dateConfig. * The default is 'Y-m-d' */ dateFormat: 'Y-m-d', /** * @cfg {String} timeFormat * Convenience config for specifying the format of the time portion. * This value is overridden if format is specified in the timeConfig. * The default is 'H:i:s' */ timeFormat: 'H:i:s', /** * the datefield configurations */ dateConfig:{}, /** * the time field configurations */ timeConfig:{}, /** * The actual date value */ dateValue: null, /** * @property dateField * @type Ext.form.field.Date */ dateField: null, /** * @property timeField * @type Ext.form.field.Time */ timeField: null, /** * initialising the components */ initComponent: function() { var me = this, key, tab; // set default to no items me.items = me.items || []; me.dateField = Ext.create('Ext.form.field.Date', Ext.apply({ format:me.dateFormat, flex:1, isFormField:false, //exclude from field query's submitValue:false }, me.dateConfig)); me.items.push(me.dateField); me.timeField = Ext.create('Ext.form.field.Time', Ext.apply({ format:me.timeFormat, flex:1, isFormField:false, //exclude from field query's submitValue:false }, me.timeConfig)); me.items.push(me.timeField); for (var i = 0; i < me.items.length; i++) { me.items[i].on('focus', Ext.bind(me.onItemFocus, me)); me.items[i].on('blur', Ext.bind(me.onItemBlur, me)); me.items[i].on('specialkey', function(field, event){ key = event.getKey(); tab = (key == event.TAB); if (tab && me.focussedItem == me.dateField) { event.stopEvent(); me.timeField.focus(); return; } me.fireEvent('specialkey', field, event); }); } me.callParent(); // this dummy is necessary because Ext.Editor will not check whether an inputEl is present or not me.inputEl = { dom:{}, swallowEvent:function(){} }; me.initField(); }, /** * Try to focus this component. * @param {Boolean} [selectText] If applicable, true to also select the text in this component * @param {Boolean/Number} [delay] Delay the focus this number of milliseconds (true for 10 milliseconds). * @return {Ext.Component} this */ focus:function(){ this.callParent(); this.dateField.focus(); }, onItemFocus:function(item){ if (this.blurTask){ this.blurTask.cancel(); } this.focussedItem = item; }, onItemBlur:function(item){ var me = this; if (item != me.focussedItem){ return; } // 100ms to focus a new item that belongs to us, otherwise we will assume the user left the field me.blurTask = new Ext.util.DelayedTask(function(){ me.fireEvent('blur', me); }); me.blurTask.delay(100); }, /** * Returns the current data value of the field. The type of value returned is particular to the type of the * particular field (e.g. a Date object for {@link Ext.form.field.Date}). * @return {Object} value The field value */ getValue: function() { var me = this, value = null, date = me.dateField.getSubmitValue(), time = me.timeField.getSubmitValue(); if(date){ if(time){ var format = me.getFormat() value = Ext.Date.parse(date + ' ' + time,format) }else{ value = me.dateField.getValue() } } return value }, /** * Sets a data value into the field and runs the change detection and validation. * @param {Object} value The value to set * @return {Ext.form.field.Field} this */ setValue: function(value){ if (Ext.isString(value)){ value = Ext.Date.parse(value, this.getFormat()); //this.dateTimeFormat } this.dateField.setValue(value); this.timeField.setValue(value); }, /** * Resets the field's {@link #originalValue} property so it matches the current {@link #getValue value}. This is * called by {@link Ext.form.Basic}.{@link Ext.form.Basic#setValues setValues} if the form's * {@link Ext.form.Basic#trackResetOnLoad trackResetOnLoad} property is set to true. */ resetOriginalValue: function() { var me = this; me.originalValue = me.getValue(); me.dateField.resetOriginalValue(); me.timeField.resetOriginalValue(); me.checkDirty(); }, /** * Returns the value that would be included in a standard form submit for this field. This will be combined with the * field's name to form a name=value pair in the {@link #getSubmitData submitted parameters}. If an empty string is * returned then just the name= will be submitted; if null is returned then nothing will be submitted. * * Note that the value returned will have been {@link #processRawValue processed} but may or may not have been * successfully {@link #validate validated}. * * @return {String} The value to be submitted, or null. */ getSubmitValue: function(){ var me = this, format = me.getFormat(), value = me.getValue(); return value ? Ext.Date.format(value, format) : null; }, /** * Returns the parameter(s) that would be included in a standard form submit for this field. Typically this will be * an object with a single name-value pair, the name being this field's {@link #getName name} and the value being * its current stringified value. More advanced field implementations may return more than one name-value pair. * * Note that the values returned from this method are not guaranteed to have been successfully {@link #validate * validated}. * * @return {Object} A mapping of submit parameter names to values; each value should be a string, or an array of * strings if that particular name has multiple values. It can also return null if there are no parameters to be * submitted. */ getSubmitData: function(){ var me = this, data = null; if (!me.disabled && me.submitValue && !me.isFileUpload()) { data = {}; data[me.getName()] = '' + me.getSubmitValue(); } return data; }, /** * Gets the current date time field format */ getFormat: function(){ var me = this; return (me.dateField.submitFormat || me.dateField.format) + " " + (me.timeField.submitFormat || me.timeField.format) }, /** * Replaces any existing {@link #minValue} with the new value and refreshes the Date picker. * @param {Date} value The minimum date that can be selected */ setMinValue: function(date){ var me = this; me.dateField.setMinValue(date); }, /** * Replaces any existing {@link #maxValue} with the new value and refreshes the Date picker. * @param {Date} value The maximum date that can be selected */ setMaxValue: function(date){ var me = this; me.dateField.setMaxValue(date); }, validate: function(){ var me = this; return me.dateField.validate(); } });