Operations Date & Time ABAP : calculs de dates, Timestamps

Catégorie
ABAP-Statements
Publié
Auteur
Johannes

Les operations Date & Time en ABAP comprennent les calculs de dates, le calendrier d’usine, les Timestamps et la conversion de fuseaux horaires. Les classes modernes comme CL_ABAP_DATFM completent les modules fonctionnels classiques.

Types de donnees

TypeFormatExemple
D (DATS)AAAAMMJJ20241124
T (TIMS)HHMMSS143052
TIMESTAMPLAAAAMMJJHHMMSS.sssssss20241124143052.1234567
TIMESTAMPAAAAMMJJHHMMSS20241124143052

Exemples

1. Calculs de dates de base

DATA: lv_date TYPE sy-datum,
lv_days TYPE i.
" Date actuelle
lv_date = sy-datum.
" Ajouter/soustraire des jours
lv_date = sy-datum + 30. " 30 jours plus tard
lv_date = sy-datum - 7. " 7 jours plus tot
" Difference entre dates
lv_days = sy-datum - '20240101'.
WRITE: / 'Jours depuis le debut de l annee:', lv_days.
" Decomposer la 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).
" Composer la date
lv_date = |{ lv_year }{ lv_month }{ lv_day }|.

2. Calculs de mois et d’annees

DATA: lv_date TYPE sy-datum.
" Ajouter des mois
CALL FUNCTION 'RP_CALC_DATE_IN_INTERVAL"
EXPORTING
date = sy-datum
days = 0
months = 3 " 3 mois
signum = '+"
years = 0
IMPORTING
calc_date = lv_date.
" Ajouter des annees
CALL FUNCTION 'RP_CALC_DATE_IN_INTERVAL"
EXPORTING
date = sy-datum
days = 0
months = 0
signum = '+"
years = 1 " 1 an
IMPORTING
calc_date = lv_date.
" Determiner le debut du mois
DATA(lv_month_start) = sy-datum.
lv_month_start+6(2) = '01'.
" Determiner la fin du mois
CALL FUNCTION 'RP_LAST_DAY_OF_MONTHS"
EXPORTING
day_in = sy-datum
IMPORTING
last_day_of_month = lv_date.
" Debut du 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. Jour de la semaine et semaine calendaire

DATA: lv_weekday TYPE p,
lv_week TYPE scal-week.
" Jour de la semaine (1=lundi, 7=dimanche)
CALL FUNCTION 'DAY_IN_WEEK"
EXPORTING
datum = sy-datum
IMPORTING
woession = lv_weekday.
CASE lv_weekday.
WHEN 1. WRITE: / 'Lundi'.
WHEN 2. WRITE: / 'Mardi'.
WHEN 3. WRITE: / 'Mercredi'.
WHEN 4. WRITE: / 'Jeudi'.
WHEN 5. WRITE: / 'Vendredi'.
WHEN 6. WRITE: / 'Samedi'.
WHEN 7. WRITE: / 'Dimanche'.
ENDCASE.
" Semaine calendaire
CALL FUNCTION 'DATE_GET_WEEK"
EXPORTING
date = sy-datum
IMPORTING
week = lv_week.
WRITE: / 'Semaine calendaire:', lv_week. " Format : AAAASS

4. Calendrier d’usine

DATA: lv_workdays TYPE i,
lv_target_date TYPE sy-datum.
" Jours ouvres entre deux dates
CALL FUNCTION 'RKE_SELECT_FACTDAYS_FOR_PERIOD"
EXPORTING
i_datab = '20240101"
i_datbi = '20240131"
i_factid = '01' " ID du calendrier d'usine
IMPORTING
e_factdays = lv_workdays.
WRITE: / 'Jours ouvres en janvier:', lv_workdays.
" Ajouter des jours ouvres
CALL FUNCTION 'BKK_ADD_WORKINGDAY"
EXPORTING
i_date = sy-datum
i_days = 5 " 5 jours ouvres
i_calendar_id = '01"
IMPORTING
e_date = lv_target_date.
WRITE: / 'Dans 5 jours ouvres:', lv_target_date.
" Verifier si c'est un jour ouvre
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: / 'Aujourd hui est un jour ouvre'.
ELSEIF sy-subrc = 4.
WRITE: / 'Aujourd hui n est pas un jour ouvre'.
ENDIF.

5. Jours feries

DATA: lt_holidays TYPE TABLE OF iscal_day.
" Jours feries pour une annee
CALL FUNCTION 'HOLIDAY_GET"
EXPORTING
holiday_calendar = 'FR' " Calendrier des jours feries
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.
" Verifier si c'est un jour ferie
CALL FUNCTION 'HOLIDAY_CHECK_AND_GET_INFO"
EXPORTING
date = sy-datum
holiday_calendar_id = 'FR"
IMPORTING
holiday_found = DATA(lv_is_holiday)
EXCEPTIONS
OTHERS = 1.
IF lv_is_holiday = abap_true.
WRITE: / 'Aujourd hui est un jour ferie'.
ENDIF.

6. Timestamps

