ABAP Date & Time Operations: Calculos de fechas, Timestamps

Kategorie
ABAP-Statements
Veröffentlicht
Autor
Johannes

Date & Time Operations en ABAP incluyen calculos de fechas, calendarios de fabrica, timestamps y conversion de zonas horarias. Las clases modernas como CL_ABAP_DATFM complementan los modulos de funciones clasicos.

Tipos de datos

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

Ejemplos

1. Calculos basicos de fechas

DATA: lv_date TYPE sy-datum,
lv_days TYPE i.
" Fecha actual
lv_date = sy-datum.
" Sumar/restar dias
lv_date = sy-datum + 30. " 30 dias despues
lv_date = sy-datum - 7. " 7 dias antes
" Diferencia entre fechas
lv_days = sy-datum - '20240101'.
WRITE: / 'Dias desde inicio de ano:', lv_days.
" Descomponer fecha
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).
" Componer fecha
lv_date = |{ lv_year }{ lv_month }{ lv_day }|.

2. Calculos de meses y anos

DATA: lv_date TYPE sy-datum.
" Sumar meses
CALL FUNCTION 'RP_CALC_DATE_IN_INTERVAL'
EXPORTING
date = sy-datum
days = 0
months = 3 " 3 meses
signum = '+'
years = 0
IMPORTING
calc_date = lv_date.
" Sumar anos
CALL FUNCTION 'RP_CALC_DATE_IN_INTERVAL'
EXPORTING
date = sy-datum
days = 0
months = 0
signum = '+'
years = 1 " 1 ano
IMPORTING
calc_date = lv_date.
" Obtener inicio de mes
DATA(lv_month_start) = sy-datum.
lv_month_start+6(2) = '01'.
" Obtener fin de mes
CALL FUNCTION 'RP_LAST_DAY_OF_MONTHS'
EXPORTING
day_in = sy-datum
IMPORTING
last_day_of_month = lv_date.
" Inicio de trimestre
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. Dia de la semana y semana calendario

DATA: lv_weekday TYPE p,
lv_week TYPE scal-week.
" Dia de la semana (1=Lunes, 7=Domingo)
CALL FUNCTION 'DAY_IN_WEEK'
EXPORTING
datum = sy-datum
IMPORTING
woession = lv_weekday.
CASE lv_weekday.
WHEN 1. WRITE: / 'Lunes'.
WHEN 2. WRITE: / 'Martes'.
WHEN 3. WRITE: / 'Miercoles'.
WHEN 4. WRITE: / 'Jueves'.
WHEN 5. WRITE: / 'Viernes'.
WHEN 6. WRITE: / 'Sabado'.
WHEN 7. WRITE: / 'Domingo'.
ENDCASE.
" Semana calendario
CALL FUNCTION 'DATE_GET_WEEK'
EXPORTING
date = sy-datum
IMPORTING
week = lv_week.
WRITE: / 'Semana calendario:', lv_week. " Formato: YYYYWW

4. Calendario de fabrica

DATA: lv_workdays TYPE i,
lv_target_date TYPE sy-datum.
" Dias laborables entre dos fechas
CALL FUNCTION 'RKE_SELECT_FACTDAYS_FOR_PERIOD'
EXPORTING
i_datab = '20240101'
i_datbi = '20240131'
i_factid = '01' " ID de calendario de fabrica
IMPORTING
e_factdays = lv_workdays.
WRITE: / 'Dias laborables en enero:', lv_workdays.
" Sumar dias laborables
CALL FUNCTION 'BKK_ADD_WORKINGDAY'
EXPORTING
i_date = sy-datum
i_days = 5 " 5 dias laborables
i_calendar_id = '01'
IMPORTING
e_date = lv_target_date.
WRITE: / 'En 5 dias laborables:', lv_target_date.
" Verificar si es dia laborable
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: / 'Hoy es dia laborable'.
ELSEIF sy-subrc = 4.
WRITE: / 'Hoy no es dia laborable'.
ENDIF.

5. Dias festivos

DATA: lt_holidays TYPE TABLE OF iscal_day.
" Dias festivos para un ano
CALL FUNCTION 'HOLIDAY_GET'
EXPORTING
holiday_calendar = 'DE' " Calendario de festivos
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.
" Verificar si es dia festivo
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: / 'Hoy es dia festivo'.
ENDIF.

6. Timestamps

DATA: lv_timestamp TYPE timestamp,
lv_timestampl TYPE timestampl.
" Timestamp actual
GET TIME STAMP FIELD lv_timestampl.
WRITE: / 'Timestamp:', lv_timestampl.
" Crear timestamp desde fecha/hora
CONVERT DATE sy-datum TIME sy-uzeit
INTO TIME STAMP lv_timestamp TIME ZONE sy-zonlo.
" Convertir timestamp a fecha/hora
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: / 'Fecha:', lv_date, 'Hora:', lv_time.
" Diferencia de timestamps
DATA: lv_ts1 TYPE timestampl,
lv_ts2 TYPE timestampl,
lv_diff TYPE timestampl.
GET TIME STAMP FIELD lv_ts1.
" ... Procesamiento ...
GET TIME STAMP FIELD lv_ts2.
lv_diff = lv_ts2 - lv_ts1. " Diferencia en segundos
WRITE: / 'Duracion:', lv_diff, 'Segundos'.

