ABAP 日期和时间

ABAP 日期和时间

ℹ️概述
ABAP毫不保留地引用了公历日历,在世界大部分地区有效。我们可以将输出转换为特定国家/地区的日历。日期是指定到关于日历的精确日、周或月的时间。时间被指定为相对于一天的精确秒或分钟。ABAP总是以24小时格式保存时间。输出可以具有特定于国家的格式。日期和时间通常被解释为在当前时区有效的本地日期。

🔧 数据类型

ABAP提供了两种内置类型来处理日期和时间:

基本数据类型

1
2
3
4
5
6
7
8
9
DATA: 
date TYPE D, -- 日期类型
time TYPE T, -- 时间类型
year TYPE I, -- 年份
month TYPE I, -- 月份
day TYPE I, -- 日期
hour TYPE I, -- 小时
minute TYPE I, -- 分钟
second TYPE I. -- 秒数

数据类型格式

数据类型 格式 说明 示例
D YYYYMMDD 固定长度字符类型 20100913 表示 2010年9月13日
T HHMMSS 固定长度字符类型 102305 表示 10:23:05

⏰ 时间戳类型

除了这些内置类型,其他两种类型用于存储UTC格式的时间戳:

编号 数据类型 说明
1 TIMESTAMP 类型P - 长度8无小数
格式:YYYYMMDDhhmmss
表示短时间戳
2 TIMESTAMPL 类型P - 长度11小数7
格式:YYYYMMDDhhmmss,mmmuuun
表示长时间戳,包含秒的分数

📅 当前日期和时间

获取系统日期

1
2
3
4
5
6
7
8
REPORT YR_SEP_15.
DATA: date_1 TYPE D.
date_1 = SY-DATUM. -- 系统日期
Write: / 'Present Date is:', date_1 DD/MM/YYYY.

-- 增加日期
date_1 = date_1 + 06.
Write: / 'Date after 6 Days is:', date_1 DD/MM/YYYY.

输出

1
2
Present Date is: 21.09.2015
Date after 6 Days is: 27.09.2015
📌日期计算说明
变量date_1分配有当前系统日期SY-DATUM的值。对于ABAP中的日期计算,这意味着我们将日期对象的日期组件增加6天。ABAP运行时环境足够聪明,可以在每月结束时翻转日期值。

时间计算

1
2
3
4
5
6
7
8
REPORT YR_SEP_15.
DATA: time_1 TYPE T.
time_1 = SY-UZEIT. -- 系统时间
Write /(60) time_1 USING EDIT MASK 'Now the Time is: __:__:__'.

-- 增加时间(75秒)
time_1 = time_1 + 75.
Write /(60) time_1 USING EDIT MASK 'A Minute and a Quarter from Now, it is: __:__:__'.

输出

1
2
Now the Time is: 11:45:05
A Minute and a Quarter from Now, it is: 11:46:20

🕐 时间戳操作

使用GET TIME STAMP

1
2
3
4
5
6
7
8
9
10
11
REPORT YR_SEP_12.
DATA:
stamp_1 TYPE TIMESTAMP, -- 短时间戳
stamp_2 TYPE TIMESTAMPL. -- 长时间戳

-- 获取当前时间戳
GET TIME STAMP FIELD stamp_1.
Write: / 'The short time stamp is:', stamp_1 TIME ZONE SY-ZONLO.

GET TIME STAMP FIELD stamp_2.
Write: / 'The long time stamp is:', stamp_2 TIME ZONE SY-ZONLO.

输出

1
2
The short time stamp is: 18.09.2015 11:19:40
The long time stamp is: 18.09.2015 11:19:40,9370000
📌时区处理
在上面的示例中,我们使用TIME ZONE添加WRITE语句显示时间戳。此添加根据指定的时区的规则格式化时间戳的输出。系统字段SY-ZONLO用于显示在用户首选项中配置的本地时区。

🔍 日期时间函数

1. 日期格式化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
DATA: 
ld_date TYPE D VALUE '20240315',
ld_char(10).

-- 格式化为 DD/MM/YYYY
ld_char = ld_date+6(2) && '/' && ld_date+4(2) && '/' && ld_date(4).
Write: / 'Formatted Date:', ld_char. -- 15/03/2024

-- 使用转换函数
CALL FUNCTION 'CONVERT_DATE_TO_EXTERNAL'
EXPORTING
date_internal = ld_date
IMPORTING
date_external = ld_char.

Write: / 'External Format:', ld_char.

2. 时间计算

1
2
3
4
5
6
7
8
9
10
11
12
13
DATA: 
ld_date TYPE D,
ld_days TYPE I VALUE 30,
ld_result TYPE D.

ld_date = sy-datum.
-- 增加30天
ld_result = ld_date + ld_days.
Write: / 'After 30 days:', ld_result.

-- 计算2周前
ld_result = ld_date - 14.
Write: / '2 weeks ago:', ld_result.

3. 时间差计算

1
2
3
4
5
6
7
8
DATA: 
ld_date1 TYPE D VALUE '20240301',
ld_date2 TYPE D VALUE '20240331',
ld_days_diff TYPE I.

-- 计算天数差
ld_days_diff = ld_date2 - ld_date1.
Write: / 'Days between dates:', ld_days_diff.

🌐 时区处理

