[ABAP] Datumsfeld auf Gültigkeit prüfen

Variante 1 (check_date)

TRY.
    cl_reca_date=>check_date( CONV d( '01.01.1999' ) ).

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

TRY.
    cl_reca_date=>check_date( CONV d( '19990101' ) ).

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

TRY.
    cl_reca_date=>check_date( CONV d( '1234' ) ).

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

Variante 2 (is_date_ok)

IF abap_true = cl_reca_date=>is_date_ok( id_date = sy-datum ).
  WRITE: / 'Ok.'.
ELSE.
  WRITE: / 'Fehlerhaft.'.
ENDIF.

[ABAP] Zeit zwischen zwei Terminen

* Anzahl Tage
DATA(lv_days) = cl_reca_date=>get_days_between_two_dates( EXPORTING
                                                            id_datefrom = '20190101'
                                                            id_dateto   = '20190201' ).

WRITE: / lv_days.

* Anzahl Monate
DATA(lv_months) = cl_reca_date=>months_between_two_dates( EXPORTING
                                                            id_date_from = '20190101'
                                                            id_date_to   = '20190201' ).

WRITE: / lv_months.

* Anzahl Jahre, Monate, Tage
DATA: lv_years TYPE i.
DATA: lv_months TYPE i.
DATA: lv_days TYPE i.

cl_reca_date=>get_date_diff( EXPORTING
                               id_date_from     = '20190101'
                               id_date_to       = '20190201'
                             IMPORTING
                               ed_years         = lv_years
                               ed_months        = lv_months
                               ed_calendar_days = lv_days
                           ).

WRITE: / lv_years.
WRITE: / lv_months.
WRITE: / lv_days.

[ABAP] Eine bestimmte Zeit (Sekundenbruchteile) warten

* WAIT UP TO ... SECONDS funktioniert nicht in allen Releases gleich zuverlässig
* Siehe auch BAPI_TRANSACTION_COMMIT mit Parameter WAIT = 'X'
* https://blogs.sap.com/2019/08/13/code-snippet-series-wait-a-fraction-of-a-second/
CLASS lcl_wait DEFINITION.
  PUBLIC SECTION.

    TYPES : BEGIN OF ty_s_tmstmp,
              start     TYPE timestampl, " Startzeit
              now       TYPE timestampl, " akt. Zeit
              elapsed   TYPE tzntstmpl,  " verstrichene Zeit in Sekunden
              limit     TYPE tzntstmpl,  " Limit in Sekunden
              condition TYPE abap_bool,  " Abbruch durch Bedingung oder Limit?
            END OF ty_s_tmstmp.

    CLASS-METHODS: wait
      IMPORTING
                i_time         TYPE tzntstmpl
      RETURNING VALUE(rv_info) TYPE ty_s_tmstmp.

  PRIVATE SECTION.
    CLASS-METHODS: condition_code
      RETURNING VALUE(rv_ok) TYPE abap_bool.
ENDCLASS.

CLASS lcl_wait IMPLEMENTATION.

  METHOD wait.
    rv_info = VALUE ty_s_tmstmp( elapsed = 0 limit = i_time condition = abap_false ).

    GET TIME STAMP FIELD rv_info-start.

* 1. Prüfung: Zeitlimit
    WHILE rv_info-elapsed < rv_info-limit.
* 2. Prüfung: Erfüllung der Abbruchbedingung
      IF abap_true = condition_code( ).
        rv_info-condition = abap_true.
        EXIT.
      ENDIF.

      GET TIME STAMP FIELD rv_info-now.
      rv_info-elapsed = cl_abap_tstmp=>subtract( tstmp1 = rv_info-now tstmp2 = rv_info-start ).
    ENDWHILE.

  ENDMETHOD.

  METHOD condition_code.
* Code mit Prüfbedingung hier (z.B. ENQUEUE_READ)
* Beispielcode testet, wann zufällig die Zahl 10 aus einer Seed von 0-100000 erzeugt wird
    DATA(lv_i) = cl_abap_random_int=>create( seed = cl_abap_random=>seed( ) min = 0 max = 100000 )->get_next( ).

    IF lv_i = 10.
      rv_ok = abap_true.
    ELSE.
      rv_ok = abap_false.
    ENDIF.
  ENDMETHOD.
