[ABAP] OADate (Ole Automation Date) in Zeitstempel (timestamp) umrechnen

* https://stackoverflow.com/questions/43519005/how-to-convert-java-date-to-oadate-or-vice-versa
* https://msdn.microsoft.com/de-de/library/system.datetime.tooadate(v=vs.110).aspx

* Zeitstempel im OADate-Format
DATA: lv_oadate TYPE decfloat34 VALUE '43013.765983796296296000'. " 05.10.2017 18:23:01
*DATA: lv_oadate TYPE decfloat34 VALUE '42909.777881944444444000'. " 23.06.2017 18:40:09
*DATA: lv_oadate TYPE decfloat34 VALUE '42370.000717592592592593'. " 01.01.2016 00:01:02
* Basisdatum für OADate ist der 30.12.1899
DATA: lv_basedate TYPE d VALUE '18991230'.

DATA: lv_oadays TYPE i.
DATA: lv_oatime TYPE decfloat34.
DATA: lv_hour TYPE decfloat34.
DATA: lv_min TYPE decfloat34.
DATA: lv_sec TYPE decfloat34.

DATA: lv_d TYPE d.
DATA: lv_t TYPE t.
DATA: lv_datetime TYPE timestamp.

* Vorkommateil: Tage seit Basisdatum
lv_oadays = trunc( lv_oadate ).
* Nachkommateil: Tageszeit
lv_oatime = lv_oadate - CONV decfloat34( lv_oadays ).
* Nachkommateil: Stunden
lv_hour = lv_oatime * 24.
* Nachkommateil: Minuten
lv_min = ( lv_hour - trunc( lv_hour ) ) * 60.
* Nachkommateil: Sekunden
lv_sec = round( val = ( ( lv_min - trunc( lv_min ) ) * 60 ) dec = 0 ).

* Datum: Basisdatum + Tage des Vorkommateils
lv_d = lv_basedate + lv_oadays.
* Uhrzeit
lv_t = |{ CONV string( trunc( lv_hour ) ) WIDTH = 2 ALPHA = IN }{ CONV string( trunc( lv_min ) ) WIDTH = 2 ALPHA = IN }{ CONV string( trunc( lv_sec ) ) WIDTH = 2 ALPHA = IN }|.

* Datum und Uhrzeit in einem Zeitstempelobjekt zusammenfassen
CONVERT DATE lv_d TIME lv_t INTO TIME STAMP lv_datetime TIME ZONE 'UTC'.

WRITE: / '     OADate:', lv_oadate.
WRITE: / 'Zeitstempel:', lv_datetime TIME ZONE 'UTC'.

[ABAP] Zeitstempel (timestamp) in OADate (Ole Automation Date) umrechnen

* https://stackoverflow.com/questions/43519005/how-to-convert-java-date-to-oadate-or-vice-versa
* https://msdn.microsoft.com/de-de/library/system.datetime.tooadate(v=vs.110).aspx

* Zeitstempel mit Datum und Uhrzeit
*DATA: lv_datetime TYPE timestamp VALUE '20171005182301'. " 43013.765983796296296000
*DATA: lv_datetime TYPE timestamp VALUE '20170623184009'. " 42909.777881944444444000
DATA: lv_datetime TYPE timestamp VALUE '20160101000102'. " 42370.000717592592592593
* Basisdatum für OADate ist der 31.12.1899
DATA: lv_ts_base TYPE timestamp VALUE '18991231000000'.

DATA: lv_d TYPE d.
DATA: lv_t TYPE t.

DATA: lv_oadays TYPE i.

DATA: lv_hour TYPE decfloat34.
DATA: lv_min TYPE decfloat34.
DATA: lv_sec TYPE decfloat34.

DATA: lv_oadate TYPE decfloat34.

* Timestamp in Date und Time aufsplitten
CONVERT TIME STAMP lv_datetime TIME ZONE 'UTC' INTO DATE lv_d TIME lv_t.

* Zeitdifferenz zwischen Zeitstempel und Basisdatum in Sekunden
DATA(lv_diff_sec) = cl_abap_tstmp=>subtract( tstmp1 = lv_datetime
                                             tstmp2 = lv_ts_base ).

* Vorkommateil: Zeitdifferenz zwischen Zeitstempel und Basisdatum in Tagen
lv_oadays = lv_diff_sec / 86400.
* Nachkommateil: Stunden
lv_hour = CONV decfloat34( lv_t+0(2) ) / 24.
* Nachkommateil: Minuten
lv_min = CONV decfloat34( lv_t+2(2) ) / 1440.
* Nachkommateil: Sekunden
lv_sec = CONV decfloat34( lv_t+4(2) ) / 86400.

* OADate zusammenrechnen: Vorkommateil + Nachkommateil
lv_oadate = lv_oadays + lv_hour + lv_min + lv_sec.

WRITE: / 'Zeitstempel:', lv_datetime TIME ZONE 'UTC'.
WRITE: / '     OADate:', lv_oadate.

[ABAP] Timerobjekt cl_gui_timer verwenden

* Hinweis zum Aufruf von cl_gui_timer:
* - selbst mit interval = 1 (1s) triggert der Timer nach run( ) nur alle 2s
* - parallele Listausgaben mehrerer Worker können scheinbar Deadlocks verursachen

* Workerobjekt, welches vom Timer aufgerufen wird
CLASS lcl_worker DEFINITION.
  PUBLIC SECTION.
    METHODS: constructor
      IMPORTING
        i_id TYPE i.
    METHODS: do
      IMPORTING
        i_cnt TYPE i.
    METHODS: finished.

  PRIVATE SECTION.
    DATA: gv_id TYPE i.