获取时区信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
DATA: 
lv_time_zone TYPE TIMEZONE,
lv_timestamp TYPE TIMESTAMP.

-- 获取系统时区
lv_time_zone = sy-zonlo.
Write: / 'System Time Zone:', lv_time_zone.

-- 转换时区
GET TIME STAMP FIELD lv_timestamp.
CONVERT TIME STAMP lv_timestamp
TIME ZONE 'UTC'
INTO DATE DATA(ld_date_utc)
TIME DATA(ld_time_utc).

Write: / 'UTC Date:', ld_date_utc.
Write: / 'UTC Time:', ld_time_utc.

时区转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
DATA: 
lv_timestamp TYPE TIMESTAMP,
lv_date TYPE D,
lv_time TYPE T,
lv_tzone TYPE TIMEZONE VALUE 'CET'. -- 中欧时间

-- 获取当前时间戳
GET TIME STAMP FIELD lv_timestamp.

-- 转换为指定时区
CONVERT TIME STAMP lv_timestamp
TIME ZONE lv_tzone
INTO lv_date lv_time.

Write: / 'Time in CET:', lv_date, lv_time.

📊 实际应用场景

1. 业务日期处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
" 计算工作日
DATA:
ld_start_date TYPE D VALUE '20240301',
ld_end_date TYPE D,
ld_work_days TYPE I.

ld_end_date = ld_start_date + 30. -- 项目截止日期

" 计算工作日(排除周末)
DO 31 TIMES.
IF ld_start_date+6(2) CO '012345' AND " 周一到周五
ld_start_date+6(2) <> '00'. " 不是周末
ADD 1 TO ld_work_days.
ENDIF.
ADD 1 TO ld_start_date.
ENDDO.

Write: / 'Total Work Days:', ld_work_days.

2. 时间戳记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
" 记录操作时间戳
DATA:
lv_timestamp TYPE TIMESTAMP,
lv_operation_log TYPE TABLE OF tsmd_log.

" 获取操作开始时间
GET TIME STAMP FIELD lv_timestamp.

" 记录日志
APPEND VALUE #(
uname = sy-uname
timestampl = lv_timestamp
action = 'CREATE_ORDER'
) TO lv_operation_log.

3. 日期验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
" 验证日期有效性
DATA:
ld_input_date TYPE D VALUE '20240230',
lv_valid TYPE ABAP_BOOL.

" 检查日期是否有效
CALL FUNCTION 'DATE_CHECK_PLAUSIBILITY'
EXPORTING
date = ld_input_date
IMPORTING
plausible = lv_valid.

IF lv_valid = abap_true.
Write: / 'Date is valid'.
ELSE.
Write: / 'Date is invalid'.
ENDIF.

⚠️ 注意事项

1. 日期范围

1
2
3
4
5
6
" ❌ 危险:可能产生无效日期
DATA: ld_date TYPE D.
ld_date = '20241301'. -- 无效的月份

" ✅ 安全:使用系统日期
ld_date = sy-datum.

2. 时区转换

1
2
3
4
5
6
7
8
" 注意时区对时间计算的影响
DATA:
lv_utc_time TYPE T VALUE '235959',
lv_local_time TYPE T.

" 考虑时区差异进行转换
" 例如UTC+8时区,需要加8小时
lv_local_time = lv_utc_time + 80000.

3. 时间戳精度

1
2
3
4
5
6
7
8
9
10
11
" TIMESTAMP精度:秒
" TIMESTAMPL精度:微秒
DATA:
lv_short TYPE TIMESTAMP,
lv_long TYPE TIMESTAMPL.

GET TIME STAMP FIELD lv_short.
GET TIME STAMP FIELD lv_long.

Write: / 'Short precision:', lv_short.
Write: / 'Long precision:', lv_long.

💡 性能优化

1. 批量日期处理



graph LR
    A[单条日期处理] --> B[性能问题]
    B --> C[批量处理优化]
    C --> D[减少函数调用]
    C --> E[使用内表循环]

2. 缓存常用日期

1
2
3
4
5
6
7
8
9
" 缓存频繁使用的日期
DATA: gv_current_date TYPE D VALUE sy-datum.
DATA: gv_fiscal_year TYPE N VALUE sy-datum(4).

" 使用缓存而不是每次都获取
IF gv_current_date <> sy-datum.
gv_current_date = sy-datum.
gv_fiscal_year = sy-datum(4).
ENDIF.

3. 时区处理优化

1
2
3
4
5
6
7
8
9
10
" 减少时区转换次数
DATA:
lv_timezone TYPE TIMEZONE VALUE sy-zonlo,
lv_timestamp TYPE TIMESTAMP.

" 一次性转换
GET TIME STAMP FIELD lv_timestamp.
CONVERT TIME STAMP lv_timestamp
TIME ZONE lv_timezone
INTO DATA(ld_date) DATA(ld_time).

💡 最佳实践

1. 日期处理原则

  • 使用标准数据类型(D、T)
  • 验证日期的有效性
  • 注意闰年和其他特殊情况

2. 时间处理原则

  • 使用24小时格式
  • 考虑时区差异
  • 正确处理时间溢出

3. 时间戳使用

  • 根据精度需求选择类型
  • 考虑存储空间
  • 注意UTC与本地时间的转换

4. 性能考虑

  • 减少不必要的日期时间转换
  • 使用批量处理
  • 缓存频繁使用的日期时间值