ABAP Date & Time Operations: Date Calculations, Timestamps

Category
ABAP-Statements
Published
Author
Johannes

Date & Time Operations in ABAP include date calculations, factory calendars, timestamps and time zone conversion. Modern classes like CL_ABAP_DATFM complement the classic function modules.

Data Types

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

Examples

1. Basic Date Calculations

DATA: lv_date TYPE sy-datum,
lv_days TYPE i.
" Current date
lv_date = sy-datum.
" Add/subtract days
lv_date = sy-datum + 30. " 30 days later
lv_date = sy-datum - 7. " 7 days earlier
" Difference between dates
lv_days = sy-datum - '20240101'.
WRITE: / 'Days since start of year:', lv_days.
" Decompose date
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).
" Assemble date
lv_date = |{ lv_year }{ lv_month }{ lv_day }|.

2. Month and Year Calculations

DATA: lv_date TYPE sy-datum.
" Add months
CALL FUNCTION 'RP_CALC_DATE_IN_INTERVAL'
EXPORTING
date = sy-datum
days = 0
months = 3 " 3 months
signum = '+'
years = 0
IMPORTING
calc_date = lv_date.
" Add years
CALL FUNCTION 'RP_CALC_DATE_IN_INTERVAL'
EXPORTING
date = sy-datum
days = 0
months = 0
signum = '+'
years = 1 " 1 year
IMPORTING
calc_date = lv_date.
" Get month start
DATA(lv_month_start) = sy-datum.
lv_month_start+6(2) = '01'.
" Get month end
CALL FUNCTION 'RP_LAST_DAY_OF_MONTHS'
EXPORTING
day_in = sy-datum
IMPORTING
last_day_of_month = lv_date.
" Quarter start
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. Weekday and Calendar Week

DATA: lv_weekday TYPE p,
lv_week TYPE scal-week.
" Weekday (1=Monday, 7=Sunday)
CALL FUNCTION 'DAY_IN_WEEK'
EXPORTING
datum = sy-datum
IMPORTING
woession = lv_weekday.
CASE lv_weekday.
WHEN 1. WRITE: / 'Monday'.
WHEN 2. WRITE: / 'Tuesday'.
WHEN 3. WRITE: / 'Wednesday'.
WHEN 4. WRITE: / 'Thursday'.
WHEN 5. WRITE: / 'Friday'.
WHEN 6. WRITE: / 'Saturday'.
WHEN 7. WRITE: / 'Sunday'.
ENDCASE.
" Calendar week
CALL FUNCTION 'DATE_GET_WEEK'
EXPORTING
date = sy-datum
IMPORTING
week = lv_week.
WRITE: / 'Calendar week:', lv_week. " Format: YYYYWW

4. Factory Calendar

DATA: lv_workdays TYPE i,
lv_target_date TYPE sy-datum.
" Working days between two dates
CALL FUNCTION 'RKE_SELECT_FACTDAYS_FOR_PERIOD'
EXPORTING
i_datab = '20240101'
i_datbi = '20240131'
i_factid = '01' " Factory calendar ID
IMPORTING
e_factdays = lv_workdays.
WRITE: / 'Working days in January:', lv_workdays.
" Add working days
CALL FUNCTION 'BKK_ADD_WORKINGDAY'
EXPORTING
i_date = sy-datum
i_days = 5 " 5 working days
i_calendar_id = '01'
IMPORTING
e_date = lv_target_date.
WRITE: / 'In 5 working days:', lv_target_date.
" Check if working day
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: / 'Today is a working day'.
ELSEIF sy-subrc = 4.
WRITE: / 'Today is not a working day'.
ENDIF.

5. Holidays

DATA: lt_holidays TYPE TABLE OF iscal_day.
" Holidays for a year
CALL FUNCTION 'HOLIDAY_GET'
EXPORTING
holiday_calendar = 'DE' " Holiday calendar
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.
" Check if holiday
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: / 'Today is a holiday'.
ENDIF.

6. Timestamps

DATA: lv_timestamp TYPE timestamp,
lv_timestampl TYPE timestampl.
" Current timestamp
GET TIME STAMP FIELD lv_timestampl.
WRITE: / 'Timestamp:', lv_timestampl.
" Create timestamp from date/time
CONVERT DATE sy-datum TIME sy-uzeit
INTO TIME STAMP lv_timestamp TIME ZONE sy-zonlo.
" Convert timestamp to date/time
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: / 'Date:', lv_date, 'Time:', lv_time.
" Timestamp difference
DATA: lv_ts1 TYPE timestampl,
lv_ts2 TYPE timestampl,
lv_diff TYPE timestampl.
GET TIME STAMP FIELD lv_ts1.
" ... processing ...
GET TIME STAMP FIELD lv_ts2.
lv_diff = lv_ts2 - lv_ts1. " Difference in seconds
WRITE: / 'Duration:', lv_diff, 'seconds'.

