Developer Story on “Date-Time”
Handling in Time Zone unaware
Scenario
Applies to:
Feature pack 1 for Duet Enterprise 1.0.
Summary
We often come across objects that do not understand time zone information. In other word these object treat time as absolute. For e.g. in ERP time recording objects would consider time as absolute. It would not have capability to consider 1AM-10AM (in local time) same as 6:30 – 14:30 (IST, in UTC format) and hence would lead to storage of two different set of data for the same time duration. In order to overcome this type of issue the document contains workarounds that should be implemented in your Duet Enterprise solution.
Authors: Balamuruga P and Moitra Oyshik Company: SAP Labs India
Created on: May 4, 2012
Author Bio
Table of Contents
Introduction ... 3
Why special handling for „Time zone unaware scenario‟ ... 3
Flow Diagram ... 4
Pre-requisite: Class Creation ... 5
How to enable the special handling for “Timezone unaware scenarios” ... 10
Modification of data type in generated Service Interface ... 11
Regenerate the Server Proxy ... 12
Code Changes in Implementation class ... 13
Handcraft Section ... 17
Provider Class implementation ... 17
Custom form for handling Date Types ... 19
Introduction
This document is relevant for the case where the backend RFC do not respect the time zone relevant information. Also below recommendations were not for the case which requires calendar building Blocks like Time management and Leave management scenario.
Why special handling for „Time zone unaware scenario‟
With Tooling story (BDC Browser generated code) for a case which has the date time handling, below are the problem statements:
A sales representative, who works in Bangalore (IST time zone), creates a sales order on 1st Jan 2012. Assume his manager is located in Palo Alto (PST) and his peer Sales Representative is in Japan.
Manager would see the sales order creation date as 31st December 2011 and his peer Sales Representative in Japan would see the creation date as 2nd January 2012.
Is the creation date on December 31st, or Jan 1st, or Jan 2nd? This would confuse the manager and peer colleagues.
Also in India, Diwali (the festival of lights) is celebrated on Tuesday, November 13, 2012. When colleagues start celebrating in India, it is still November 12 in Toronto. In Japan it will be already November 14. So is Diwali on November 12, 13 or 14?
Technical Consultant should be aware of the behavior of RFC. Whether the RFC respects the timezone information or not? By default, when the date-time information is passed to Gateway Addon; date-time value will be converted to GMT time zone format from the SharePoint layer. Then Gateway Addon layer will convert the received date-time value to user‟s time zone and it will be stored in database table. This time zone conversion code was generated by the BDC Browser code.
For the case where RFC does not respects the time zone information, DB table will not reflect the actual value of date-time which was entered by the user. Due to different value of date-time, it would affect various other calculations based on the existing wrong value. In this case, actual value of user entered date-time in SharePoint is expected in DB table. Values are expected to be saved without honoring the time zone. Currently there is a discrepancy in data store in SAP.
Flow Diagram
Yes
No
Tooling using BDC Browser
Handcraft Analysis on Backend RE Backend RFC respect the time zone
information
No Special Handling
required
Changes in ESR layer Section 1.3.1
Changes in Gateway Addon layer Section 1.3
Changes in Sharepoint side [ Custom Control ] Section 1.5
Approach
Ensure that data type for the date
and time attributes should
be STRING
Changes in Gateway Addon layer Section 1.4.1
Pre-requisite: Class Creation
This section is common for both Tooling and handcraft part.
Please create below class & method in the Gateway Addon system.
Class Name Class Purpose
ZIWTNG_CL_DATETIME_UTIL Class to handle the Date Time Format Conversion logic. Instantiation: Public
Method and Purpose:
GET_DATE_IN ( ) – This method converts from YYYY-MM-DDTHH:MM:SS to YYYYMMDD format
GET_TIMESTAMP_IN ( ) – This method YYYY-MM-DDTHH:MM:SS to YYYYMMDDHHMMSS format
GET_TIMESTAMP_OUT ( ) – This method converts from YYYYMMDDHHMMSS To YYYY-MM-DDTHH:MM:SS format GET_DATE_OUT ( ) – This method converts from
YYYYMMDD To YYYY-MM-DDTHH:MM:SS format
Method name: GET_DATE_IN ( ) Level: Static method
Visibility: Public
Name Operation type Type Optional
IV_DATETIME Importing STRING No
RT_DATE Returning CHAR8 No
Method name: GET_TIMESTAMP_IN ( ) Level: Static method
Visibility: Public
Name Operation type Type Optional
IV_DATETIME Importing STRING No
RT_DATETIME Returning CHAR14 No
Method name: GET_TIMESTAMP_OUT ( ) Level: Static method
Visibility: Public
Name Operation type Type Optional
IV_DATETIME Importing TIMESTAMPL No
Method name: GET_DATE_OUT ( ) Level: Static method
Visibility: Public
Name Operation type Type Optional
IV_DATETIME Importing /OSP/DT_DATE No
RT_DATETIME Returning CHAR19 No
Method name: GET_TIME_IN ( ) Level: Static method
Visibility: Public
Name Operation type Type Optional
IV_DATETIME Importing STRING No
RT_TIME Returning CHAR6 No
Method name: GET_TIME_OUT ( ) Level: Static method
Visibility: Public
Name Operation type Type Optional
IV_TIME Importing TIMS No
RT_DATETIME Returning CHAR19 No
Code Snippet for GET_DATE_IN Method
method get_date_in.
* Conversion logic
* Conversion from YYYY-MM-DDTHH:MM:SS to YYYYMMDD
data: lv_date_of_birth_full type c length 19. data: lv_date_of_birth type c length 8.
lv_date_of_birth_full = iv_datetime.
* Conversion from YYYY-MM-DDTHH:MM:SS to YYYYMMDDHHMMSS if lv_date_of_birth_full is not initial.
replace all occurrences of '-' in lv_date_of_birth_full with ''. replace all occurrences of ':' in lv_date_of_birth_full with ''. replace all occurrences of 'T' in lv_date_of_birth_full with ''. endif.
* Conversion from YYYYMMDDHHMMSS to YYYYMMDD lv_date_of_birth = lv_date_of_birth_full. rt_date = lv_date_of_birth.
Code Snippet for GET_TIMESTAMP_IN Method
method get_timestamp_in.
* Conversion logic
* Conversion from YYYY-MM-DDTHH:MM:SS to YYYYMMDDHHMMSS
data: lv_datetime_nonutc type c length 19.
lv_datetime_nonutc = iv_datetime.
if lv_datetime_nonutc is not initial.
replace all occurrences of '-' in lv_datetime_nonutc with ''. replace all occurrences of ':' in lv_datetime_nonutc with ''. replace all occurrences of 'T' in lv_datetime_nonutc with ''. endif.
rt_datetime = lv_datetime_nonutc. endmethod.
Code Snippet for GET_TIMESTAMP_OUT Method method get_timestamp_out.
* Conversion logic
* Conversion from YYYYMMDDHHMMSS To YYYY-MM-DDTHH:MM:SS data: lv_datetime type string.
data: lv_datetime_nonutc type c length 19. data: lv_length type i.
* Getting length of the datetime input lv_datetime = iv_datetime.
lv_length = strlen( iv_datetime ).
* conversion logic from 20120304050600 to 2012-03-04t05:06:00 if iv_datetime is not initial.
if lv_length ge 14.
clear: lv_datetime_nonutc.
concatenate lv_datetime+0(4) '-' lv_datetime+4(2) '-' lv_datetime+6(2) 'T' lv_datetime+8(2) ':' lv_datetime+10(2) ':' lv_datetime+12(2)
into lv_datetime_nonutc. rt_datetime = lv_datetime_nonutc. else. rt_datetime = '8900:12:31T00:00:00'. endif. else. rt_datetime = '8900:12:31T00:00:00'. endif. endmethod.
Code Snippet for GET_DATE_OUT Method
method get_date_out.
* Conversion logic
* Conversion from YYYYMMDD To YYYY-MM-DDTHH:MM:SS
data: lv_datetime type string.
data: lv_datetime_nonutc type c length 19. data: lv_length type i.
* Getting the length of the datetime input lv_datetime = iv_datetime.
lv_length = strlen( lv_datetime ).
* Conversion logic from 19901112 TO 1990-11-12T00:00:00 if lv_length ge 8.
if iv_datetime is not initial.
*convert to ISO Date Time Format YYYY-MM-DDThh:mm:ss clear: lv_datetime_nonutc.
concatenate lv_datetime+0(4) '-' lv_datetime+4(2) '-' lv_datetime+6(2) 'T00:00:00' into lv_datetime_nonutc. rt_datetime = lv_datetime_nonutc. endif. else. rt_datetime = '8900:12:31T00:00:00'. endif. if iv_datetime is initial. rt_datetime = '8900:12:31T00:00:00'. endif. endmethod.
Code Snippet for GET_TIME_IN Method
method get_time_in.
* Conversion logic
* Conversion from YYYY-MM-DDTHH:MM:SS to HHMMSS
data: lv_date_of_time_full type c length 19.
lv_date_of_time_full = iv_datetime.
* Conversion from YYYY-MM-DDTHH:MM:SS to YYYYMMDDHHMMSS if lv_date_of_time_full is not initial.
replace all occurrences of '-' in lv_date_of_time_full with ''. replace all occurrences of ':' in lv_date_of_time_full with ''.
replace all occurrences of 'T' in lv_date_of_time_full with ''. endif.
* Conversion from YYYYMMDDHHMMSS to YYYYMMDD rt_time = lv_date_of_time_full+8(6).
endmethod.
Code Snippet for GET_TIME_OUT Method
method GET_TIME_OUT.
* Conversion logic
* Conversion from YYYYMMDD To YYYY-MM-DDTHH:MM:SS
DATA: lv_datetime TYPE string.
DATA: lv_datetime_nonutc TYPE c LENGTH 19. DATA: lv_length TYPE i.
* Getting the length of the datetime input lv_datetime = iv_time.
lv_length = strlen( lv_datetime ).
* Conversion logic from 19901112 TO 1990-11-12T00:00:00 IF lv_length EQ 6.
IF iv_time IS NOT INITIAL.
*convert to ISO Date Time Format YYYY-MM-DDThh:mm:ss CLEAR: lv_datetime_nonutc.
CONCATENATE '8900-12-31T'
lv_datetime+0(2)':' lv_datetime+2(2)':' lv_datetime+4(2) INTO lv_datetime_nonutc. rt_datetime = lv_datetime_nonutc. ENDIF. ELSE. rt_datetime = '8900-12-31T00:00:00'. ENDIF. endmethod.
How to enable the special handling for “Timezone unaware scenarios”
For the above stated problem, we need a way to get the actual date-time value from the SharePoint to Gateway Addon layer.
Identify the fields which fall in the DATETIME & DATE fields under this scenario. Let us consider DATETIME_NONUTC and DATE_OF_BIRTH as the indicative fields in the example discussed below.
Use custom control in SharePoint UI in order to avoid the GMT time zone conversion. This control will format the user entered value to particular string format. String format will be passed to Gateway Addon.
Operations
Attributes
Date
Time
Time
Date of Birth
Query
Special handling required. Custom Form that would contain date time picker control and in the backend there should be code to populate the picker with thedata from the string parameter and vice versa. Data Type in ESR should be STRING Special handling required. Custom Form (with Time only flag) that would contain date time
picker control and in the backend there should be code to populate the picker with the data from the string parameter
and vice versa. Data Type in ESR should be
STRING
Special handling required. Custom Form (with Time only flag) that would contain date time picker control and in the backend there should be code to populate the
picker with the data from the string parameter and vice
versa. Data Type in ESR should be STRING
Read
Create
Update
Delete
No changes required No changes required No changes required To receive the string format value, Server proxy‟s interface data type should be of STRING. After receiving the input, format needs to be converted so that it can be passed to Gateway API.
„xsd:string‟ data type have to be set in the DATETIME_NONUTC and DATE_OF_BIRTH fields of generated web service and in addition the BDC model should also have string data type. The Date-time object (for DATETIME_NONUTC, TIME and DATE_OF_BIRTH) in the SharePoint is converted into string with format
YYYY-MM-DDTHH: MM: SS. The corresponding string format is read in the Gateway Addon layer and then
Gateway Addon to SharePoint. Gateway Addon will send the dates in string format, which is then read in SharePoint layer and converted to Date-Time object.
Following are the steps to enhance the existing functionality in Gateway Addon Layer
a) Modification of data type to „xsd:string‟ for DATETIME_NONUTC & DATE_OF_BIRTH, TIME fields in generated service interface
b) Regenerate the Server Proxy
c) Enhance code in Create, Update, Query and Read methods of WS Implementation class Modification of data type in generated Service Interface
Modification of data type has to be done in ESR system, locate the service in ESR system under the namespace where it was created. Namespace can be found with below steps
1. Go to transaction /iwtng/bdc_browser.
2. Go to menu path Tool settings ->Service settings. 3. Double click the active configuration id.
4. Namespace can found from this Service settings view.
For Query and Read operation, the output data type must have those identified fields. For Update and Create operation input data type should have those identified fields.
Below is the example for Create operation. Select the Create operation in ESR view, double click on Request Message Type, and double click on data type for IS_INPUT. It will open up the data type for input to create operation. To this data type, modify the data type to IWXString, as shown below.
DATE_OF_BIRTH, DATETIME_NONUTC and TIME fields‟ data type should be set to IWXString. Save the data type.
Follow the above steps for modifying the data type for those identified fields
Request data type for Update operation (data type corresponding to IS_INPUT)
Response data type for Query operation (data type corresponding to ET_OUTPUT)
Response data type for Read operation (data type corresponding to ES_OUTPUT) Finally save and activate all the data types.
Regenerate the Server Proxy
Once the data types have been changed, save and activate the service interface in ESR system. Re-generate the server proxy and the changed data type in sproxy transaction in Gateway Addon. The server proxy should have the changes.
Code Changes in Implementation class
For Create and Update operations, the string fields would be filled up by the SharePoint codebehind. The string values will then be converted into ABAP date and stored in backend system. For read scenario, Read and Query operations, the string fields will be filled up in Gateway Addon layer and then sent to SharePoint without time zone conversion.
The code has to be changed in the implementation class of the proxy. You can find the implementation class in the following screen.
Methods (CREATE_ZDT_PBAPOC_BLOGREQUEST & UPDATE_ZDT_PBAPOC_BLOGREQUEST) Please note down below highlighted code block. Below blocks were the generated code for the two fields DATETIME_NONUTC and DATE_OF_BIRTH. In this case, there are only two fields. Depending on the number of these kind of fields, below highlighted code block would vary in numbers and also in the
positioning of these code blocks in your method. Hence the suggestion for removing the below code block is to look for the code block which is identical as below highlighted blocks and comment or delete the code blocks.
Code Snippet for DATETIME_NONUTC and DATE_OF_BIRTH [ONLY for Create and Update method]
* Comment below line
* ls_input-DATETIME_NONUTC = <ls_input>-is_input-DATETIME_NONUTC.
* From the sharepoint, we get the input(lv_date_of_birth_full) as * 1990-11-12T00:00:00, this needs to modified to 19901112
ls_input-date_of_birth
= ziwtng_cl_datetime_util=>get_date_in( <ls_input>-is_input-date_of_birth ).
* From the sharepoint, we get the input(lv_datetime_nonutc) as * 2012-03-04T05:06:00, this needs to modified to 20120304050600
ls_input-datetime_nonutc
= ziwtng_cl_datetime_util=>get_timestamp_in( <ls_input>-is_input-datetime_nonutc ).
* From the SharePoint, we get the input(time) as * YYYY-MM-DDTHH:MM:SS this needs to modified to HHMMSS
ls_input-time
= ziwtng_cl_datetime_util=>get_time_in( <ls_input>-is_input-time ).
Following code explains the handling in Read and Find methods of the provider class.
Methods (READ_ZDT_PBAPOC_BLOGBY_IDQUERY & FIND_ZDT_PBAPOC_BLOGBY_ELEMENT)
Please note down below highlighted code block. Below blocks were the generated code for the two fields DATETIME_NONUTC and DATE_OF_BIRTH. In this case, there are only two fields. Depending on the number of these kinds of fields, below highlighted code block would vary in numbers and also in the
positioning of these code blocks in your method. Hence the suggestion for removing the below code block is to look for the code block which is identical as below highlighted blocks and comment or delete the code blocks.
Insert the below block code.
Code Snippet for End Time [Only for Read and Query]
* Conversion logic from 20120304050600 TO 2012-03-04T05:06:00 <ls_output>-es_output-datetime_nonutc
= ziwtng_cl_datetime_util=>get_timestamp_out( ls_output-datetime_nonutc ).
* Conversion logic from 19901112 TO 1990-11-12T00:00:00 <ls_output>-es_output-date_of_birth
= ziwtng_cl_datetime_util=>get_date_out( ls_output-date_of_birth ).
* Conversion logic from HHMMSS TO YYYY-MM-DDTHH:MM:SS
<ls_output>-es_output-time = ziwtng_cl_datetime_util=>get_time_out( ls_output1-time ).
Note: As mentioned in the above sections, provider class methods have been modified. In future, if the same service interface gets regenerated, then there is a chance of losing the code which exists as part of provider class method implementations. Before the service interface gets regenerated, please do not forget to take the backup of the existing code. After regeneration, old code should be placed back
Handcraft Section
Technical Consultant should be aware of the behavior of RFC. Whether the RFC respects the timezone information or not? For the DATETIME_NONUTC & DATE_OF_BIRTH field, if the RFC does not respect the time zone, then following point should be considered.
Ensure the data type for those fields in ESR is with „xsd:string‟ data type. Provider Class implementation
1) Create the „ZIWTNG_CL_DATETIME_UTIL‟ class as mentioned in the pre-requisite section.
2) In READ & QUERY method, ensure that those fields are set to YYYY-MM-DDTHH:MM:SS format in the output parameters.
Sample code flow for READ & QUERY Method below mentioned in order to show the special handling of date-time value. Special handling has to be done only after invoking the SOAP Bridge API.
* Invoke the SOAP Bridge API to read the user’s detail
CALL METHOD /iwfnd/cl_soap_bridge=>bulk_read EXPORTING iv_request_uuid = lv_request_id iv_consumer_type_code = /iwtng/if_common_const=>gcv_consumer_type_code iv_cor_gsdo_type = lc_gsdo_user iv_exception_class_name = /iwtng/cl_global_constants=>gc_exception_class it_input = lt_user_ids IMPORTING et_output = lt_output. .
* Place the below code block after invoking the SOAP Bridge API. * Loop through output internal table for formatting the datetime
* Conversion logic from 20120304050600 TO 2012-03-04T05:06:00 <ls_output>-es_output-datetime_nonutc
= ziwtng_cl_datetime_util=>get_timestamp_out( ls_output-datetime_nonutc ).
* Conversion logic from 19901112 TO 1990-11-12T00:00:00 <ls_output>-es_output-date_of_birth
= ziwtng_cl_datetime_util=>get_date_out( ls_output-date_of_birth ).
* Conversion logic from HHMMSS TO YYYY-MM-DDTHH:MM:SS <ls_output>-es_output-time
= ziwtng_cl_datetime_util=>get_time_out( ls_output-time ).
* Conversion logic from HHMMSS TO YYYY-MM-DDTHH:MM:SS <ls_output>-es_output-time
= ziwtng_cl_datetime_util=>get_time_out( ls_output-time ).
a. In CREATE & UPDATE method, ensure to implement the format conversion logic(YYYY-MM-DDTHH:MM:SS to YYYYMMDDHHMMSS) before invoking the Gateway API.
Sample code flow for CREATE Method below mentioned in order to show the special handling of datetime value. Special handling has to be done only before invoking the SOAP Bridge API.
Code Snippet for DATETIME_NONUTC and DATE_OF_BIRTH [ONLY for Create and Update method]
DATA: lv_datetime_nonutc TYPE C LENGTH 14. DATA: lv_date_of_birth TYPE C LENGTH 8.
* Comment below line
* ls_input-DATETIME_NONUTC = <ls_input>-is_input-DATETIME_NONUTC.
. .
* From the SharePoint, we get the input(ls_input-date_of_birth) as * 1990-11-12T00:00:00, this needs to modified to 19901112
ls_input-date_of_birth
= ziwtng_cl_datetime_util=>get_date_in( <ls_input>-is_input-date_of_birth ).
* From the SharePoint, we get the input(datetime_nonutc) as * 2012-03-04T05:06:00, this needs to modified to 20120304050600
ls_input-datetime_nonutc
= ziwtng_cl_datetime_util=>get_timestamp_in( <ls_input>-is_input-datetime_nonutc ).
* From the SharePoint, we get the input(time) as * YYYY-MM-DDTHH:MM:SS this needs to modified to HHMMSS
ls_input-time
= ziwtng_cl_datetime_util=>get_time_in( <ls_input>-is_input-time ).
* Invoking SOAP Bridge API for create CALL METHOD /iwfnd/cl_soap_bridge=>create
EXPORTING iv_request_uuid = ls_input-user_iwxcreate_request_s-request_id iv_consumer_type_code = /iwtng/if_common_const=>gcv_consumer_typ_code iv_cor_gsdo_type = /iwtng/cl_gl_constants=>gc_gsdo_user iv_exception_class_name = /iwtng/cl_gl_constants=>gc_exception_class is_input = ls_input-user_iwxcreate_request_s-item IMPORTING es_output = output-attachment_iwxcreate_response-user_id.
Custom form for handling Date Types
We would consider an ECT that would show the following: 1. Non UTC Date (Date Of Birth)
2. Non UTC Date & Time 3. UTC Date & Time 4. Time
By following the previous steps as mentioned we would be able to receive the models but this data type inside it would still have date time type. So we need to change them to string format.
We can do this by simply replacing: 1.
By
2.
By
Also, as these type descriptors were previously of type DateTime we get few additional fields which need to be removed.
So from the model, remove all the occurrences of:
And
Once this step is done we would update the model with the correct endpoint and upload it in the central admin.
Before we proceed forward, it is advisable that you familiarize yourself with custom forms and how to build them.
Now we have to build a custom UI to show case the data. Building a Display form would be straight forward as there are no new controls required here. For edit(Update Scenario) and new forms(Create Scenario) we will introduce two DateTimePickerControl. One for Date_Of_Birth and the other for DATETIME_NONUTC. While adding the picker for the Date_Of_Birth scenario, we need to make sure that the DateOnly property for the DateTimePickerControl is set to true. This is done so that the time drop downs are not shown in the UI.
So, the below markup would show the custom control that we are building up for UPDATE scenario.
<link rel="stylesheet" type="text/css" href="/_layouts/1033/styles/datepicker.css"/> <asp:Table ID="Table1" runat="server" Width="100%">
<asp:TableRow>
<asp:TableCell CssClass="ms-formlabel">
<SharePoint:FieldLabel runat="server" ID="FieldLabel2" FieldName="USER_NAME" DisplaySize="5" />
</asp:TableCell>
<asp:TableCell CssClass="ms-formbody">
<SharePoint:FormField runat="server" ID="ffldUSER_NAME" ControlMode="Edit" FieldName="USER_NAME" />
</asp:TableCell> </asp:TableRow> <asp:TableRow>
<asp:TableCell CssClass="ms-formlabel">
<SharePoint:FieldLabel runat="server" ID="FieldLabel1" FieldName="DATE_OF_BIRTH" DisplaySize="5" />
</asp:TableCell>
<asp:TableCell CssClass="ms-formbody">
<SharePoint:FormField runat="server" ID="ffldDATE_OF_BIRTH" ControlMode="Display" FieldName="DATE_OF_BIRTH" Visible="false"/>
<SharePoint:DateTimeControl runat="server" ID="DATE_OF_BIRTH_PICKER" DateOnly="True"/>
</asp:TableCell> </asp:TableRow> <asp:TableRow>
<asp:TableCell CssClass="ms-formlabel">
<SharePoint:FieldLabel runat="server" ID="FieldLabel3" FieldName="DATETIME_NONUTC" DisplaySize="5" />
</asp:TableCell>
<asp:TableCell CssClass="ms-formbody">
<SharePoint:FormField runat="server" ID="ffldDATETIME_NONUTC" ControlMode="Display" FieldName="DATETIME_NONUTC" Visible="false"/>
<SharePoint:DateTimeControl runat="server" ID="DATETIME_NONUTC_PICKER" /> </asp:TableCell>
</asp:TableRow> <asp:TableRow>
<asp:TableCell CssClass="ms-formlabel">
<SharePoint:FieldLabel runat="server" ID="FieldLabel4" FieldName="DATETIME_UTC" DisplaySize="5" />
</asp:TableCell>
<asp:TableCell CssClass="ms-formbody">
<SharePoint:FormField runat="server" ID="ffldDATETIME_UTC" ControlMode="Edit" FieldName="DATETIME_UTC" />
</asp:TableCell>
<asp:TableCell CssClass="ms-formlabel">
<SharePoint:FieldLabel runat="server" ID="FieldLabel5" FieldName="TIME" DisplaySize="5" />
</asp:TableCell>
<asp:TableCell CssClass="ms-formbody">
<SharePoint:FormField runat="server" ID="ffldTIME" ControlMode="Display" FieldName="TIME" Visible="false"/>
<SharePoint:DateTimeControl runat="server" ID="TimeOFBirth" TimeOnly="true"/>
</asp:TableCell>
</asp:TableRow>
<asp:TableRow>
<asp:TableCell CssClass="ms-formlabel">
<SharePoint:FieldLabel runat="server" ID="FieldLabel5" FieldName="ID" DisplaySize="5" />
</asp:TableCell>
<asp:TableCell CssClass="ms-formbody">
<SharePoint:FormField runat="server" ID="ffldID" ControlMode="Display" FieldName="ID" />
</asp:TableCell> </asp:TableRow> <asp:TableRow>
<asp:TableCell CssClass="ms-formlabel">
<SharePoint:FieldLabel runat="server" ID="FieldLabel6" FieldName="ESDATRID" DisplaySize="5" />
</asp:TableCell>
<asp:TableCell CssClass="ms-formbody">
<SharePoint:FormField runat="server" ID="ffldESDATRID" ControlMode="Edit" FieldName="ESDATRID" />
</asp:TableCell> </asp:TableRow>
</asp:Table>
Pay notice that DATE_OF_BIRTH_PICKER control has DateOnly property set to true & TimeOFBirth picker control has TimeOnly property set to true.
Next we go on building the New items form (CREATE Scenario) as shown below:
<link rel="stylesheet" type="text/css" href="/_layouts/1033/styles/datepicker.css"/> <asp:Table ID="Table1" runat="server" Width="100%">
<asp:TableRow>
<asp:TableCell CssClass="ms-formlabel">
<SharePoint:FieldLabel runat="server" ID="FieldLabel2" FieldName="USER_NAME" DisplaySize="5" />
</asp:TableCell>
<asp:TableCell CssClass="ms-formbody">
<SharePoint:FormField runat="server" ID="ffldUSER_NAME" ControlMode="New" FieldName="USER_NAME" />
</asp:TableRow> <asp:TableRow>
<asp:TableCell CssClass="ms-formlabel">
<SharePoint:FieldLabel runat="server" ID="FieldLabel1" FieldName="DATE_OF_BIRTH" DisplaySize="5" />
</asp:TableCell>
<asp:TableCell CssClass="ms-formbody">
<SharePoint:DateTimeControl runat="server" ID="DATE_OF_BIRTH_PICKER" DateOnly="True"/>
</asp:TableCell> </asp:TableRow> <asp:TableRow>
<asp:TableCell CssClass="ms-formlabel">
<SharePoint:FieldLabel runat="server" ID="FieldLabel3" FieldName="DATETIME_NONUTC" DisplaySize="5" />
</asp:TableCell>
<asp:TableCell CssClass="ms-formbody">
<SharePoint:DateTimeControl runat="server" ID="DATETIME_NONUTC_PICKER" /> </asp:TableCell>
</asp:TableRow> <asp:TableRow>
<asp:TableCell CssClass="ms-formlabel">
<SharePoint:FieldLabel runat="server" ID="FieldLabel4" FieldName="DATETIME_UTC" DisplaySize="5" />
</asp:TableCell>
<asp:TableCell CssClass="ms-formbody">
<SharePoint:FormField runat="server" ID="ffldDATETIME_UTC" ControlMode="New" FieldName="DATETIME_UTC" />
</asp:TableCell>
<asp:TableCell CssClass="ms-formlabel">
<SharePoint:FieldLabel runat="server" ID="FieldLabel5" FieldName="TIME" DisplaySize="5" />
</asp:TableCell>
<asp:TableCell CssClass="ms-formbody">
<SharePoint:FormField runat="server" ID="ffldTIME" ControlMode="Display" FieldName="TIME" Visible="false"/>
<SharePoint:DateTimeControl runat="server" ID="TimeOFBirth" TimeOnly="true"/>
</asp:TableCell>
</asp:TableRow> <asp:TableRow>
<asp:TableCell CssClass="ms-formlabel">
<SharePoint:FieldLabel runat="server" ID="FieldLabel5" FieldName="ID" DisplaySize="5" />
</asp:TableCell>
<asp:TableCell CssClass="ms-formbody">
<SharePoint:FormField runat="server" ID="ffldID" ControlMode="Display" FieldName="ID" />
</asp:TableCell> </asp:TableRow> <asp:TableRow>
<SharePoint:FieldLabel runat="server" ID="FieldLabel6" FieldName="ESDATRID" DisplaySize="5" />
</asp:TableCell>
<asp:TableCell CssClass="ms-formbody">
<SharePoint:FormField runat="server" ID="ffldESDATRID" ControlMode="New" FieldName="ESDATRID" />
</asp:TableCell> </asp:TableRow> </asp:Table>
With the above markup, our new forms are ready. Next our work would be to capture the data filled by the user in these controls and populate our backend parameter (string parameter) with it in the format that is mutually agreed by the SharePoint and ABAP developer. In our case the format had been decided as „yyyy-MM-ddTHH:mm:ss‟.
These data need to be captured during the save event of the form , so we will go ahead and override the out of the box save handler and place our own save handler in there using the following piece of code.
Next, would be to defince our save handler wherein we would take the data from the controls and populate the backend parameters. This can be done by the following piece of code.
In case of edit form, we also need to make sure that the data received from the string parameters are properly rendered onto the screen using the DateTimePickerControl. For this we need to override the Pre-render event of our edit page so that we can populate the controls before they are Pre-rendered onto the screen. We can override the Pre_render event in the constructor of our Edit Page web part using the code shown blow.
Once we have overridden the event, let‟s use our own event handler to populate the data. The code block that will help us do that is listed below.
protected void Page_Load(object sender, EventArgs e) {
SPContext.Current.FormContext.OnSaveHandler += FormSaveHandler; }
private void FormSaveHandler(object sender, EventArgs args) {
//Before the mapping is done, please decide upon a date format with the ABAP developer
SPContext.Current.Item["DATE_OF_BIRTH"] = DATE_OF_BIRTH_PICKER.SelectedDate.ToString("yyyy-MM-ddTHH:mm:ss"); SPContext.Current.Item["DATETIME_NONUTC"] = DATETIME_NONUTC_PICKER.SelectedDate.ToString("yyyy-MM-ddTHH:mm:ss"); SPContext.Current.Item["TIME"] = TimeOFBirth.SelectedDate.ToString("yyyy-MM-ddTHH:mm:ss");
SaveButton.SaveItem(SPContext.Current, false, String.Empty); }
protected EditFormUserControl() {
this.PreRender += Page_PreRender; }
protected void Page_PreRender(object sender, EventArgs args) {
if (!IsPostBack) {
MakeDateSelections();
private void MakeDateSelections() {
/* We are doing the following checks
*1. If the data from the backend is not null (data returned as object here)
*2. If the control is not uninitialized
*3. The object type data returned by the backend has a non emptyString value
*4. It is not equivalent to the current selection of DateTimeControlPicker */
if (ffldDATETIME_NONUTC != null && ffldDATETIME_NONUTC.ItemFieldValue != null && !String.IsNullOrEmpty(ffldDATETIME_NONUTC.ItemFieldValue.ToString()) && ffldDATETIME_NONUTC.ItemFieldValue.ToString() != DATETIME_NONUTC_PICKER.SelectedDate.ToString()) { DateTime dateTime = DateTime.Parse(ffldDATETIME_NONUTC.ItemFieldValue.ToString()); DATETIME_NONUTC_PICKER.SelectedDate = dateTime; }
// Same validation as mentioned above is also done for Date Of Birth field. if (ffldDATE_OF_BIRTH != null && ffldDATE_OF_BIRTH.ItemFieldValue != null && !String.IsNullOrEmpty(ffldDATE_OF_BIRTH.ItemFieldValue.ToString()) && ffldDATE_OF_BIRTH.ItemFieldValue.ToString() != DATE_OF_BIRTH_PICKER.SelectedDate.ToString()) { DateTime dateTime = DateTime.Parse(ffldDATE_OF_BIRTH.ItemFieldValue.ToString()); DATE_OF_BIRTH_PICKER.SelectedDate = dateTime; }
if (ffldTIME != null && ffldTIME.ItemFieldValue != null
&& !String.IsNullOrEmpty(ffldTIME.ItemFieldValue.ToString()) && ffldTIME.ItemFieldValue.ToString() != TimeOFBirth.SelectedDate.ToString()) { DateTime dateTime = DateTime.Parse(ffldTIME.ItemFieldValue.ToString()); TimeOFBirth.SelectedDate = dateTime; }
protected void Page_PreRender(object sender, EventArgs args) {
if (!IsPostBack) {
MakeDateSelections();
{
/* We are doing the following checks
*1. If the data from the backend is not null (data returned as object here)
*2. If the control is not uninitialized
*3. The object type data returned by the backend has a non emptyString value
*4. It is not equivalent to the current selection of DateTimeControlPicker */
if (ffldDATETIME_NONUTC != null && ffldDATETIME_NONUTC.ItemFieldValue != null && !String.IsNullOrEmpty(ffldDATETIME_NONUTC.ItemFieldValue.ToString()) && ffldDATETIME_NONUTC.ItemFieldValue.ToString() != DATETIME_NONUTC_PICKER.SelectedDate.ToString()) { DateTime dateTime = DateTime.Parse(ffldDATETIME_NONUTC.ItemFieldValue.ToString()); DATETIME_NONUTC_PICKER.SelectedDate = dateTime; }
// Same validation as mentioned above is also done for Date Of Birth field. if (ffldDATE_OF_BIRTH != null && ffldDATE_OF_BIRTH.ItemFieldValue != null && !String.IsNullOrEmpty(ffldDATE_OF_BIRTH.ItemFieldValue.ToString()) && ffldDATE_OF_BIRTH.ItemFieldValue.ToString() != DATE_OF_BIRTH_PICKER.SelectedDate.ToString()) { DateTime dateTime = DateTime.Parse(ffldDATE_OF_BIRTH.ItemFieldValue.ToString()); DATE_OF_BIRTH_PICKER.SelectedDate = dateTime; }
if (ffldTIME != null && ffldTIME.ItemFieldValue != null
&& !String.IsNullOrEmpty(ffldTIME.ItemFieldValue.ToString()) && ffldTIME.ItemFieldValue.ToString() != TimeOFBirth.SelectedDate.ToString()) { DateTime dateTime = DateTime.Parse(ffldTIME.ItemFieldValue.ToString()); TimeOFBirth.SelectedDate = dateTime; }
protected void Page_PreRender(object sender, EventArgs args) {
if (!IsPostBack) {
MakeDateSelections(); }
private void MakeDateSelections() {
/* We are doing the following checks
*1. If the data from the backend is not null (data returned as object here)
*2. If the control is not uninitialized
*4. It is not equivalent to the current selection of DateTimeControlPicker */
if (ffldDATETIME_NONUTC != null && ffldDATETIME_NONUTC.ItemFieldValue != null && !String.IsNullOrEmpty(ffldDATETIME_NONUTC.ItemFieldValue.ToString()) && ffldDATETIME_NONUTC.ItemFieldValue.ToString() != DATETIME_NONUTC_PICKER.SelectedDate.ToString()) { DateTime dateTime = DateTime.Parse(ffldDATETIME_NONUTC.ItemFieldValue.ToString()); DATETIME_NONUTC_PICKER.SelectedDate = dateTime; }
// Same validation as mentioned above is also done for Date Of Birth field. if (ffldDATE_OF_BIRTH != null && ffldDATE_OF_BIRTH.ItemFieldValue != null && !String.IsNullOrEmpty(ffldDATE_OF_BIRTH.ItemFieldValue.ToString()) && ffldDATE_OF_BIRTH.ItemFieldValue.ToString() != DATE_OF_BIRTH_PICKER.SelectedDate.ToString()) { DateTime dateTime = DateTime.Parse(ffldDATE_OF_BIRTH.ItemFieldValue.ToString()); DATE_OF_BIRTH_PICKER.SelectedDate = dateTime; }
if (ffldTIME != null && ffldTIME.ItemFieldValue != null
&& !String.IsNullOrEmpty(ffldTIME.ItemFieldValue.ToString()) && ffldTIME.ItemFieldValue.ToString() != TimeOFBirth.SelectedDate.ToString()) { DateTime dateTime = DateTime.Parse(ffldTIME.ItemFieldValue.ToString()); TimeOFBirth.SelectedDate = dateTime;
With these changes datetime custom form is ready to be deployed. Once everything is up and running, our final screen would look like:
Copyright
© Copyright 2012 SAP AG. All rights reserved.
No part of this publication may be reproduced or transmitted in any form or for any purpose without the express permission of SAP AG. The information contained herein may be changed without prior notice.
Some software products marketed by SAP AG and its distributors contain proprietary software components of other software vendors.
Microsoft, Windows, Excel, Outlook, and PowerPoint are registered trademarks of Microsoft Corporation.
IBM, DB2, DB2 Universal Database, System i, System i5, System p, System p5, System x, System z, System z10, System z9, z10, z9, iSeries, pSeries, xSeries, zSeries, eServer, z/VM, z/OS, i5/OS, S/390, OS/390, OS/400, AS/400, S/390 Parallel Enterprise Server, PowerVM, Power Architecture, POWER6+, POWER6, POWER5+, POWER5, POWER, OpenPower, PowerPC, BatchPipes, BladeCenter, System Storage, GPFS, HACMP, RETAIN, DB2 Connect, RACF, Redbooks, OS/2, Parallel Sysplex, MVS/ESA, AIX, Intelligent Miner, WebSphere, Netfinity, Tivoli and Informix are trademarks or registered trademarks of IBM Corporation.
Linux is the registered trademark of Linus Torvalds in the U.S. and other countries.
Adobe, the Adobe logo, Acrobat, PostScript, and Reader are either trademarks or registered trademarks of Adobe Systems Incorporated in the United States and/or other countries.
Oracle is a registered trademark of Oracle Corporation.
UNIX, X/Open, OSF/1, and Motif are registered trademarks of the Open Group.
Citrix, ICA, Program Neighborhood, MetaFrame, WinFrame, VideoFrame, and MultiWin are trademarks or registered trademarks of Citrix Systems, Inc.
HTML, XML, XHTML and W3C are trademarks or registered trademarks of W3C®, World Wide Web Consortium, Massachusetts Institute of Technology.
Java is a registered trademark of Oracle Corporation.
JavaScript is a registered trademark of Oracle Corporation, used under license for technology invented and implemented by Netscape.
SAP, R/3, SAP NetWeaver, Duet, PartnerEdge, ByDesign, SAP Business ByDesign, and other SAP products and services mentioned herein as well as their respective logos are trademarks or registered trademarks of SAP AG in Germany and other countries.
Business Objects and the Business Objects logo, BusinessObjects, Crystal Reports, Crystal Decisions, Web Intelligence, Xcelsius, and other Business Objects products and services mentioned herein as well as their respective logos are trademarks or registered trademarks of Business Objects S.A. in the United States and in other countries. Business Objects is an SAP company.
All other product and service names mentioned are the trademarks of their respective companies. Data contained in this document serves informational purposes only. National product specifications may vary.
These materials are subject to change without notice. These materials are provided by SAP AG and its affiliated companies ("SAP Group") for informational purposes only, without representation or warranty of any kind, and SAP Group shall not be liable for errors or omissions with respect to the materials. The only warranties for SAP Group products and services are those that are set forth in the express warranty statements accompanying such products and services, if any. Nothing herein should be construed as constituting an additional warranty.