ENDCLASS.

START-OF-SELECTION.

  DATA(lv_ts) = VALUE timestampl( ).

  GET TIME STAMP FIELD lv_ts.
  WRITE: / |Start: { lv_ts TIMESTAMP = ISO }|.

* max. Wartezeit in Sekunden (auch Sekundenbruchteile möglich)
  DATA(lv_info) = lcl_wait=>wait( '1.5' ).
  WRITE: / |Elapsed: { lv_info-elapsed TIMESTAMP = ISO }|.
  WRITE: / |Abbruchbedingung erfüllt: { lv_info-condition }|.

  GET TIME STAMP FIELD lv_ts.
  WRITE: / |Ende: { lv_ts TIMESTAMP = ISO }|.

[ABAP] Fabrikkalender: Anzahl Arbeitstage zu Fabrikkalenderdatum hinzurechnen

* Transaktion: SCAL (Fabrikkalender mit CUA-Oberfläche)
* Tabelle: TFACD (Fabrikkalenderdefinitionen)

* Werk
PARAMETERS: p_werks TYPE t001w-werks DEFAULT '10'.
* zu prüfendes Datum
PARAMETERS: p_date TYPE scal-date DEFAULT '20190101'.
* Kennzeichen, wie Arbeitstag berechnet werden soll:
* '+' nächster Arbeitstag
* '-' vorheriger Arbeitstag
PARAMETERS: p_corr TYPE scal-indicator DEFAULT '+'.
* Anzahl Arbeitstage, die zum Fabrikkalenderdatum dazugerechnet werden sollen
PARAMETERS: p_days TYPE i DEFAULT 3.

START-OF-SELECTION.

* Fabrikkalender zum Werk ermitteln
  SELECT SINGLE *
    INTO @DATA(lv_t001w)
    FROM t001w WHERE werks = @p_werks.

  IF sy-subrc = 0.
    DATA: lv_fabkldate TYPE scal-date.
    DATA: lv_factorydate TYPE scal-facdate.
    DATA: lv_workingday_indicator TYPE scal-indicator.

* Kalenderfunktion Fabrikkalenderdatum zu einem Datum geben
    CALL FUNCTION 'DATE_CONVERT_TO_FACTORYDATE'
      EXPORTING
        factory_calendar_id          = lv_t001w-fabkl " Schlüssel des Fabrikkalenders
        correct_option               = p_corr         " Kennzeichen, wie Arbeitstag berechnet werden soll
        date                         = p_date         " Datum, das in Fabrikkalenderdatum umzuwandeln ist
      IMPORTING
        date                         = lv_fabkldate            " Fabrikkalenderdatum
        factorydate                  = lv_factorydate          " Nummer des Arbeitstags im angegebenen Kalender
        workingday_indicator         = lv_workingday_indicator " Kennzeichen, ob Datum ein Arbeitstag ist
      EXCEPTIONS
        calendar_buffer_not_loadable = 1
        correct_option_invalid       = 2
        date_after_range             = 3
        date_before_range            = 4
        date_invalid                 = 5
        factory_calendar_not_found   = 6
        OTHERS                       = 7.

    IF sy-subrc = 0.
      WRITE: / 'Ursprgl. Datum:', p_date.
      WRITE: / 'Fabrikkalenderdatum:', lv_fabkldate.
      WRITE: / 'Nummer des Arbeitstags:', lv_factorydate.

* Anzahl Tage zu Fabrikkalenderdatum hinzuzurechnen
      lv_factorydate = lv_factorydate + p_days.

      WRITE: / 'Korrigierter Arbeitstag:', lv_factorydate.

      DATA: lv_new_date TYPE scal-date.