7. Time Zones

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.
" Convert to UTC
CONVERT TIME STAMP lv_timestamp TIME ZONE 'UTC'
INTO DATE lv_date_utc TIME lv_time_utc.
" Convert to local time
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: / 'Local:', lv_date_loc, lv_time_loc.
" Get time zone from plant
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. Format Date

DATA: lv_date TYPE sy-datum VALUE '20241124',
lv_formatted TYPE string.
" With string templates
lv_formatted = |{ lv_date DATE = USER }|. " User format
lv_formatted = |{ lv_date DATE = ISO }|. " 2024-11-24
lv_formatted = |{ lv_date DATE = ENVIRONMENT }|. " System format
WRITE: / lv_formatted.
" With function module
CALL FUNCTION 'CONVERT_DATE_TO_EXTERNAL'
EXPORTING
date_internal = lv_date
IMPORTING
date_external = lv_formatted.
" Custom format
lv_formatted = |{ lv_date+6(2) }.{ lv_date+4(2) }.{ lv_date(4) }|.
" 24.11.2024

9. Format Time

DATA: lv_time TYPE sy-uzeit VALUE '143052',
lv_formatted TYPE string.
" With string templates
lv_formatted = |{ lv_time TIME = USER }|.
lv_formatted = |{ lv_time TIME = ISO }|. " 14:30:52
" Custom format
lv_formatted = |{ lv_time(2) }:{ lv_time+2(2) }|. " 14:30

10. Date Validation

DATA: lv_date TYPE sy-datum VALUE '20240230'. " Invalid: Feb 30
" Check with function module
CALL FUNCTION 'DATE_CHECK_PLAUSIBILITY'
EXPORTING
date = lv_date
EXCEPTIONS
plausibility_check_failed = 1
OTHERS = 2.
IF sy-subrc <> 0.
WRITE: / 'Invalid date'.
ENDIF.
" Simple check
IF lv_date IS INITIAL OR lv_date = '00000000'.
WRITE: / 'Date is empty'.
ENDIF.
" Range check
IF lv_date < '19000101' OR lv_date > '99991231'.
WRITE: / 'Date outside valid range'.
ENDIF.

11. Date Helper Class

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 'Monday'
WHEN 2 THEN 'Tuesday'
WHEN 3 THEN 'Wednesday'
WHEN 4 THEN 'Thursday'
WHEN 5 THEN 'Friday'
WHEN 6 THEN 'Saturday'
WHEN 7 THEN 'Sunday'
).
ENDMETHOD.
ENDCLASS.
" Usage
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: / 'Next month:', lv_next_month,
/ 'Month end:', lv_month_end,
/ 'Weekday:', lv_weekday.

12. Calculate Time Difference

" Seconds between two time points
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.
" Calculate as timestamps
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: / 'Difference:', lv_hours, 'hours'.

13. Performance Measurement

DATA: lv_start TYPE timestampl,
lv_end TYPE timestampl,
lv_runtime TYPE p DECIMALS 3.
GET TIME STAMP FIELD lv_start.
" Execute code
LOOP AT lt_data INTO DATA(ls_data).
" Processing...
ENDLOOP.
GET TIME STAMP FIELD lv_end.
" Runtime in seconds
lv_runtime = cl_abap_tstmp=>subtract(
tstmp1 = lv_end
tstmp2 = lv_start
).
WRITE: / 'Runtime:', lv_runtime, 'seconds'.

14. SQL Date Functions

" Modern Open SQL with date functions
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).
" Group by month
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. Date in RAP

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

Important Function Modules

Function ModuleDescription
RP_CALC_DATE_IN_INTERVALAdd months/years
RP_LAST_DAY_OF_MONTHSMonth end
DATE_GET_WEEKCalendar week
DAY_IN_WEEKWeekday
HOLIDAY_GETHolidays
DATE_CHECK_WORKINGDAYCheck working day
BKK_ADD_WORKINGDAYAdd working days

Important Notes / Best Practice

  • sy-datum/sy-uzeit only for current time, do not manipulate.
  • Timestamps for precise time measurement and time zones.
  • Factory calendar for business logic with working days.
  • GET TIME STAMP instead of sy-uzeit for precision.
  • UTC for storage, local time for display.
  • String templates for formatting: |{ date DATE = ISO }|.
  • Date functions in SQL available from ABAP 7.50.
  • Validation before calculations.
  • Combine with Built-in Functions for more operations.