Initial support for time stamps

Cells which are formatted with both date format codes and time format codes are converted to time stamp values instead of date values. There is no support for time zones - all values will be converted to UTC time stamps.
This commit is contained in:
Kjetil Kilhavn 2023-08-10 07:11:03 +00:00
parent c28162d1d1
commit 7e5f6da63d
5 changed files with 229 additions and 8 deletions

View File

@ -7,6 +7,7 @@ CLASS zcl_excel_common DEFINITION
*"* do not include other source files here!!!
PUBLIC SECTION.
TYPES ty_timestamp TYPE p LENGTH 8 DECIMALS 0.
CONSTANTS c_excel_baseline_date TYPE d VALUE '19000101'. "#EC NOTEXT
CLASS-DATA c_excel_numfmt_offset TYPE int1 VALUE 164. "#EC NOTEXT . . . . . . . . . . . . . . . . " .
CONSTANTS c_excel_sheet_max_col TYPE int4 VALUE 16384. "#EC NOTEXT
@ -116,6 +117,13 @@ CLASS zcl_excel_common DEFINITION
VALUE(ep_value) TYPE t
RAISING
zcx_excel .
CLASS-METHODS excel_string_to_timestamp
IMPORTING
!ip_value TYPE zexcel_cell_value
RETURNING
VALUE(ep_value) TYPE ty_timestamp
RAISING
zcx_excel .
CLASS-METHODS excel_string_to_number
IMPORTING
!ip_value TYPE zexcel_cell_value
@ -863,7 +871,7 @@ CLASS zcl_excel_common IMPLEMENTATION.
CHECK ip_value IS NOT INITIAL AND ip_value CN ' 0'.
TRY.
lv_date_int = ip_value.
lv_date_int = floor( ip_value ).
IF lv_date_int NOT BETWEEN 1 AND 2958465.
zcx_excel=>raise_text( 'Unable to interpret date' ).
ENDIF.
@ -897,7 +905,7 @@ CLASS zcl_excel_common IMPLEMENTATION.
TRY.
lv_day_fraction = ip_value.
lv_day_fraction = ip_value - floor( ip_value ).
lv_seconds_in_day = lv_day_fraction * lc_seconds_in_day.
ep_value = lv_seconds_in_day.
@ -908,6 +916,13 @@ CLASS zcl_excel_common IMPLEMENTATION.
ENDMETHOD.
METHOD excel_string_to_timestamp.
CONVERT DATE excel_string_to_date( ip_value )
TIME excel_string_to_time( ip_value )
INTO TIME STAMP ep_value TIME ZONE 'UTC'.
ENDMETHOD.
METHOD get_fieldcatalog.
DATA: lr_dref_tab TYPE REF TO data,
lo_salv_table TYPE REF TO cl_salv_table,

View File

@ -1,5 +1,8 @@
CLASS lcl_excel_common_test DEFINITION DEFERRED.
CLASS zcl_excel_common DEFINITION LOCAL FRIENDS lcl_excel_common_test.
CLASS ltc_xlsx_date_time DEFINITION DEFERRED.
CLASS zcl_excel_common DEFINITION LOCAL FRIENDS
lcl_excel_common_test
ltc_xlsx_date_time.
*----------------------------------------------------------------------*
* CLASS lcl_Excel_Common_Test DEFINITION
@ -131,6 +134,19 @@ CLASS lcl_excel_common_test DEFINITION FOR TESTING
ENDCLASS.
CLASS ltc_xlsx_date_time DEFINITION FINAL FOR TESTING
DURATION SHORT
RISK LEVEL HARMLESS.
PRIVATE SECTION.
CONSTANTS dt_20230803_140711 TYPE string VALUE `45141.588321759256`. "Excel raw value for date & time 2023-08-03 14:07:11
METHODS:
from_xlsx_to_date FOR TESTING RAISING cx_static_check,
from_xlsx_to_time FOR TESTING RAISING cx_static_check,
from_xlsx_to_timestamp FOR TESTING RAISING cx_static_check.
ENDCLASS.
*----------------------------------------------------------------------*
* CLASS lcl_Excel_Common_Test IMPLEMENTATION
*----------------------------------------------------------------------*
@ -1688,3 +1704,50 @@ CLASS lcl_excel_common_test IMPLEMENTATION.
ENDMETHOD.
ENDCLASS.
CLASS ltc_xlsx_date_time IMPLEMENTATION.
METHOD from_xlsx_to_date.
TRY.
cl_abap_unit_assert=>assert_equals(
act = zcl_excel_common=>excel_string_to_date( dt_20230803_140711 )
exp = '20230803'
msg = 'Wrong date from conversion'
level = if_aunit_constants=>critical ).
CATCH zcx_excel INTO DATA(excel_error).
cl_abap_unit_assert=>fail(
msg = 'unexpected exception' && excel_error->get_text( )
level = if_aunit_constants=>critical ).
ENDTRY.
ENDMETHOD.
METHOD from_xlsx_to_time.
TRY.
cl_abap_unit_assert=>assert_equals(
act = zcl_excel_common=>excel_string_to_time( dt_20230803_140711 )
exp = '140711'
msg = 'Wrong time from conversion'
level = if_aunit_constants=>critical ).
CATCH zcx_excel INTO DATA(excel_error).
cl_abap_unit_assert=>fail(
msg = 'unexpected exception' && excel_error->get_text( )
level = if_aunit_constants=>critical ).
ENDTRY.
ENDMETHOD.
METHOD from_xlsx_to_timestamp.
TRY.
cl_abap_unit_assert=>assert_equals(
act = zcl_excel_common=>excel_string_to_timestamp( dt_20230803_140711 )
exp = '20230803140711'
msg = 'Wrong time stamp from conversion'
level = if_aunit_constants=>critical ).
CATCH zcx_excel INTO DATA(excel_error).
cl_abap_unit_assert=>fail(
msg = 'unexpected exception' && excel_error->get_text( )
level = if_aunit_constants=>critical ).
ENDTRY.
ENDMETHOD.
ENDCLASS.