DATA: lv_timestamp TYPE timestamp,
lv_timestampl TYPE timestampl.
" Timestamp actuel
GET TIME STAMP FIELD lv_timestampl.
WRITE: / 'Timestamp:', lv_timestampl.
" Creer un Timestamp a partir de date/heure
CONVERT DATE sy-datum TIME sy-uzeit
INTO TIME STAMP lv_timestamp TIME ZONE sy-zonlo.
" Convertir un Timestamp en date/heure
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, 'Heure:', lv_time.
" Difference de Timestamps
DATA: lv_ts1 TYPE timestampl,
lv_ts2 TYPE timestampl,
lv_diff TYPE timestampl.
GET TIME STAMP FIELD lv_ts1.
" ... Traitement ...
GET TIME STAMP FIELD lv_ts2.
lv_diff = lv_ts2 - lv_ts1. " Difference en secondes
WRITE: / 'Duree:', lv_diff, 'secondes'.

7. Fuseaux horaires

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 en UTC
CONVERT TIME STAMP lv_timestamp TIME ZONE 'UTC"
INTO DATE lv_date_utc TIME lv_time_utc.
" Convertir en heure locale
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.
" Determiner le fuseau horaire a partir d'une usine
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. Formater une date

DATA: lv_date TYPE sy-datum VALUE '20241124',
lv_formatted TYPE string.
" Avec les String Templates
lv_formatted = |{ lv_date DATE = USER }|. " Format utilisateur
lv_formatted = |{ lv_date DATE = ISO }|. " 2024-11-24
lv_formatted = |{ lv_date DATE = ENVIRONMENT }|. " Format systeme
WRITE: / lv_formatted.
" Avec un module fonctionnel
CALL FUNCTION 'CONVERT_DATE_TO_EXTERNAL"
EXPORTING
date_internal = lv_date
IMPORTING
date_external = lv_formatted.
" Format personnalise
lv_formatted = |{ lv_date+6(2) }.{ lv_date+4(2) }.{ lv_date(4) }|.
" 24.11.2024

9. Formater une heure

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

10. Validation de date

DATA: lv_date TYPE sy-datum VALUE '20240230'. " Invalide : 30 fevrier
" Verification avec module fonctionnel
CALL FUNCTION 'DATE_CHECK_PLAUSIBILITY"
EXPORTING
date = lv_date
EXCEPTIONS
plausibility_check_failed = 1
OTHERS = 2.
IF sy-subrc <> 0.
WRITE: / 'Date invalide'.
ENDIF.
" Verification simple
IF lv_date IS INITIAL OR lv_date = '00000000'.
WRITE: / 'La date est vide'.
ENDIF.
" Verifier la plage
IF lv_date < '19000101' OR lv_date > '99991231'.
WRITE: / 'Date en dehors de la plage valide'.
ENDIF.

11. Classe d’aide pour les dates

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 'Lundi"
WHEN 2 THEN 'Mardi"
WHEN 3 THEN 'Mercredi"
WHEN 4 THEN 'Jeudi"
WHEN 5 THEN 'Vendredi"
WHEN 6 THEN 'Samedi"
WHEN 7 THEN 'Dimanche"
).
ENDMETHOD.
ENDCLASS.
" Utilisation
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: / 'Mois prochain:', lv_next_month,
/ 'Fin du mois:', lv_month_end,
/ 'Jour de la semaine:', lv_weekday.

12. Calculer la difference de temps

" Secondes entre deux moments
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.
" Calculer comme 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, 'heures'.

13. Mesure de performance

DATA: lv_start TYPE timestampl,
lv_end TYPE timestampl,
lv_runtime TYPE p DECIMALS 3.
GET TIME STAMP FIELD lv_start.
" Executer le code
LOOP AT lt_data INTO DATA(ls_data).
" Traitement...
ENDLOOP.
GET TIME STAMP FIELD lv_end.
" Duree en secondes
lv_runtime = cl_abap_tstmp=>subtract(
tstmp1 = lv_end
tstmp2 = lv_start
).
WRITE: / 'Duree:', lv_runtime, 'secondes'.

14. Fonctions de date SQL

" Open SQL moderne avec fonctions de date
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).
" Regroupement par mois
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 dans RAP

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

Modules fonctionnels importants

Module fonctionnelDescription
RP_CALC_DATE_IN_INTERVALAjouter des mois/annees
RP_LAST_DAY_OF_MONTHSFin du mois
DATE_GET_WEEKSemaine calendaire
DAY_IN_WEEKJour de la semaine
HOLIDAY_GETJours feries
DATE_CHECK_WORKINGDAYVerifier jour ouvre
BKK_ADD_WORKINGDAYAjouter des jours ouvres

Remarques importantes / Bonnes pratiques

  • sy-datum/sy-uzeit uniquement pour l’heure actuelle, ne pas modifier.
  • Timestamps pour la mesure precise du temps et les fuseaux horaires.
  • Calendrier d’usine pour la logique metier avec les jours ouvres.
  • GET TIME STAMP au lieu de sy-uzeit pour la precision.
  • UTC pour le stockage, heure locale pour l’affichage.
  • String Templates pour le formatage : |{ date DATE = ISO }|.
  • Fonctions de date en SQL disponibles depuis ABAP 7.50.
  • Validation avant les calculs.
  • Combinez avec les Built-in Functions pour d’autres operations.