ABAP Date & Time Operations: Datumsberechnungen, Timestamps

kategorie
ABAP-Statements
Veröffentlicht
autor
Johannes

Date & Time Operations in ABAP umfassen Datumsberechnungen, Fabrikkalender, Timestamps und Zeitzonenkonvertierung. Moderne Klassen wie CL_ABAP_DATFM ergänzen die klassischen Funktionsbausteine.

Datentypen

TypFormatBeispiel
D (DATS)YYYYMMDD20241124
T (TIMS)HHMMSS143052
TIMESTAMPLYYYYMMDDHHMMSS.sssssss20241124143052.1234567
TIMESTAMPYYYYMMDDHHMMSS20241124143052

Beispiele

1. Grundlegende Datumsberechnungen

DATA: lv_date TYPE sy-datum,
lv_days TYPE i.
" Aktuelles Datum
lv_date = sy-datum.
" Tage addieren/subtrahieren
lv_date = sy-datum + 30. " 30 Tage später
lv_date = sy-datum - 7. " 7 Tage früher
" Differenz zwischen Daten
lv_days = sy-datum - '20240101'.
WRITE: / 'Tage seit Jahresanfang:', lv_days.
" Datum zerlegen
DATA: lv_year TYPE n LENGTH 4,
lv_month TYPE n LENGTH 2,
lv_day TYPE n LENGTH 2.
lv_year = sy-datum(4).
lv_month = sy-datum+4(2).
lv_day = sy-datum+6(2).
" Datum zusammensetzen
lv_date = |{ lv_year }{ lv_month }{ lv_day }|.

2. Monats- und Jahresberechnungen

DATA: lv_date TYPE sy-datum.
" Monate addieren
CALL FUNCTION 'RP_CALC_DATE_IN_INTERVAL'
EXPORTING
date = sy-datum
days = 0
months = 3 " 3 Monate
signum = '+'
years = 0
IMPORTING
calc_date = lv_date.
" Jahre addieren
CALL FUNCTION 'RP_CALC_DATE_IN_INTERVAL'
EXPORTING
date = sy-datum
days = 0
months = 0
signum = '+'
years = 1 " 1 Jahr
IMPORTING
calc_date = lv_date.
" Monatsanfang ermitteln
DATA(lv_month_start) = sy-datum.
lv_month_start+6(2) = '01'.
" Monatsende ermitteln
CALL FUNCTION 'RP_LAST_DAY_OF_MONTHS'
EXPORTING
day_in = sy-datum
IMPORTING
last_day_of_month = lv_date.
" Quartalsanfang
DATA(lv_quarter) = ( ( sy-datum+4(2) - 1 ) DIV 3 ) * 3 + 1.
DATA(lv_quarter_start) = |{ sy-datum(4) }{ lv_quarter WIDTH = 2 ALIGN = RIGHT PAD = '0' }01|.

3. Wochentag und Kalenderwoche

DATA: lv_weekday TYPE p,
lv_week TYPE scal-week.
" Wochentag (1=Montag, 7=Sonntag)
CALL FUNCTION 'DAY_IN_WEEK'
EXPORTING
datum = sy-datum
IMPORTING
woession = lv_weekday.
CASE lv_weekday.
WHEN 1. WRITE: / 'Montag'.
WHEN 2. WRITE: / 'Dienstag'.
WHEN 3. WRITE: / 'Mittwoch'.
WHEN 4. WRITE: / 'Donnerstag'.
WHEN 5. WRITE: / 'Freitag'.
WHEN 6. WRITE: / 'Samstag'.
WHEN 7. WRITE: / 'Sonntag'.
ENDCASE.
" Kalenderwoche
CALL FUNCTION 'DATE_GET_WEEK'
EXPORTING
date = sy-datum
IMPORTING
week = lv_week.
WRITE: / 'Kalenderwoche:', lv_week. " Format: YYYYWW

4. Fabrikkalender