View File

@ -693,6 +693,7 @@ CLASS zcl_excel_worksheet DEFINITION
*"* private components of class ZCL_EXCEL_WORKSHEET
*"* do not include other source files here!!!
TYPES ty_table_settings TYPE STANDARD TABLE OF zexcel_s_table_settings WITH DEFAULT KEY.
CONSTANTS c_typekind_timestamp TYPE abap_typekind VALUE 'Z'.
DATA active_cell TYPE zexcel_s_cell_data .
DATA charts TYPE REF TO zcl_excel_drawings .
DATA columns TYPE REF TO zcl_excel_columns .
@ -782,6 +783,16 @@ CLASS zcl_excel_worksheet DEFINITION
EXPORTING
!ep_value TYPE simple
!ep_value_type TYPE abap_typekind .
METHODS is_date_format
IMPORTING
!ip_value TYPE simple
RETURNING
VALUE(result) TYPE abap_bool.
METHODS is_time_format
IMPORTING
!ip_value TYPE simple
RETURNING
VALUE(result) TYPE abap_bool.
METHODS move_supplied_borders
IMPORTING
iv_border_supplied TYPE abap_bool
@ -2167,7 +2178,7 @@ CLASS zcl_excel_worksheet IMPLEMENTATION.
EXCEPTIONS
OTHERS = 3
).
IF lt_ddic_object IS INITIAL.
IF sy-subrc = 0 AND lt_ddic_object IS INITIAL.
lt_comp_view = lo_line_type->get_included_view( ).
LOOP AT lt_comp_view INTO ls_comp_view.
ls_comp_view-type->get_ddic_object(
@ -2176,7 +2187,7 @@ CLASS zcl_excel_worksheet IMPLEMENTATION.
EXCEPTIONS
OTHERS = 3
).
IF lt_ddic_object_comp IS NOT INITIAL.
IF sy-subrc = 0 AND lt_ddic_object_comp IS NOT INITIAL.
READ TABLE lt_ddic_object_comp INTO ls_ddic_object INDEX 1.
ls_ddic_object-fieldname = ls_comp_view-name.
APPEND ls_ddic_object TO lt_ddic_object.
@ -2237,8 +2248,13 @@ CLASS zcl_excel_worksheet IMPLEMENTATION.
REPLACE ALL OCCURRENCES OF REGEX '\[\L[^]]*\]' IN lv_format_code WITH ''.
IF lv_format_code CA 'yd' OR lv_format_code EQ zcl_excel_style_number_format=>c_format_date_std.
"TODO kjetil-kilhavn make use of is_date_format( ) and is_time_format( )
IF lv_format_code CA 'hs'.
ls_style_conv-abap_type = c_typekind_timestamp.
ELSE.
" DATE = yyyymmdd
ls_style_conv-abap_type = cl_abap_typedescr=>typekind_date.
ENDIF.
ELSEIF lv_format_code CA 'hs'.
" TIME = hhmmss
ls_style_conv-abap_type = cl_abap_typedescr=>typekind_time.
@ -2295,6 +2311,8 @@ CLASS zcl_excel_worksheet IMPLEMENTATION.
READ TABLE lt_style_conv INTO ls_style_conv WITH KEY cell_style = <ls_sheet_content>-cell_style BINARY SEARCH.
IF sy-subrc EQ 0.
CASE ls_style_conv-abap_type.
WHEN c_typekind_timestamp.
<lv_data> = zcl_excel_common=>excel_string_to_timestamp( <ls_sheet_content>-cell_value ).
WHEN cl_abap_typedescr=>typekind_date.
<lv_data> = zcl_excel_common=>excel_string_to_date( <ls_sheet_content>-cell_value ).
WHEN cl_abap_typedescr=>typekind_time.
@ -3241,6 +3259,31 @@ CLASS zcl_excel_worksheet IMPLEMENTATION.
ENDMETHOD. "IS_CELL_MERGED
METHOD is_date_format.
"TODO kjetil-kilhavn figure out how to detect all valid date formats, including mmmmm (first letter of month name)
result = abap_false.
CHECK ip_value CA 'ymd'.
result = abap_true.
ENDMETHOD. "is_date_format
METHOD is_time_format.
"TODO kjetil-kilhavn try to implement this without hardcoding too much...
"Note: Open Office XML: m or mm represents minutes if it is used
" immediately after h or hh (for hours) or
" immediately before ss (for seconds)
" Source: ECMA-376 Part 1 page 1789
" What about e.g. format code 'm:s' - s is also valid for seconds!?
result = abap_false.
CHECK ip_value CA 'hms'.
result = abap_true.
ENDMETHOD. "is_time_format
METHOD move_supplied_borders.
DATA: ls_borderx TYPE zexcel_s_cstylex_border.