7. Zonas horarias

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.
" Convertir a UTC
CONVERT TIME STAMP lv_timestamp TIME ZONE 'UTC'
INTO DATE lv_date_utc TIME lv_time_utc.
" Convertir a hora local
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.
" Obtener zona horaria de planta
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. Formatear fecha

DATA: lv_date TYPE sy-datum VALUE '20241124',
lv_formatted TYPE string.
" Con String Templates
lv_formatted = |{ lv_date DATE = USER }|. " Formato usuario
lv_formatted = |{ lv_date DATE = ISO }|. " 2024-11-24
lv_formatted = |{ lv_date DATE = ENVIRONMENT }|. " Formato sistema
WRITE: / lv_formatted.
" Con modulo de funciones
CALL FUNCTION 'CONVERT_DATE_TO_EXTERNAL'
EXPORTING
date_internal = lv_date
IMPORTING
date_external = lv_formatted.
" Formato propio
lv_formatted = |{ lv_date+6(2) }.{ lv_date+4(2) }.{ lv_date(4) }|.
" 24.11.2024

9. Formatear hora

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

10. Validacion de fecha

DATA: lv_date TYPE sy-datum VALUE '20240230'. " Invalido: 30 de Feb
" Verificacion con modulo de funciones
CALL FUNCTION 'DATE_CHECK_PLAUSIBILITY'
EXPORTING
date = lv_date
EXCEPTIONS
plausibility_check_failed = 1
OTHERS = 2.
IF sy-subrc <> 0.
WRITE: / 'Fecha invalida'.
ENDIF.
" Verificacion simple
IF lv_date IS INITIAL OR lv_date = '00000000'.
WRITE: / 'Fecha esta vacia'.
ENDIF.
" Verificar rango
IF lv_date < '19000101' OR lv_date > '99991231'.
WRITE: / 'Fecha fuera del rango valido'.
ENDIF.

11. Clase auxiliar de fechas

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 'Lunes'
WHEN 2 THEN 'Martes'
WHEN 3 THEN 'Miercoles'
WHEN 4 THEN 'Jueves'
WHEN 5 THEN 'Viernes'
WHEN 6 THEN 'Sabado'
WHEN 7 THEN 'Domingo'
).
ENDMETHOD.
ENDCLASS.
" Uso
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: / 'Proximo mes:', lv_next_month,
/ 'Fin de mes:', lv_month_end,
/ 'Dia de la semana:', lv_weekday.

12. Calcular diferencia de tiempo

" Segundos entre dos momentos
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.
" Calcular como 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: / 'Diferencia:', lv_hours, 'Horas'.

13. Medicion de rendimiento

DATA: lv_start TYPE timestampl,
lv_end TYPE timestampl,
lv_runtime TYPE p DECIMALS 3.
GET TIME STAMP FIELD lv_start.
" Ejecutar codigo
LOOP AT lt_data INTO DATA(ls_data).
" Procesamiento...
ENDLOOP.
GET TIME STAMP FIELD lv_end.
" Tiempo de ejecucion en segundos
lv_runtime = cl_abap_tstmp=>subtract(
tstmp1 = lv_end
tstmp2 = lv_start
).
WRITE: / 'Tiempo de ejecucion:', lv_runtime, 'Segundos'.

14. Funciones de fecha SQL

" Open SQL moderno con funciones de fecha
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).
" Agrupacion por mes
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. Fecha en RAP

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

Modulos de funciones importantes

Modulo de funcionesDescripcion
RP_CALC_DATE_IN_INTERVALSumar meses/anos
RP_LAST_DAY_OF_MONTHSFin de mes
DATE_GET_WEEKSemana calendario
DAY_IN_WEEKDia de la semana
HOLIDAY_GETDias festivos
DATE_CHECK_WORKINGDAYVerificar dia laborable
BKK_ADD_WORKINGDAYSumar dias laborables

Notas importantes / Mejores practicas

  • sy-datum/sy-uzeit solo para hora actual, no manipular.
  • Timestamps para medicion de tiempo precisa y zonas horarias.
  • Calendario de fabrica para logica de negocio con dias laborables.
  • GET TIME STAMP en lugar de sy-uzeit para precision.
  • UTC para almacenamiento, hora local para visualizacion.
  • String Templates para formateo: |{ date DATE = ISO }|.
  • Funciones de fecha en SQL disponibles desde ABAP 7.50.
  • Validacion realizar antes de calculos.
  • Combina con Built-in Functions para mas operaciones.