diff --git a/etemplate/js/et2_date.js b/etemplate/js/et2_date.js
new file mode 100644
index 0000000000..e297e17c96
--- /dev/null
+++ b/etemplate/js/et2_date.js
@@ -0,0 +1,158 @@
+/**
+ * eGroupWare eTemplate2 - JS Date object
+ *
+ * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
+ * @package etemplate
+ * @subpackage api
+ * @link http://www.egroupware.org
+ * @author Nathan Gray
+ * @copyright Nathan Gray 2011
+ * @version $Id$
+ */
+
+"use strict";
+
+/*egw:uses
+ jquery.jquery;
+ et2_inputWidget;
+ et2_valueWidget;
+*/
+
+/**
+ * Class which implements the "date" XET-Tag
+ */
+var et2_date = et2_inputWidget.extend({
+
+ attributes: {
+ "value": {
+ "type": "any"
+ },
+ "type": {
+ "ignore": false
+ }
+ },
+
+ /**
+ * Internal container for working easily with dates
+ */
+ date: new Date(),
+
+ init: function(_parent) {
+ this._super.apply(this, arguments);
+
+ this.input = null;
+
+ this.createInputWidget();
+ },
+
+ createInputWidget: function() {
+ this.input = $j(document.createElement("input"));
+
+ var type="text";
+ switch(this.type) {
+ case "date":
+ type = "date";
+ break;
+ case "date-timeonly":
+ type = "time";
+ break;
+ case "date-time":
+ type = "datetime-local";
+ break;
+ }
+ this.input.attr("type", type).addClass("et2_date");
+
+ this.setDOMNode(this.input[0]);
+ },
+
+ set_type: function(_type) {
+ this.type = _type;
+ this.createInputWidget();
+ },
+
+ set_value: function(_value) {
+ if(typeof _value == 'string' && isNaN(_value)) {
+ _value = Date.parse(_value);
+ }
+ this.value = _value;
+
+ // JS dates use milliseconds
+ this.date.setTime(parseInt(_value)*1000);
+ var display = this.date.toString();
+
+ // TODO: Use user's preference, not browser's locale
+ switch(this.type) {
+ case "date":
+ display = this.date.toLocaleDateString();
+ break;
+ case "date-timeonly":
+ display = this.date.toLocaleTimeString();
+ break;
+ case "date-time":
+ display = this.date.toLocaleString();
+ break;
+ }
+ this._super.apply(this, [display]);
+ }
+});
+
+et2_register_widget(et2_date, ["date", "date-time", "date-timeonly"]);
+
+/**
+ * et2_date_ro is the dummy readonly implementation of the date widget.
+ */
+var et2_date_ro = et2_valueWidget.extend({
+
+ /**
+ * Ignore all more advanced attributes.
+ */
+ attributes: {
+ "value": {
+ "type": "integer"
+ },
+ "type": {
+ "ignore": false
+ }
+ },
+
+ /**
+ * Internal container for working easily with dates
+ */
+ date: new Date(),
+
+ init: function() {
+ this._super.apply(this, arguments);
+
+ this.value = "";
+ this.span = $j(document.createElement("time"))
+ .addClass("et2_date_ro");
+
+ this.setDOMNode(this.span[0]);
+ },
+
+ set_value: function(_value) {
+ this.value = _value;
+
+ // JS dates use milliseconds
+ this.date.setTime(parseInt(_value)*1000);
+ var display = this.date.toString();
+
+ // TODO: Use user's preference, not browser's locale
+ switch(this.type) {
+ case "date":
+ display = this.date.toLocaleDateString();
+ break;
+ case "time":
+ display = this.date.toLocaleTimeString();
+ break;
+ case "date-time":
+ display = this.date.toLocaleString();
+ break;
+ }
+ this.span.attr("datetime", this.date.toUTCString()).text(display);
+ }
+
+});
+
+et2_register_widget(et2_date_ro, ["date_ro", "date-time_ro"]);
+
diff --git a/etemplate/js/et2_selectbox.js b/etemplate/js/et2_selectbox.js
index bbf4abcf22..faa5eb1096 100644
--- a/etemplate/js/et2_selectbox.js
+++ b/etemplate/js/et2_selectbox.js
@@ -151,7 +151,7 @@ var et2_selectbox = et2_inputWidget.extend({
et2_register_widget(et2_selectbox, ["menupopup", "listbox", "select-cat",
"select-account", "select-percent", 'select-priority', 'select-access',
'select-country', 'select-state', 'select-year', 'select-month',
- 'select-day', 'select-dow', 'select-hour', 'select-number', 'select-app',
+ 'select-day', 'select-dow', 'select-hour', 'date-houronly', 'select-number', 'select-app',
'select-lang', 'select-bool', 'select-timezone' ]);
/**
diff --git a/etemplate/js/etemplate2.js b/etemplate/js/etemplate2.js
index a2585c4e0c..16d43690bd 100644
--- a/etemplate/js/etemplate2.js
+++ b/etemplate/js/etemplate2.js
@@ -23,6 +23,7 @@
et2_selectbox;
et2_checkbox;
et2_radiobox;
+ et2_date;
et2_styles;
et2_html;
et2_tabs;
diff --git a/etemplate/js/test/et2_test_date.xet b/etemplate/js/test/et2_test_date.xet
new file mode 100644
index 0000000000..43a6c8d4ac
--- /dev/null
+++ b/etemplate/js/test/et2_test_date.xet
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/etemplate/js/test/et2_test_dates.json b/etemplate/js/test/et2_test_dates.json
new file mode 100644
index 0000000000..201258c179
--- /dev/null
+++ b/etemplate/js/test/et2_test_dates.json
@@ -0,0 +1,11 @@
+var d = new Date();
+var date = Math.round(d.valueOf() / 1000);
+var date_test_data = {
+ content: {
+ "date": date,
+ "date_time":date,
+ "time":date,
+ "ro_date": date,
+ "ro_date_time": date
+ }
+}
diff --git a/etemplate/js/test/test_xml.html b/etemplate/js/test/test_xml.html
index cc12436b16..56fb53b831 100644
--- a/etemplate/js/test/test_xml.html
+++ b/etemplate/js/test/test_xml.html
@@ -26,6 +26,7 @@
+
@@ -37,6 +38,7 @@
+