Date & Time Values and Offsets

The Date Picker, Date Time Picker, and Time Picker controls have the capability to output an ISO 8601 string value which is converted to a DateTime or DateTimeOffset object in a workflow (depending on the type of the workflow argument).

Example

Assume one is using a control on March 8 at 10AM in Florida during Eastern Standard Time:

  • Date Picker

    The Date Picker control should have Ignore Time set to true and will only output the date portion of the ISO 8601 string.

    vm.models output example: “2018-03-08”
    DateTime object will show: Mar 8, 2018 at midnight, no offset
    DateTimeOffset: same and likewise no offset
  • Date Time Picker

    vm.models output example: “2018-03-08T10:00:00.000-05:00”
    DateTime object will show: Mar 8, 2018 10 AM, offset is lost
    DateTimeOffset: same but will have Offset property set to 5 hours
  • Time Picker

    vm.models output example: 2018-03-08T10:00:00.000-05:00”
    DateTime object will show: same as Date Time Picker, but typically you will ignore the date and use only a formatted String as the time
    DateTimeOffset: same but will have Offset property set to 5 hours

When binding vm.models values for these controls directly to an entity argument property, if the associated database Datetime column is not nullable, the entity data type is DateTime instead of Nullable<DateTime>. This means a direct model map to an entity argument property must have Required set to true so that client-side validation will prevent submittal of a value if it is null (no value). Failure to do so would cause an error in the workflow and a subsequent abort of the workflow.

If the entity property is Nullable<DateTime>, you still might not want to store a null value in certain circumstances (since it might be invalid for the situation). In that case you could mark it required in the client, or conditionally in the workflow, check your argument with the property myDate.HasValue and create a descriptive server validation error if it is false.

Known Limitations for DateTime Localization

Relative and Fixed Date/Time

DateTime objects are used in two different ways.

  1. The first way is where the time in any time zone is relative to a reference time zone.

    Midnight in UTC (Greenwich Mean Time) is 7 PM on the previous day in Florida (except when EDT changes it to 8 PM). They represent the same moment in time and thus ...

    2018-03-09T00:00:00ZZ,
    2018-03-08T19:00:00.000-05:00 and
    2018-03-08T16:00:00.000-08:00

    ... are the same moment in time, even though they are different dates and times.

  2. The second is where the time needs to be fixed for a date.

    A birth date, marriage date, scheduled date and time, a historical date and time might need to be fixed to the time zone they were created in because one needs to know the date and time in that time zone even if you are in another time zone. You do not want a birth date to change to a different date in your local time zone, because it is a legal date on a document and the birth occurred in a specific time zone.

    The Date Picker object has an Ignore Time property which should always be set to true to mitigate this problem.

DateTime Object in a Workflow

When using a DateTime object in a workflow, there is a known limitation for localization. If the server is in a different time zone, the offset and time both get shifted to the server time zone when the client Date Time Picker value is sent to the server.

Example

Date input is 2018-03-08T10:00:00.000-05:00. This is 10 AM in EST.

If the server is in PST, it gets translated to 2018-03-08T07:00:00.000-08:00 which is 7 AM in PST. This is the same moment in time.

If it is bound to a DateTime object in a workflow, the offset is dropped, so it will be stored in the database as 7 AM.

Now, if it is used in a form, it is brought back from the database as 7AM in the workflow and a DateTime object is created from that.

Since the server (workflow is running on the server) is in PST, when sent to the client, it will have an offset added to it to recreate the original server string 2018-03-08T07:00:00.000-08:00.

If it is bound to a Date Time Control in the client, because the client is in EST, it will be translated back to 10 AM in EST. This of course is desirable. Of course, if the client is in some other time zone, it will appear as the local time in their time zone.

All good so far. However, there are several problems:

  • One is that when the object is used in a workflow or any other server side process, it is not known where the object was created (where the browser was located), so if the date and time are the 2nd type of DateTime object detailed above, you have a problem.

  • And, if the date time is used on a different server that is not in the same time zone as the server that stored it, you now cannot get back the original date and time in any time zone.

  • In Azure environments you may not only not know where the server is located, it may change the next time you use the form.

DateTimeOffset is the way to save a value for the case where the time zone (offset) is important; however, currently no entities support DateTimeOffset. A DateTimeOffset preserves the time zone offset, and when stored in a DateTimeOffset column in a database, and a server uses the return value, it is weighted by the time zone offset it is in.

  1. One way to store a DateTimeOffset argument value is to convert it to a String and store it in a String column, and conversely parse it as a DateTimeOffset on its return with DateTimeOffset.Parse (using an Invoke activity) and then assign it to an argument. This technique can recreate the original DateTimeOffset on the same server.

  2. A second alternative is to use the properties of the DateTimeOffset object, “DateTime” and “Offset” to store these in two separate columns. Offset as a TimeSpan would have to be converted to a String (ToString()), but DateTime could be stored either in a database DateTime column or String column. When these return from the database, you would have to use the information to reverse the process to create a new DateTimeOffset object. Use Parse on each (if both are a String) and then use "new DateTimeOffset(parsedDateTime, parsedTimespan)" with an Invoke activity to recreate the argument and model value sent back to the control. This technique is only useful if the server now happens to be in a different time zone than the original server because the original server DateTimeOffset can be recreated.

  3. And a third alternative is to bind the component value to a String and simply treat it as a String always (making it not very useful in a workflow if it needs to be compared to other dates and times. It would have to be converted to a DateTime first). However, of the 3 alternatives, this is the only way to preserve the original time zone offset because the string will not be altered anywhere on the path from the client to the server to the database.

PDF Generation

At this time there is no resolution for PDF generation when the server is in a different time zone than the client. That is because PDF generation must be done on the server in a simulated browser environment. The simulated browser picks up the time locale of the server, and thus translates dates to local time (the server time). This makes a PDF where a Date Time Picker has been used to select the time have a different time than the client had. Since this is under the control of 3rd party software, there may be no fix possible. This is being looked into.