Click here to Skip to main content
15,867,939 members
Articles / Web Development / HTML
Article

ASP.NET Outlook-like Time Field

Rate me:
Please Sign up or sign in to vote.
4.49/5 (17 votes)
10 Oct 2007CPOL3 min read 106.4K   699   55   24
An article on building a time field that mimics the behavior of the one found in Microsoft Outlook.
Image 1

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 drop-downs for each time field: one each 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, as 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 text box 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 (i.e. 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.

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 (complete JavaScript source code found in App_GlobalResources\OutlookTimeField.txt):

Client-side Implementation

JavaScript
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 characterPart contains an A or an a, treat it as AM (unless the hour is greater than 12). Otherwise, default to PM. Second, depending on how many characters were entered in numericPart, split the hours and minutes according to the following algorithm:

JavaScript
...
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:

JavaScript
...
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 text box'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:

C#
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:

C#
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:

C#
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, as it's relatively straightforward. You can find my implementation in App_Code\TimeSpanHelper.cs.

Using the Code

It's essentially the same as using any other web control, but for the sake of being thorough:

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

Points of Interest

  • Set the AutoCompleteType property of OutlookTimeField to None to prevent any browser annoyances.
  • I have not implemented this web control in a standalone DLL both 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 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.

Acknowledgements

Thank you to Alfredo Pinto for providing the port to VB.NET.

History

  • 11 December, 2005 -- Original version posted
  • 23 December, 2005 -- Article updated
  • 9 January, 2006 -- Article moved
  • 10 October, 2007 -- VB.NET source download and Acknowledgements section added

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Canada Canada
Steven is VP Development at MBC Computer Solutions Ltd. (http://www.mbccs.com), a Richmond Hill 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.

In his spare time, Steven enjoys a wide array of music, is an avid skier and enjoys spending time with friends.

Steven is the primary contributor of MBC's blog which can be read at http://blogs.mbccs.com/mbccomputersolutions

Comments and Discussions

 
QuestionAdditional js code after validation Pin
PandoT20-Jul-09 6:41
PandoT20-Jul-09 6:41 
AnswerRe: Additional js code after validation Pin
Steven Berkovitz20-Jul-09 6:42
Steven Berkovitz20-Jul-09 6:42 
AnswerRe: Additional js code after validation Pin
PandoT20-Jul-09 9:57
PandoT20-Jul-09 9:57 
GeneralRe: Additional js code after validation Pin
Steven Berkovitz20-Jul-09 9:58
Steven Berkovitz20-Jul-09 9:58 
GeneralCode not working in VS 2008 Pin
jruggs7-Mar-08 11:32
jruggs7-Mar-08 11:32 
GeneralRe: Code not working in VS 2008 Pin
Steven Berkovitz7-Mar-08 13:40
Steven Berkovitz7-Mar-08 13:40 
GeneralRe: Code not working in VS 2008 [modified] Pin
jruggs10-Mar-08 3:47
jruggs10-Mar-08 3:47 
GeneralRe: Code not working in VS 2008 - new clue Pin
jruggs10-Mar-08 6:00
jruggs10-Mar-08 6:00 
GeneralRe: Code not working in VS 2008 - new clue Pin
jruggs10-Mar-08 7:37
jruggs10-Mar-08 7:37 
GeneralPorting to VB Pin
Proteo528-Apr-07 10:12
Proteo528-Apr-07 10:12 
GeneralRe: Porting to VB Pin
Steven Berkovitz28-Apr-07 17:22
Steven Berkovitz28-Apr-07 17:22 
GeneralRe: Porting to VB Pin
Proteo523-May-07 13:30
Proteo523-May-07 13:30 
GeneralRe: Porting to VB Pin
mbruyns30-Jul-07 1:42
mbruyns30-Jul-07 1:42 
Hi
Has this VB code been posted anywhere? I would love to use it.

Thanks! Smile | :)
Generalalmost works Pin
Beskur9-Feb-06 16:17
Beskur9-Feb-06 16:17 
GeneralRe: almost works Pin
Steven Berkovitz10-Feb-06 3:57
Steven Berkovitz10-Feb-06 3:57 
GeneralIgnores Regional Settings Pin
Albanaco23-Dec-05 8:18
Albanaco23-Dec-05 8:18 
GeneralDiagrams Pin
valamas14-Dec-05 13:54
valamas14-Dec-05 13:54 
GeneralRe: Diagrams Pin
Steven Berkovitz14-Dec-05 13:56
Steven Berkovitz14-Dec-05 13:56 
GeneralNeeds Validation Pin
AbuseByUnkindPeople11-Dec-05 21:16
AbuseByUnkindPeople11-Dec-05 21:16 
GeneralRe: Needs Validation Pin
Steven Berkovitz12-Dec-05 3:24
Steven Berkovitz12-Dec-05 3:24 
GeneralRe: Needs Validation Pin
Albanaco23-Dec-05 8:21
Albanaco23-Dec-05 8:21 
GeneralRe: Needs Validation Pin
Steven Berkovitz23-Dec-05 8:32
Steven Berkovitz23-Dec-05 8:32 
GeneralRe: Needs Validation Pin
MKauffman12-Oct-07 8:15
MKauffman12-Oct-07 8:15 
GeneralRe: Needs Validation Pin
Steven Berkovitz12-Oct-07 8:19
Steven Berkovitz12-Oct-07 8:19 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.