ENDCLASS.

CLASS lcl_worker IMPLEMENTATION.
  METHOD constructor.
    gv_id = i_id.
  ENDMETHOD.
  METHOD do.
    WRITE: / 'Do( ) - Timer ID:', gv_id, 'Counter:', i_cnt.
  ENDMETHOD.

  METHOD finished.
    WRITE: / 'Finished( ) - Timer ID:', gv_id.
  ENDMETHOD.
ENDCLASS.

* Timerobjekt, welches zyklisch Worker aufruft
CLASS lcl_timer DEFINITION.
  PUBLIC SECTION.
    METHODS: start
      IMPORTING
        i_worker    TYPE REF TO lcl_worker
        i_interval  TYPE i
        i_count_max TYPE i.

  PRIVATE SECTION.

    DATA: o_worker TYPE REF TO lcl_worker.
    DATA: gv_count_current TYPE i.
    DATA: gv_count_max TYPE i.

    METHODS: on_timer_finished FOR EVENT finished OF cl_gui_timer
      IMPORTING
          sender.
ENDCLASS.

CLASS lcl_timer IMPLEMENTATION.
  METHOD start.
* Übergabewerte ok?
    IF i_count_max >= 0 AND i_interval > 0.
      o_worker = i_worker.

* erster Aufruf -> Funktion sofort ausführen
      o_worker->do( i_cnt = 1 ).

* Zählervariablen setzen
      gv_count_current = 1.
      gv_count_max = i_count_max.

* Timer erzeugen
      DATA(o_timer) = NEW cl_gui_timer( ).
      SET HANDLER on_timer_finished FOR o_timer.
      o_timer->interval = i_interval.
      o_timer->run( ).
    ENDIF.
  ENDMETHOD.

  METHOD on_timer_finished.
* Funktion ausführen
    o_worker->do( i_cnt = gv_count_current + 1 ).

* unendlich
    IF gv_count_max = 0.
* Timer wieder ausführen
      sender->run( ).
    ELSE.
* Anzahl Durchgänge
      gv_count_current = gv_count_current + 1.

      IF gv_count_current < gv_count_max.
* Timer wieder ausführen
        sender->run( ).
      ELSE.
        o_worker->finished( ).
      ENDIF.
    ENDIF.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.

* 1. Timerobjekt mit Worker ID 1
  NEW lcl_timer( )->start( i_worker    = NEW lcl_worker( i_id = 1 )
                           i_interval  = 1
                           i_count_max = 10 ).

* 2. Timerobjekt mit Worker ID 2
  NEW lcl_timer( )->start( i_worker    = NEW lcl_worker( i_id = 2 )
                           i_interval  = 3
                           i_count_max = 5 ).

* 3. Timerobjekt mit Worker ID 3
  NEW lcl_timer( )->start( i_worker    = NEW lcl_worker( i_id = 3 )
                           i_interval  = 2
                           i_count_max = 3 ).

  MESSAGE 'Ok.' TYPE 'S'.

[ABAP] PopUp-Fenster für Datumsauswahl (Kalender) anzeigen

Variante 1 (Monatskalender, Monat und Jahr wählbar)

DATA: lv_dat TYPE sy-datum.
  
CALL FUNCTION 'F4_DATE'
  IMPORTING
    select_date                  = lv_dat
  EXCEPTIONS
    calendar_buffer_not_loadable = 1
    date_after_range             = 2
    date_before_range            = 3
    date_invalid                 = 4
    factory_calendar_not_found   = 5
    holiday_calendar_not_found   = 6
    parameter_conflict           = 7
    OTHERS                       = 8.

Variante 2 (Jahreskalender, scrollbar)

DATA: lv_dat TYPE sy-datum.
  
CALL FUNCTION 'POPUP_CALENDAR_SDB'
  EXPORTING
    sel_day    = abap_true
    focus_day  = sy-datum
  IMPORTING
    begin_date = lv_dat.

[ABAP] UTC-Zeit (TIMESTAMP) in Datum (d) und Uhrzeit (t) splitten

DATA: lv_date TYPE d.
DATA: lv_time TYPE t.
DATA: lv_utc TYPE timestamp.

GET TIME STAMP FIELD lv_utc.

* Zeitzohne bleibt UTC, Timestamp wird also ohne Umrechnung der Zone gesplittet
CONVERT TIME STAMP lv_utc TIME ZONE 'UTC' INTO DATE lv_date TIME lv_time.

WRITE: / |UTC: { lv_utc TIMESTAMP = USER }|.
WRITE: / |UTC-Datum: { lv_date DATE = USER }|.
WRITE: / |UTC-Zeit: { lv_time TIME = USER }|.

[ABAP] UTC-Zeit (TIMESTAMP) in Systemzeit wandeln

DATA: lv_utc TYPE timestamp.
DATA: lv_date TYPE d.
DATA: lv_time TYPE t.

START-OF-SELECTION.

  GET TIME STAMP FIELD lv_utc.

  TRY.
      cl_abap_tstmp=>systemtstmp_utc2syst( EXPORTING
                                             utc_tstmp = lv_utc
                                           IMPORTING
                                             syst_date = lv_date
                                             syst_time = lv_time  ).

      WRITE: / |UTC: { lv_utc TIMESTAMP = USER }|.
      WRITE: / |System-Datum: { lv_date DATE = USER }|.
      WRITE: / |System-Zeit: { lv_time TIME = USER }|.

    CATCH cx_root INTO DATA(e_text).
      WRITE: / e_text->get_text( ).
  ENDTRY.