Microsoft InfoPath 2010
The official blog of the Microsoft InfoPath team

January, 2005

  • Microsoft InfoPath 2010

    Date Calculations in InfoPath

    • 8 Comments

    The SP1 update of InfoPath 2003 added calculation support – the value of a node can be set to the result of an XPath expression. This makes it possible to avoid writing code (script or managed) in many InfoPath forms. Date calculations, however, still require knuckling down and writing old fashioned procedural code.

     

    InfoPath stores dates according to the W3C XML Schema standard, which in turn uses a variant of ISO 8601 dates. The XPath expression language, however, has no special support for date types – just strings, numbers, Booleans, node-sets and fragments. This means that while you can manipulate dates as strings – you can’t do calculations with them.

     

    Before we dive into some sample code, though, a few notes:

     

    You can do date comparisons with XPath! The date format is “yyyy-mm-dd” – always 4-2-2 – which means you can do lexical (“string”) comparisons on two dates and determine ordering and equality.

     

    As a guiding rule, you should be as paranoid with date calculations as you are with financial calculations. Identify and test your edge cases thoroughly, and make sure your code matches cultural interpretations, not code convenience. For example, if you compare two dates the context and desired result matters. The relationship between a duration in days and an age in years is not simply 1/365 (or 1/365.25, or … ) – the convention for age in most cultures is “has the person had a birthday yet?” so you’d better make sure the code matches. Who wants to miss their birthday?

     

    A good rule of software development is that if you have to think too much about a problem you’re writing too much code, and the more code you write the more likely you are to have bugs. So the moral of this story is: make someone else do all the work.

     

    The general pattern for dealing with date calculations in InfoPath is to use an existing library. The two handy libraries for this are the Windows Scripting engine and the .NET Framework. Since we have a lot of script examples on this blog let’s use .NET this time.

     

    The .NET Framework has a DateTime struct type and if you look in MSDN you’ll find it has plenty of methods and operator overloads for doing calculations such as adding days and computing TimeSpans. Looks good – I bet the .NET people know what they’re doing.

     

    So basically we just want to convert an XML date into a DateTime, do some stuff with it, then convert back.

     

    Here are the functions you need:

     

           private static DateTime Iso8601ToDate( string iso8601Date )

           {

                  if( iso8601Date == null )

                         throw new ArgumentNullException( "iso8601Date" );

     

                  return DateTime.ParseExact( iso8601Date, "yyyy-MM-dd", null );

           }

     

           private static string DateToIso8601( DateTime dateTime )

           {

                  return dateTime.ToString( "yyyy-MM-dd" );

           }

     

    Wow – after that preamble I bet that was a bit of a let down!

     

    Now let’s use it. I built a simple calculation form that looks like this:

     

    Date Calculation Form Screenshot

     

    The button handlers look like this:

     

           [InfoPathEventHandler(MatchPath="date1_add1", EventType=InfoPathEventType.OnClick)]

           public void date1_add1_OnClick(DocActionEvent e)

           {

                  IXMLDOMNode dateNode = thisXDocument.DOM.selectSingleNode( "/my:myFields/my:Date1" );

     

                  try

                  {

                         DateTime dt = Iso8601ToDate( dateNode.text );

                         dt = dt.AddDays( 1 );

                         dateNode.text = DateToIso8601( dt );

                  }

                  catch( FormatException ) {}

           }

     

    Then I added OnAfterChange handlers for the date fields which call a sync method:

     

           [InfoPathEventHandler(MatchPath="/my:myFields/my:Date2", EventType=InfoPathEventType.OnAfterChange)]

           public void Date2_OnAfterChange(DataDOMEvent e)

           {

                  if (e.IsUndoRedo)

                         return;

     

                  SyncDifference();

           }

     

           private void SyncDifference()

           {

                  IXMLDOMNode date1Node = thisXDocument.DOM.selectSingleNode( "/my:myFields/my:Date1" );

                  IXMLDOMNode date2Node = thisXDocument.DOM.selectSingleNode( "/my:myFields/my:Date2" );

                  IXMLDOMNode diffNode  = thisXDocument.DOM.selectSingleNode( "/my:myFields/my:Difference" );

                  IXMLDOMNode ageNode   = thisXDocument.DOM.selectSingleNode( "/my:myFields/my:Age" );

     

                  if( date1Node != null && date2Node != null && diffNode != null )

                  {

                         try

                         {

                               DateTime dt1 = Iso8601ToDate( date1Node.text );

                               DateTime dt2 = Iso8601ToDate( date2Node.text );

     

                               TimeSpan ts = dt2 - dt1;

                               diffNode.text = ts.Days.ToString();

                         }

                         catch( FormatException ) {}

                  }

           }

     

    You might notice that this is computing the difference in days. The TimeSpan structure represents an interval of time, and days are the maximum granularity that a pure duration can have – month and year durations require a fixed point in time to calculate from;  even weeks can be ambiguous – is that whole weeks or calendar-weeks-spanned? And whose calendar in the first place?

     

    So how do you go from two DateTime structures to an age? The old fashioned way – “have I had a birthday yet this year?”

     

           int ageInYears = dt2.Year - dt1.Year;

           if( ( dt2.Month < dt1.Month ) ||

               ( dt2.Month == dt1.Month && dt2.Day < dt1.Day ) )

           {

                  ageInYears--;

           }

           ageNode.text = ageInYears.ToString();

     

    Time calculations are even more fun. No-one mention leap seconds and we’ll get by just fine.

     

    (Update 1/25/05 @ 11:30 AM PST - having a problem uploading the screenshot to our images site. We'll fix that ASAP.)

    (Update 1/25/05 @ 11:50 AM PST - Fixed!)

  • Microsoft InfoPath 2010

    Why doesn't InfoPath have a Password control?

    • 1 Comments

    Q: I want to create a control for entering passwords in an InfoPath form. How do I do this?

    A: InfoPath cannot guarantee the security and privacy of text entered into a password control, so it does not contain such a control.

    But Why?

    A typical password control contains features that make it hard for anyone but the user typing the password to discover what that text is. Password controls may also interact with a secure server so that the password is not sent in clear text, but is instead encrypted. Further, copying the text from a password control is not allowed.

    Because InfoPath allows files to be stored to a user’s local file system, InfoPath will the store the value typed into a field along with the XML. Additionally, InfoPath does not perform encryption of network traffic, meaning field values used as passwords will always be sent in clear text.

    But I still want a password control

    For the above reasons, we discourage using the contents of a field in an InfoPath form for entering and submitting passwords. Additionally, modifying the files used to create an InfoPath view to use the standard HTML password control (e.g. <INPUT type=”password” ID=”MyPassword”>), will not work in InfoPath. The nearest behavior that can be achieved is to format an InfoPath Text Box control to use a symbol font set, such as WebDings or WingDings so that the text being entered cannot easily be read as it is being typed.

Page 1 of 1 (2 items)