以下表达式应足以满足 OP 所表达的目的;即仅获取字符串格式HH:MM:SS
[其中HH
代表00
to 24
] 的 TIME 值,该值源自作为有效时间戳的字符串值,其格式MM/DD/YYYY HH:MI:SS AM
为MM
[ie just M
] 和DD
[ie just D
] 的单个数字变体必须得到支持。例如,列名ts_str
是VARCHAR(22)
:
char( time( insert( right( ts_str, 11 ), 6, 3, '' ) )
+ int( left( right( ts_str, 05 ), 2 ) ) seconds
, JIS )
在上面的表达式中,最右边的 11 个字符是格式 中的时间HH:MM:SS AM
部分,其中:SS
被 INSERT 标量截断,因此留下一个有效的*USA
时间字符串值HH:SS AM
,该字符串被转换为 TIME 值时间投射标量。然后第二行选择该SS
值,将其转换为具有 INT 转换标量的整数,该值是以 SECONDS 为单位的持续时间添加到派生的 TIME。最后,使用[ 00 到 24]的*JIS
格式将调整后的时间值 [即加回的秒数] 转换为字符。HH:MM:SS
HH
现在,关于 TIMESTAMP_FORMAT:
IBM i 7.1 的 DB2 根据更新的文档IBM i 7.1-> Database-> Reference-> SQL reference-> Built-in functions-> Scalar functions-> TIMESTAMP_FORMAT,显然具有某种程度的技术刷新或累积级别,'AM'
在格式字符串中增加了对子午线指标 [ie ] 的支持。尽管在 IBM i 7.3 上已确认以下表达式,但可以将来自 OP 的类似时间戳的字符串正确转换为TIMESTAMP:
to_date( ts_str, 'MM/DD/YYYY HH:MI:SS AM')
因此,使用 TIMESTAMP_FORMAT [在上面显示为 TO_DATE],给定ts_str
表示 OP 中描述的输入值,应该能够直接从这些输入时间戳字符串派生 TIMESTAMP,只指定带有格式字符串的标量作为第二个参数,输入字符串作为第一个参数。很简单,如果有的话。
注意:我不知道该版本中该功能可用的 v7r1 上的 TR 或累积 PTF 级别;也许在 IBM i 7.1 知识中心和/或维护级别中查找“截至 2016 年 4 月的新增功能”,两者都在此处提及:TO_CHAR VARCHAR_FORMAT enhancement for numeric editing format-string
以下脚本包含一个设置,然后测试两个不同的表达式以验证结果;这些是前面/上面提到的表达式。然后在后面的示例中使用相同的设置,如果TIMESTAMP_FORMAT
[aka TO_DATE
] 增强功能不可用 [例如,在较旧的 v7r1 维护级别,或在永远不会看到增强功能的 v6r1 上],或者像在以前的版本中一样甚至没有可用的 TIMESTAMP_FORMAT 函数,例如用于验证后三个脚本的 v5r3:
create table so24067379
( ts_str for ts varchar(26) /* `for ts`-short name ref'd later */
, ts_val timestamp
)
;
insert into so24067379 values
( '1/2/1900 12:00:00 AM', '1900-01-02-00.00.00.000000')
, ( '1/12/2012 12:00:00 AM', '2012-01-12-00.00.00.000000')
, ('12/12/1900 12:00:00 AM', '1900-12-12-00.00.00.000000')
, ( '1/2/2012 12:07:00 AM', '2012-01-02-00.07.00.000000')
, ( '7/12/1900 01:07:08 AM', '1900-07-12-01.07.08.000000')
, ( '12/2/2012 01:07:08 AM', '2012-12-02-01.07.08.000000')
, ('12/12/2012 01:07:08 AM', '2012-12-12-01.07.08.000000')
, ( '1/12/1900 12:13:14 PM', '1900-01-12-12.13.14.000000')
, ( '7/2/2012 12:13:14 PM', '2012-07-02-12.13.14.000000')
, ('12/12/1900 12:13:14 PM', '1900-12-12-12.13.14.000000')
, ( '1/2/2012 01:00:00 PM', '2012-01-02-13.00.00.000000')
, ('12/12/1900 01:14:15 PM', '1900-12-12-13.14.15.000000')
, ( '1/2/2012 07:20:21 PM', '2012-01-02-19.20.21.000000')
, ( '1/12/2012 07:20:21 PM', '2012-01-12-19.20.21.000000')
;
create view test0 as
( select m.*
/* , to_date( ts_str, 'MM/DD/YYYY HH:MI:SS AM') */
, TIMESTAMP
( date( to_date( ts, 'MM/DD/YYYY' ) )
, time( insert( right( ts, 11 ), 6, 3, '' ) )
+ int( left( right( ts, 05 ), 2 ) ) seconds
) as ts_tst
from so24067379 as m
)
; -- functional on v6r1 and above per no reference to Meridian enhancement
select * from test0
where ts_val <> ts_tst
; -- above query returns zero rows as verification of adjusted TIME
create view test1 ( ts_str, ts_val, ts_tst ) as
( select m.*
, to_date( ts_str, 'MM/DD/YYYY HH:MI:SS AM')
from so24067379 as m
)
; -- requires v7r1 **plus** Meridian enhancement
select * from test1 where ts_val <> ts_tst
; -- above query returns zero rows as verification of TO_DATE
以下两个没有使用 TO_DATE 的替代方案在概念上实际上是相同的;每个人都只是为相同的逻辑选择不同的地方来调整时间。第一个可能更漂亮,但重复了更多的表达式,因为任务首先被拆分为 AM 和 PM,然后都生成时间值,然后调整小时数,具体取决于前两位数字是否是'12':
create view test2 ( ts_str, ts_val, ts_tst ) as
( select
m.* /* , to_date( ts_str, 'MM/DD/YYYY HH:MI:SS AM') */
, (TIMESTAMP
( date( left( ts, locate(' ', ts) - 1 ) )
, case right( ts, 2 )
when 'PM'
then time( substr( ts, locate(' ', ts) + 1 , 8 )
) + case substr( ts, locate(' ', ts) + 1 , 2 )
when '12' then 0 else 12 end HOURS
when 'AM'
then time( substr( ts, locate(' ', ts) + 1 , 8 )
) - case substr( ts, locate(' ', ts) + 1 , 2 )
when '12' then 12 else 0 end HOURS
end
)
) as true_ts
from so24067379 as m
)
;
select * from test2 where ts_val <> ts_tst
-- above query returns zero rows as verification of TO_DATE replacement
这种变化利用了这样一个事实,即所有时间值都将作为HH:MM:SS
[对于 HH 01 到 12] 有效,尽管根据 Meridian 规范的待定调整不正确;首先计算 TIME 值,然后使用逻辑来决定调整该时间值需要多少小时。第一个测试是针对以“12”开始的时间,“AM”值必须减去 12 小时才能得到 00 小时,但“PM”值不需要进行调整;然后对于任何其他“PM”值[即 01 到 11],必须添加 12 小时,最后对于任何其他“AM”值[即 01 到 11],无需进行调整:
create view test3 ( ts_str, ts_val, ts_tst ) as
( select
m.* /* , to_date( ts_str, 'MM/DD/YYYY HH:MI:SS AM') */
, ( TIMESTAMP
( date( left( ts, locate(' ', ts) - 1 ) )
, time( substr( ts, locate(' ', ts) + 1 , 8 ) )
+ case
when substr( ts, locate(' ', ts) + 1 , 2 ) = '12'
then case when right( ts, 2 ) = 'AM' then -12
else 0 /* must be 12:xx PM */ end
when right( ts, 2 ) = 'PM' then 12
else 0
end HOURS
)
) as true_ts
from so24067379 as m
)
;
select * from test3 where ts_val <> ts_tst
; -- above query returns zero rows as verification of TO_DATE replacement
虽然以下内容更简洁,并且使用提供的原始表达式来获取 TIME 来代替 OP 显示的内容,但我将这个示例放在最后,因为虽然表达式很好,但逻辑不是很直观从阅读使用 CASE 表达式的先前表达式可能会直觉到:
create view test4 as
( select m.*
/* , to_date( ts_str, 'MM/DD/YYYY HH:MI:SS AM') */
, TIMESTAMP
( date( left( ts, locate(' ' , ts ) ) )
, time( insert( right( ts, 11 ), 6, 3, '' ) )
+ int( left( right( ts, 05 ), 2 ) ) seconds
) as ts_tst
from so24067379 as m
)
;
select * from test4
where ts_val <> ts_tst
; -- above query returns zero rows as verification of TO_DATE replacement