ASP.NET Outlook-Like Time Field

View demo project

Introduction

The IT side of my company is almost completely service based, mostly involving on-site technicians. We have an internal e-Business portal that we use to track services rendered to the clients. The complaint I received most often from our technicians was how annoying it was to enter the time in and out for service calls. I was presenting three dropdowns for each time field: one for hour, minute, and AM/PM. I had heard enough complaining; it was time to do something. Almost all of the time pickers I found on the net were either based on the same concept or a textbox with a masked input. I found these to be equally annoying because moving from field to field was sometimes cumbersome and input was often too restrictive.

One of my favorite Outlook features is the time picker used in appointments and tasks. It is a textbox that applies logic when it loses focus. It takes just about anything you can throw at it as input and computes a time in hh:mm tt format (in other words, 3:45 PM). I decided to duplicate this behavior and encompass it in an ASP.NET 2.0 Web control. The standard TextBox control provided 99% of what I needed to accomplish, so I derived my class from this one and started coding!

Background

It was decided that the control had to accept any of the following inputs:

  • 3:45 PM
  • 1:45a
  • 230a
  • 545p
  • 1843
  • 23
  • 21:30

It took me two attempts to get it right. My first attempt involved parsing the input for the location of the colon, reading each side into hours and minutes respectively and then searching for an 'a' or 'p' to indicate the time of the day. However, this proved to be troublesome when the input was not exactly as expected, like when a decimal was used instead, and so forth.

My final implementation handles this by splitting the text input into two components: a numeric component and a text component. I accomplish this with the following client-side JavaScript (the complete JavaScript source code is found in App_GlobalResources\OutlookTimeField.txt):

Client-Side Implementation

function UpdateTime(sender) {
   ...
   var numericPart = '';
   var characterPart = '';
   var i;
   var current;

   // Break the text input into numeric and
   // character parts for easier parsing
   for(i = 0; i < text.length; i++) {
      current = text.charAt(i);

      if(IsNumeric(current))
         numericPart = numericPart + current;

      if(IsCharacter(current))
         characterPart = characterPart + current;
   }
   ...
}

function IsNumeric(text) {
   var validChars = '0123456789';
   return (validChars.indexOf(text) > -1)
}

function IsCharacter(text) {
   var validChars = 
     'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
   return (validChars.indexOf(text) > -1)
}

After the text is split, figuring out what the user intended is relatively simple. First, if the characterPart contains an A or an a, treat it as AM (unless the hour is greater then 12); otherwise, default to PM. Second, depending on how many characters were entered in the numericPart, split the hours and minutes according to the following algorithm:

...
if(numericPart.length >= 4) {
   hour = numericPart.substring(0, 2);
   minute = numericPart.substring(2, 4);
} else if(numericPart.length == 3) {
   hour = numericPart.substring(0, 1);
   minute = numericPart.substring(1, 3);
} else if(numericPart.length == 2) {
   hour = numericPart.substring(0, 2);
   minute = '00';
} else if(numericPart.length == 1) {
   hour = numericPart.substring(0, 1);
   minute = '00';
} else {
   // Just use the current hour
   var d = new Date();
   hour = d.getHours();

   minute = '00';
}
...

Next, apply some 24-hour logic:

...
if(hour > 12) {
   if(hour <= 24) {
      hour -= 12;
      dayPart = 'PM';
   } else {
      // If the hour is still > 12 then the 
      // user has inputed something that doesn't
      // exist, so just use current hour

      hour = (new Date()).getHours();

      if(hour > 12) {
         hour -= 12;
         dayPart = 'PM';
      } else {
         dayPart = 'AM';
      }
   }
}

if(hour == 0) {
   hour = 12;
   dayPart = 'AM';
}
...

All that's left on the client side is updating the sending textbox's value:

sender.value = hour + ':' + minute + ' ' + dayPart;

Server-Side implementation

There really isn't much to the server-side code. First, override the Render method to add an onBlur event to the TextBox:

protected override void Render(HtmlTextWriter writer) {
   writer.AddAttribute(BLUR_ATTRIBUTE, "UpdateTime(this);");
   base.Render(writer);
}

Second, override the OnPreRender method to inject the client-side script:

protected override void OnPreRender(EventArgs e) {
   base.OnPreRender(e);
   if (!Page.ClientScript.IsClientScriptBlockRegistered(this.GetType(),
                                                        SCRIPT_KEY)) {
       Page.ClientScript.RegisterClientScriptBlock(this.GetType(),
         SCRIPT_KEY, Resources.ControlResources.OutlookTimeField, true);
   }
}

And last, create a new property that returns a TimeSpan structure with the corresponding time:

public TimeSpan Time
{
   get {
      if (string.IsNullOrEmpty(Text))
         return TimeSpan.Zero;
      else
         return TimeSpanHelper.GetTimeSpan(Text);
   }
   set { Text = TimeSpanHelper.ConvertTimeSpanToString(value); }
}

I won't go into the code that converts to and from the TimeSpan structure because it's relatively straightforward. You can find my implementation in App_Code\TimeSpanHelper.cs.

Using the Code

Use it the same as using any other Web control, but for the sake of being thorough:

...
<%@ Register TagPrefix="mbc"
             Namespace="Mbccs.WebControls" %>
...
<mbc:OutlookTimeField runat="server" ID="startTime"
                      AutoCompleteType="None" />
...

Points of Interest

  • Set the AutoCompleteType property of the OutlookTimeField to None to prevent any browser annoyances.
  • I have not implemented this Web control in a standalone DLL for simplicity and because there are tons of articles on doing this.
  • I am purposefully throwing a FormatException (this is not a bug) if you type in bogus data (that the control does not re-format) and attempt to post back. If you want to prevent this, you should:
    1. add a RegularExpressionValidator using the regular expression found in App_Code\TimeSpanHelper.cs or
    2. catch the exception and apply some custom logic—perhaps using a default date or the current date.


About the Author

Steven Berkovitz

Steven is VP Development at MBC Computer Solutions Ltd. (http://www.mbccs.com), a Markham based company specializing in e-Business Application Development, e-Store Solutions, Managed Co-Location and Proactive IT services. Steven has over 10 years experience in software and hardware design and is experienced with a large array of platforms, technologies and languages. Steven also has 3 years experience in QA software development for the accounting industry.

Downloads

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • Some of the statistics about the growth of enterprise mobility are surprising, not the least of which is the 4X increase in mobile malware from 2013 to 2014. Many employees today not only expect to be able to use their own personal devices at work, but will even contravene policies that restrict personal device use for work.  IT must find the right approach that balances the benefits of mobility with the risks involved.  This on-demand webcast offers steps toward developing a winning mobile strategy, and …

Most Popular Programming Stories

More for Developers

RSS Feeds