DATA: lv_workdays TYPE i,
lv_target_date TYPE sy-datum.
" Arbeitstage zwischen zwei Daten
CALL FUNCTION 'RKE_SELECT_FACTDAYS_FOR_PERIOD'
EXPORTING
i_datab = '20240101'
i_datbi = '20240131'
i_factid = '01' " Fabrikkalender-ID
IMPORTING
e_factdays = lv_workdays.
WRITE: / 'Arbeitstage im Januar:', lv_workdays.
" Arbeitstage addieren
CALL FUNCTION 'BKK_ADD_WORKINGDAY'
EXPORTING
i_date = sy-datum
i_days = 5 " 5 Arbeitstage
i_calendar_id = '01'
IMPORTING
e_date = lv_target_date.
WRITE: / 'In 5 Arbeitstagen:', lv_target_date.
" Prüfen ob Arbeitstag
CALL FUNCTION 'DATE_CHECK_WORKINGDAY'
EXPORTING
date = sy-datum
factory_calendar_id = '01'
EXCEPTIONS
date_after_range = 1
date_before_range = 2
date_invalid = 3
date_no_workingday = 4
factory_calendar_not_found = 5
OTHERS = 6.
IF sy-subrc = 0.
WRITE: / 'Heute ist ein Arbeitstag'.
ELSEIF sy-subrc = 4.
WRITE: / 'Heute ist kein Arbeitstag'.
ENDIF.

5. Feiertage

DATA: lt_holidays TYPE TABLE OF iscal_day.
" Feiertage für ein Jahr
CALL FUNCTION 'HOLIDAY_GET'
EXPORTING
holiday_calendar = 'DE' " Feiertagskalender
date_from = '20240101'
date_to = '20241231'
TABLES
holidays = lt_holidays
EXCEPTIONS
factory_calendar_not_found = 1
holiday_calendar_not_found = 2
date_has_invalid_format = 3
date_inconsistency = 4
OTHERS = 5.
IF sy-subrc = 0.
LOOP AT lt_holidays INTO DATA(ls_holiday).
WRITE: / ls_holiday-date, ls_holiday-holiday_text.
ENDLOOP.
ENDIF.
" Prüfen ob Feiertag
CALL FUNCTION 'HOLIDAY_CHECK_AND_GET_INFO'
EXPORTING
date = sy-datum
holiday_calendar_id = 'DE'
IMPORTING
holiday_found = DATA(lv_is_holiday)
EXCEPTIONS
OTHERS = 1.
IF lv_is_holiday = abap_true.
WRITE: / 'Heute ist Feiertag'.
ENDIF.

6. Timestamps

DATA: lv_timestamp TYPE timestamp,
lv_timestampl TYPE timestampl.
" Aktueller Timestamp
GET TIME STAMP FIELD lv_timestampl.
WRITE: / 'Timestamp:', lv_timestampl.
" Timestamp aus Datum/Zeit erstellen
CONVERT DATE sy-datum TIME sy-uzeit
INTO TIME STAMP lv_timestamp TIME ZONE sy-zonlo.
" Timestamp in Datum/Zeit konvertieren
DATA: lv_date TYPE sy-datum,
lv_time TYPE sy-uzeit.
CONVERT TIME STAMP lv_timestamp TIME ZONE sy-zonlo
INTO DATE lv_date TIME lv_time.
WRITE: / 'Datum:', lv_date, 'Zeit:', lv_time.
" Timestamp-Differenz
DATA: lv_ts1 TYPE timestampl,
lv_ts2 TYPE timestampl,
lv_diff TYPE timestampl.
GET TIME STAMP FIELD lv_ts1.
" ... Verarbeitung ...
GET TIME STAMP FIELD lv_ts2.
lv_diff = lv_ts2 - lv_ts1. " Differenz in Sekunden
WRITE: / 'Dauer:', lv_diff, 'Sekunden'.

7. Zeitzonen

