0

我需要一个 iSeries DB2 SQL 查询,它可以将此时间戳1/1/1900 12:00:00 AM转换为00:00:00(处理 12 小时到 24 小时的时间转换)。我使用的标量函数不处理这种时间格式,我设法支持它的唯一方法是使用以下查询:

SELECT (
    CASE 
    WHEN LOCATE('PM', '1/12/1900 12:00:00 AM') = 0 THEN 
    REPLACE(CHAR(TIME(TIMESTAMP_FORMAT('1/12/1900 12:00:00 AM', 'DD/MM/YYYY HH24:MI:SS'))) , '.', ':' )
    ELSE
    REPLACE(CHAR(TIME(TIMESTAMP_FORMAT('1/12/1900 12:00:00 AM', 'DD/MM/YYYY HH24:MI:SS')) + 12 HOURS) , '.', ':' )
    END) 
FROM SYSIBM.SYSDUMMY1;

有没有更好的方法来实现目标?

编辑 2014 年 6 月 6 日

我发现可以在连接字符串中设置日期格式(ISO,USA,...),这将解决我目前遇到的所有问题,但是,我还没有找到正确的参数名称来实现这一点。

4

2 回答 2

1

假设您在连接上指定了正确的值以指示正确的日期和时间格式,如果您可以分别转换/获取日期和时间值,则可以使用以下查询转换为所需的格式:

select to_char( timestamp( date('12/12/1901') , time('12:00 PM') ),  'YYYY-MM-DD HH24:MI:SS')
from sysibm/sysdummy1;
于 2015-05-28T07:40:49.563 回答
1

以下表达式应足以满足 OP 所表达的目的;即仅获取字符串格式HH:MM:SS[其中HH代表00to 24] 的 TIME 值,该值源自作为有效时间戳的字符串值,其格式MM/DD/YYYY HH:MI:SS AMMM[ie just M] 和DD[ie just D] 的单个数字变体必须得到支持。例如,列名ts_strVARCHAR(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:SSHH

现在,关于 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
于 2016-10-13T04:57:26.210 回答