* Kalenderfunktion Datum zu einem Fabrikkalenderdatum geben
      CALL FUNCTION 'FACTORYDATE_CONVERT_TO_DATE'
        EXPORTING
          factorydate                  = lv_factorydate
          factory_calendar_id          = lv_t001w-fabkl
        IMPORTING
          date                         = lv_new_date
        EXCEPTIONS
          calendar_buffer_not_loadable = 1
          factorydate_after_range      = 2
          factorydate_before_range     = 3
          factorydate_invalid          = 4
          factory_calendar_id_missing  = 5
          factory_calendar_not_found   = 6
          OTHERS                       = 7.

      IF sy-subrc = 0.
        WRITE: / 'Neues Fabrikkalenderdatum:', lv_new_date.
      ENDIF.

    ENDIF.
  ELSE.
    WRITE: / |Kein Fabrikkalender zum Werk { p_werks } verfügbar.|.
  ENDIF.

[ABAP] Fabbrikkalender zum Werk lesen

* Transaktion: SCAL (Fabrikkalender mit CUA-Oberfläche)
* Tabelle: TFACD (Fabrikkalenderdefinitionen)

* Werk
PARAMETERS: p_werks TYPE t001w-werks DEFAULT '10'.
* zu prüfendes Datum
PARAMETERS: p_date TYPE scal-date DEFAULT '20190101'.
* Kennzeichen, wie Arbeitstag berechnet werden soll:
* '+' nächster Arbeitstag
* '-' vorheriger Arbeitstag
PARAMETERS: p_corr TYPE scal-indicator DEFAULT '+'.

START-OF-SELECTION.

* Fabrikkalender zum Werk ermitteln
  SELECT SINGLE *
    INTO @DATA(lv_t001w)
    FROM t001w WHERE werks = @p_werks.

  IF sy-subrc = 0.
    DATA: lv_fabkldate TYPE scal-date.
    DATA: lv_factorydate TYPE scal-facdate.
    DATA: lv_workingday_indicator TYPE scal-indicator.

* Kalenderfunktion Fabrikkalenderdatum zu einem Datum geben
    CALL FUNCTION 'DATE_CONVERT_TO_FACTORYDATE'
      EXPORTING
        factory_calendar_id          = lv_t001w-fabkl " Schlüssel des Fabrikkalenders
        correct_option               = p_corr         " Kennzeichen, wie Arbeitstag berechnet werden soll
        date                         = p_date         " Datum, das in Fabrikkalenderdatum umzuwandeln ist
      IMPORTING
        date                         = lv_fabkldate            " Fabrikkalenderdatum
        factorydate                  = lv_factorydate          " Nummer des Arbeitstags im angegebenen Kalender
        workingday_indicator         = lv_workingday_indicator " Kennzeichen, ob Datum ein Arbeitstag ist
      EXCEPTIONS
        calendar_buffer_not_loadable = 1
        correct_option_invalid       = 2
        date_after_range             = 3
        date_before_range            = 4
        date_invalid                 = 5
        factory_calendar_not_found   = 6
        OTHERS                       = 7.

    IF sy-subrc = 0.
      WRITE: / 'Werk:', p_werks.
      WRITE: / 'ID Fabrikkalender:', lv_t001w-fabkl.
      WRITE: / 'Datum:', p_date.
      WRITE: / 'Fabrikkalenderdatum:', lv_fabkldate.
      WRITE: / 'Nummer des Arbeitstags:', lv_factorydate.
      WRITE: / 'Datum ist Arbeitstag:', lv_workingday_indicator.
    ENDIF.
  ELSE.
    WRITE: / |Kein Fabrikkalender zum Werk { p_werks } verfügbar.|.
  ENDIF.

[ABAP] Zeit zu Zeit und Datum rechnen

DATA: lv_sdate TYPE d.
DATA: lv_stime TYPE t.

* akt. Datum und Zeit
lv_sdate = sy-datum.
lv_stime = sy-uzeit.

* 10:00:00Uhr
DATA: lv_addtime TYPE t VALUE '100000'.

DATA: lv_edate TYPE d.
DATA: lv_etime TYPE t.

START-OF-SELECTION.

* Zeitwerte addieren
  CALL FUNCTION 'C14B_ADD_TIME'
    EXPORTING
      i_starttime = lv_stime
      i_startdate = lv_sdate
      i_addtime   = lv_addtime
    IMPORTING
      e_endtime   = lv_etime
      e_enddate   = lv_edate.

  WRITE: / lv_sdate, lv_stime.
  WRITE: / lv_edate, lv_etime.