Exact Time Picker

Time picker field is always useful when authors need to manage the time separated from the date. Episerver stores dates as UTC and it dynamically displays the datetime of the content editor’s machine but what happen if time zone is also managed as a separated field. In this post I will show you how can we store a time field using the server local time.  

There is a common requirement from clients that want an event detail page where they define the start datetime, optionally the end datetime, and the timezone so clients know exactly when the event is happening. 

When you try to get the value programmatically, it’s a challenge to display the correct date and time to the end user.

As author, in order to be able to set the exact value that will be displayed to the user, we need to create a custom field that will be rendered as a time picker. 

1- Editor Descriptor
This class will let us define how a property would look like and be presented to editors. We need to create a custom editor descriptor for the exact time picker:

[EditorDescriptorRegistration(TargetType = typeof(DateTime?), UIHint = CmsUiHints.ExactTime)]
[EditorDescriptorRegistration(TargetType = typeof(DateTime), UIHint = CmsUiHints.ExactTime)]
public class UtcDateTimeEditorDescriptor : DateTimeEditorDescriptor
{
	public override void ModifyMetadata(ExtendedMetadata metadata, IEnumerable<Attribute> attributes)
	{
		ClientEditingClass = "foundation/editors/ExactTime";

		base.ModifyMetadata(metadata, attributes);
	}
}

We can have the UIHint in a constant to follow best practices:

public static class CmsUiHints
{
	public const string ExactTime = "ExactTime";
}

2- Dojo/Dijit widget
We must define a custom Dojo/Dijit widget referenced by code which will tell Episerver how this field should be rendered and presented data to the author as well as what data will it store.

define([
    "dojo/_base/array",
    "dojo/_base/connect",
    "dojo/_base/declare",
    "dojo/_base/lang",

    "dijit/_CssStateMixin",
    "dijit/_Widget",
    "dijit/_TemplatedMixin",
    "dijit/_WidgetsInTemplateMixin",
    "dijit/dijit", // loads the optimized dijit layer
    "epi/shell/widget/DateTimeSelectorDropDown",
    "epi/epi"

],
    function (
        array,
        connect,
        declare,
        lang,
        _CssStateMixin,
        _Widget,
        _TemplatedMixin,
        _WidgetsInTemplateMixin,
        dijit,
        DateTextBox,
        epi
    ) {

        return declare("foundation.editors.UTCDateTime", [_Widget, _TemplatedMixin, _WidgetsInTemplateMixin, _CssStateMixin], {

            templateString: "<div class=\"dijitInline\"> \
                            <input data-dojo-attach-point=\"dateTextBox\" data-dojo-type=\"dijit/form/TimeTextBox\"/> \
                        </div>",

            constructor: function () {

            },

            _onLoad: function () {
                
                this.inherited(arguments);

                var dd = new Date(this.value);

                if (dd != undefined) {

                    if (!isNaN(dd.getTime())) {

                        //Convert to local time
                        dd = new Date(dd.getTime() + (dd.getTimezoneOffset() * 60000));
                    }
                }
                var _this = this;

                this.dateTextBox.set('value', dd);

                dojo.connect(this.dateTextBox, "onChange", function (value) {

                    _this._setValue(value);

                });
            },
            // Setter for value property
            _setValueAttr: function (value) {

                this._setValue(value, true);

                this._onLoad();
            },

            onChange: function (value) {
                console.log(value);
                // Event that tells EPiServer when the widget's value has changed.
            },

            _getValueAttr: function () {

                var date = this.dateTextBox.get('value');

                if (date == undefined)
                    return date;

                if (isNaN(date.getTime()))
                    return date;

                //Convert to UTC time to make it locale independent
                date = new Date(date.getTime() - (date.getTimezoneOffset() * 60000));

                return date;
            },

            _setReadOnlyAttr: function (value) {
                this._set("readOnly", value);
            },

            _setValue: function (value) {
                if (this._started && epi.areEqual(this.value, value)) {
                    return;
                }

                // set value to this widget (and notify observers)
                this._set("value", value);
            }
        });
    });

This widget can be stored under your Website project under ClientResources/Scripts/ExactTime.js. Don’t forget to configure your module.config file

<?xml version="1.0" encoding="utf-8"?>
<module>
  <dojo>
    <paths>
      <add name="foundation" path="~/ClientResources/Scripts" />
    </paths>
  </dojo>
</module>

3- Field definition
Finally, we need to define a field that will use our custom editor descriptor and will store the exact time we need to display.

[Required]
[UIHint(CmsUiHints.ExactTime)]
[Display(Name = "Start Time", GroupName = SystemTabNames.Content, Order = 10)]
public virtual DateTime StartTime { get; set; }

That’s it, you will be able to get the exact time from Epi without taking care of the time zone and avoiding the UTC value.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: