diff --git a/src/zcl_excel_common.clas.abap b/src/zcl_excel_common.clas.abap index 17263df..4cfddb3 100644 --- a/src/zcl_excel_common.clas.abap +++ b/src/zcl_excel_common.clas.abap @@ -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, diff --git a/src/zcl_excel_common.clas.testclasses.abap b/src/zcl_excel_common.clas.testclasses.abap index f6a53af..2d693fb 100644 --- a/src/zcl_excel_common.clas.testclasses.abap +++ b/src/zcl_excel_common.clas.testclasses.abap @@ -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. diff --git a/src/zcl_excel_worksheet.clas.abap b/src/zcl_excel_worksheet.clas.abap index c650015..a1b1e7d 100644 --- a/src/zcl_excel_worksheet.clas.abap +++ b/src/zcl_excel_worksheet.clas.abap @@ -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. - " DATE = yyyymmdd - ls_style_conv-abap_type = cl_abap_typedescr=>typekind_date. + "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 = -cell_style BINARY SEARCH. IF sy-subrc EQ 0. CASE ls_style_conv-abap_type. + WHEN c_typekind_timestamp. + = zcl_excel_common=>excel_string_to_timestamp( -cell_value ). WHEN cl_abap_typedescr=>typekind_date. = zcl_excel_common=>excel_string_to_date( -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. diff --git a/src/zcl_excel_worksheet.clas.testclasses.abap b/src/zcl_excel_worksheet.clas.testclasses.abap index 3493506..34a6083 100644 --- a/src/zcl_excel_worksheet.clas.testclasses.abap +++ b/src/zcl_excel_worksheet.clas.testclasses.abap @@ -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. diff --git a/src/zcx_excel.clas.xml b/src/zcx_excel.clas.xml index 5bbc56e..23ddcb3 100644 --- a/src/zcx_excel.clas.xml +++ b/src/zcx_excel.clas.xml @@ -42,6 +42,14 @@ 0001 + + + LIMU + CPUB + ZCX_EXCEL + 0001 + + CONSTRUCTOR