View File

@ -6,6 +6,8 @@ CLASS ltc_normalize_style_param DEFINITION DEFERRED.
CLASS ltc_check_cell_column_formula DEFINITION DEFERRED.
CLASS ltc_check_overlapping DEFINITION DEFERRED.
CLASS ltc_set_cell_value_types DEFINITION DEFERRED.
CLASS ltc_is_date_format DEFINITION DEFERRED.
CLASS ltc_is_time_format DEFINITION DEFERRED.
CLASS zcl_excel_worksheet DEFINITION LOCAL FRIENDS
ltc_normalize_column_heading
ltc_normalize_columnrow_param
@ -14,7 +16,9 @@ CLASS zcl_excel_worksheet DEFINITION LOCAL FRIENDS
ltc_check_overlapping
ltc_normalize_style_param
ltc_check_cell_column_formula
ltc_set_cell_value_types.
ltc_set_cell_value_types
ltc_is_date_format
ltc_is_time_format.
CLASS lcl_excel_worksheet_test DEFINITION FOR TESTING
RISK LEVEL HARMLESS
@ -301,6 +305,38 @@ CLASS ltc_set_cell_value_types DEFINITION FOR TESTING
ENDCLASS.
CLASS ltc_is_date_format DEFINITION FINAL FOR TESTING
DURATION SHORT
RISK LEVEL HARMLESS.
PRIVATE SECTION.
DATA cut TYPE REF TO zcl_excel_worksheet.
DATA xlsx TYPE REF TO zcl_excel.
METHODS:
setup,
teardown.
METHODS:
date_format_iso FOR TESTING RAISING cx_static_check.
ENDCLASS.
CLASS ltc_is_time_format DEFINITION FINAL FOR TESTING
DURATION SHORT
RISK LEVEL HARMLESS.
PRIVATE SECTION.
DATA cut TYPE REF TO zcl_excel_worksheet.
DATA xlsx TYPE REF TO zcl_excel.
METHODS:
setup,
teardown.
METHODS:
time_format_iso FOR TESTING RAISING cx_static_check.
ENDCLASS.
CLASS lcl_excel_worksheet_test IMPLEMENTATION.
* ==============================================
@ -1664,3 +1700,59 @@ CLASS ltc_set_cell_value_types IMPLEMENTATION.
ENDMETHOD.
ENDCLASS.
CLASS ltc_is_date_format IMPLEMENTATION.
METHOD setup.
TRY.
xlsx = NEW #( ).
cut = NEW #( xlsx ).
CATCH zcx_excel INTO DATA(excel_error).
cl_abap_unit_assert=>fail(
msg = 'Could not create instance'
quit = if_aunit_constants=>class ).
ENDTRY.
ENDMETHOD.
METHOD teardown.
CLEAR cut.
CLEAR xlsx.
ENDMETHOD.
METHOD date_format_iso.
cl_abap_unit_assert=>assert_true(
act = cut->is_date_format( ip_value = 'yyyy-mm-dd' )
msg = 'ISO date format not reckognized'
level = if_aunit_constants=>critical ).
ENDMETHOD.
ENDCLASS.
CLASS ltc_is_time_format IMPLEMENTATION.
METHOD setup.
TRY.
xlsx = NEW #( ).
cut = NEW #( xlsx ).
CATCH zcx_excel INTO DATA(excel_error).
cl_abap_unit_assert=>fail(
msg = 'Could not create instance'
quit = if_aunit_constants=>class ).
ENDTRY.
ENDMETHOD.
METHOD teardown.
CLEAR cut.
CLEAR xlsx.
ENDMETHOD.
METHOD time_format_iso.
cl_abap_unit_assert=>assert_true(
act = cut->is_time_format( ip_value = 'hh:mm:ss' )
msg = 'ISO time format not reckognized'
level = if_aunit_constants=>critical ).
ENDMETHOD.
ENDCLASS.

View File

@ -42,6 +42,14 @@
<LFD_NUM>0001</LFD_NUM>
</SOTR_USE>
</SOTR_USE>
<SOTS_USE>
<SOTR_USEU>
<PGMID>LIMU</PGMID>
<OBJECT>CPUB</OBJECT>
<OBJ_NAME>ZCX_EXCEL</OBJ_NAME>
<LFD_NUM>0001</LFD_NUM>
</SOTR_USEU>
</SOTS_USE>
<DESCRIPTIONS>
<SEOCOMPOTX>
<CMPNAME>CONSTRUCTOR</CMPNAME>