DATA: lv_timestamp TYPE timestamp,
lv_date_utc TYPE sy-datum,
lv_time_utc TYPE sy-uzeit,
lv_date_loc TYPE sy-datum,
lv_time_loc TYPE sy-uzeit.
GET TIME STAMP FIELD lv_timestamp.
" In UTC konvertieren
CONVERT TIME STAMP lv_timestamp TIME ZONE 'UTC'
INTO DATE lv_date_utc TIME lv_time_utc.
" In lokale Zeit konvertieren
CONVERT TIME STAMP lv_timestamp TIME ZONE sy-zonlo
INTO DATE lv_date_loc TIME lv_time_loc.
WRITE: / 'UTC:', lv_date_utc, lv_time_utc.
WRITE: / 'Lokal:', lv_date_loc, lv_time_loc.
" Zeitzone aus Werk ermitteln
SELECT SINGLE tzone FROM t001w
WHERE werks = '1000'
INTO @DATA(lv_timezone).
CONVERT TIME STAMP lv_timestamp TIME ZONE lv_timezone
INTO DATE lv_date_loc TIME lv_time_loc.

8. Datum formatieren

DATA: lv_date TYPE sy-datum VALUE '20241124',
lv_formatted TYPE string.
" Mit String Templates
lv_formatted = |{ lv_date DATE = USER }|. " Benutzerformat
lv_formatted = |{ lv_date DATE = ISO }|. " 2024-11-24
lv_formatted = |{ lv_date DATE = ENVIRONMENT }|. " Systemformat
WRITE: / lv_formatted.
" Mit Funktionsbaustein
CALL FUNCTION 'CONVERT_DATE_TO_EXTERNAL'
EXPORTING
date_internal = lv_date
IMPORTING
date_external = lv_formatted.
" Eigenes Format
lv_formatted = |{ lv_date+6(2) }.{ lv_date+4(2) }.{ lv_date(4) }|.
" 24.11.2024

9. Zeit formatieren

DATA: lv_time TYPE sy-uzeit VALUE '143052',
lv_formatted TYPE string.
" Mit String Templates
lv_formatted = |{ lv_time TIME = USER }|.
lv_formatted = |{ lv_time TIME = ISO }|. " 14:30:52
" Eigenes Format
lv_formatted = |{ lv_time(2) }:{ lv_time+2(2) }|. " 14:30

10. Datumsvalidierung

DATA: lv_date TYPE sy-datum VALUE '20240230'. " Ungültig: 30. Feb
" Prüfung mit Funktionsbaustein
CALL FUNCTION 'DATE_CHECK_PLAUSIBILITY'
EXPORTING
date = lv_date
EXCEPTIONS
plausibility_check_failed = 1
OTHERS = 2.
IF sy-subrc <> 0.
WRITE: / 'Ungültiges Datum'.
ENDIF.
" Einfache Prüfung
IF lv_date IS INITIAL OR lv_date = '00000000'.
WRITE: / 'Datum ist leer'.
ENDIF.
" Bereich prüfen
IF lv_date < '19000101' OR lv_date > '99991231'.
WRITE: / 'Datum außerhalb gültigem Bereich'.
ENDIF.

11. Date Helper Klasse

CLASS zcl_date_helper DEFINITION.
PUBLIC SECTION.
CLASS-METHODS: add_days
IMPORTING iv_date TYPE sy-datum
iv_days TYPE i
RETURNING VALUE(rv_date) TYPE sy-datum.
CLASS-METHODS: add_months
IMPORTING iv_date TYPE sy-datum
iv_months TYPE i
RETURNING VALUE(rv_date) TYPE sy-datum.
CLASS-METHODS: add_workdays
IMPORTING iv_date TYPE sy-datum
iv_days TYPE i
iv_calendar TYPE scal-fcalid DEFAULT '01'
RETURNING VALUE(rv_date) TYPE sy-datum.
CLASS-METHODS: get_month_start
IMPORTING iv_date TYPE sy-datum
RETURNING VALUE(rv_date) TYPE sy-datum.
CLASS-METHODS: get_month_end
IMPORTING iv_date TYPE sy-datum
RETURNING VALUE(rv_date) TYPE sy-datum.
CLASS-METHODS: get_quarter
IMPORTING iv_date TYPE sy-datum
RETURNING VALUE(rv_quarter) TYPE i.
CLASS-METHODS: is_workday
IMPORTING iv_date TYPE sy-datum
iv_calendar TYPE scal-fcalid DEFAULT '01'
RETURNING VALUE(rv_result) TYPE abap_bool.
CLASS-METHODS: get_weekday_name
IMPORTING iv_date TYPE sy-datum
RETURNING VALUE(rv_name) TYPE string.
ENDCLASS.
CLASS zcl_date_helper IMPLEMENTATION.
METHOD add_days.
rv_date = iv_date + iv_days.
ENDMETHOD.
METHOD add_months.
CALL FUNCTION 'RP_CALC_DATE_IN_INTERVAL'
EXPORTING
date = iv_date
days = 0
months = abs( iv_months )
signum = COND #( WHEN iv_months >= 0 THEN '+' ELSE '-' )
years = 0
IMPORTING
calc_date = rv_date.
ENDMETHOD.
METHOD add_workdays.
CALL FUNCTION 'BKK_ADD_WORKINGDAY'
EXPORTING
i_date = iv_date
i_days = iv_days
i_calendar_id = iv_calendar
IMPORTING
e_date = rv_date
EXCEPTIONS
OTHERS = 1.
IF sy-subrc <> 0.
rv_date = iv_date + iv_days. " Fallback
ENDIF.
ENDMETHOD.
METHOD get_month_start.
rv_date = iv_date.
rv_date+6(2) = '01'.
ENDMETHOD.
METHOD get_month_end.
CALL FUNCTION 'RP_LAST_DAY_OF_MONTHS'
EXPORTING
day_in = iv_date
IMPORTING
last_day_of_month = rv_date.
ENDMETHOD.
METHOD get_quarter.
rv_quarter = ( ( iv_date+4(2) - 1 ) DIV 3 ) + 1.
ENDMETHOD.
METHOD is_workday.
CALL FUNCTION 'DATE_CHECK_WORKINGDAY'
EXPORTING
date = iv_date
factory_calendar_id = iv_calendar
EXCEPTIONS
date_no_workingday = 4
OTHERS = 1.
rv_result = xsdbool( sy-subrc = 0 ).
ENDMETHOD.
METHOD get_weekday_name.
DATA: lv_day TYPE p.
CALL FUNCTION 'DAY_IN_WEEK'
EXPORTING datum = iv_date
IMPORTING woession = lv_day.
rv_name = SWITCH #( lv_day
WHEN 1 THEN 'Montag'
WHEN 2 THEN 'Dienstag'
WHEN 3 THEN 'Mittwoch'
WHEN 4 THEN 'Donnerstag'
WHEN 5 THEN 'Freitag'
WHEN 6 THEN 'Samstag'
WHEN 7 THEN 'Sonntag'
).
ENDMETHOD.
ENDCLASS.
" Verwendung
DATA(lv_next_month) = zcl_date_helper=>add_months( iv_date = sy-datum iv_months = 1 ).
DATA(lv_month_end) = zcl_date_helper=>get_month_end( sy-datum ).
DATA(lv_weekday) = zcl_date_helper=>get_weekday_name( sy-datum ).
WRITE: / 'Nächster Monat:', lv_next_month,
/ 'Monatsende:', lv_month_end,
/ 'Wochentag:', lv_weekday.

12. Zeitdifferenz berechnen

" Sekunden zwischen zwei Zeitpunkten
DATA: lv_date1 TYPE sy-datum VALUE '20240101',
lv_time1 TYPE sy-uzeit VALUE '080000',
lv_date2 TYPE sy-datum VALUE '20240102',
lv_time2 TYPE sy-uzeit VALUE '170000',
lv_seconds TYPE i.
" Als Timestamps berechnen
DATA: lv_ts1 TYPE timestamp,
lv_ts2 TYPE timestamp.
CONVERT DATE lv_date1 TIME lv_time1
INTO TIME STAMP lv_ts1 TIME ZONE 'UTC'.
CONVERT DATE lv_date2 TIME lv_time2
INTO TIME STAMP lv_ts2 TIME ZONE 'UTC'.
lv_seconds = cl_abap_tstmp=>subtract(
tstmp1 = lv_ts2
tstmp2 = lv_ts1
).
DATA(lv_hours) = lv_seconds / 3600.
WRITE: / 'Differenz:', lv_hours, 'Stunden'.

13. Performance-Messung

DATA: lv_start TYPE timestampl,
lv_end TYPE timestampl,
lv_runtime TYPE p DECIMALS 3.
GET TIME STAMP FIELD lv_start.
" Code ausführen
LOOP AT lt_data INTO DATA(ls_data).
" Verarbeitung...
ENDLOOP.
GET TIME STAMP FIELD lv_end.
" Laufzeit in Sekunden
lv_runtime = cl_abap_tstmp=>subtract(
tstmp1 = lv_end
tstmp2 = lv_start
).
WRITE: / 'Laufzeit:', lv_runtime, 'Sekunden'.

14. SQL Datumsfunktionen

" Modernes Open SQL mit Datumsfunktionen
SELECT vbeln, erdat,
DATS_DAYS_BETWEEN( erdat, @sy-datum ) AS days_ago,
DATS_ADD_DAYS( erdat, 30 ) AS plus_30,
DATS_ADD_MONTHS( erdat, 1 ) AS plus_month
FROM vbak
WHERE erdat >= DATS_ADD_DAYS( @sy-datum, -365 )
INTO TABLE @DATA(lt_orders).
" Gruppierung nach Monat
SELECT EXTRACT( YEAR FROM erdat ) AS year,
EXTRACT( MONTH FROM erdat ) AS month,
COUNT(*) AS count,
SUM( netwr ) AS total
FROM vbak
GROUP BY EXTRACT( YEAR FROM erdat ), EXTRACT( MONTH FROM erdat )
INTO TABLE @DATA(lt_monthly).

15. Datum in RAP

" CDS View mit Datumsberechnungen
@AbapCatalog.viewEnhancementCategory: [#NONE]
define view entity ZI_OrderDates as select from vbak
{
key vbeln,
erdat,
@Semantics.systemDate.createdAt: true
erdat as CreatedAt,
// Berechnete Felder
dats_days_between(erdat, $session.system_date) as DaysAgo,
dats_add_days(erdat, 30) as DueDate
}

Wichtige Funktionsbausteine

FunktionsbausteinBeschreibung
RP_CALC_DATE_IN_INTERVALMonate/Jahre addieren
RP_LAST_DAY_OF_MONTHSMonatsende
DATE_GET_WEEKKalenderwoche
DAY_IN_WEEKWochentag
HOLIDAY_GETFeiertage
DATE_CHECK_WORKINGDAYArbeitstag prüfen
BKK_ADD_WORKINGDAYArbeitstage addieren

Wichtige Hinweise / Best Practice

  • sy-datum/sy-uzeit nur für aktuelle Zeit, nicht manipulieren.
  • Timestamps für genaue Zeitmessung und Zeitzonen.
  • Fabrikkalender für Geschäftslogik mit Arbeitstagen.
  • GET TIME STAMP statt sy-uzeit für Präzision.
  • UTC für Speicherung, lokale Zeit für Anzeige.
  • String Templates für Formatierung: |{ date DATE = ISO }|.
  • Datumsfunktionen in SQL ab ABAP 7.50 verfügbar.
  • Validation vor Berechnungen durchführen.
  • Kombinieren Sie mit Built-in Functions für weitere Operationen.