6

我正在使用 2 个字段,这些字段存储为 smallint 军事结构化时间。
编辑我在 IBM Informix Dynamic Server 版本 10.00.FC9 上运行

beg_tm 和 end_tm

样本值

beg_tm   545
end_tm   815

beg_tm   1245
end_tm   1330

样本输出

beg_tm   5:45 am
end_tm   8:15 am

beg_tm   12:45 pm
end_tm   1:30 pm

我在 Perl 中有这个工作,但我正在寻找一种方法来使用 SQL 和 case 语句来完成它。

这甚至可能吗?


编辑

本质上,这种格式必须在 ACE 报告中使用。我找不到使用简单块在输出部分中格式化它的方法

if(beg_tm>=1300) then
beg_tm = vbeg_tm - 1200

其中 vbeg_tm 是已声明的 char(4) 变量


编辑 这适用于 >=1300 小时(2230 除外!!)

select substr((beg_tm-1200),0,1)||":"||substr((beg_tm-1200),2,2) from mtg_rec where beg_tm>=1300;

这适用于 < 1200 小时(有时.... 10:40 失败)

select substr((mtg_rec.beg_tm),0,(length(cast(beg_tm as varchar(4)))-2))||":"||(substr((mtg_rec.beg_tm),2,2))||" am" beg_tm from mtg_rec where mtg_no = 1;


编辑
Jonathan Leffler 的表达式方法中使用的转换语法的变体

SELECT  beg_tm,
        cast((MOD(beg_tm/100 + 11, 12) + 1) as VARCHAR(2)) || ':' ||
        SUBSTRING(cast((MOD(beg_tm, 100) + 100) as CHAR(3)) FROM 2) ||
        SUBSTRING(' am pm' FROM (MOD(cast((beg_tm/1200) as INT), 2) * 3) + 1 FOR 3),
        end_tm,
        cast((MOD(end_tm/100 + 11, 12) + 1) as VARCHAR(2)) || ':' ||
        SUBSTRING(cast((MOD(end_tm, 100) + 100) as CHAR(3)) FROM 2) ||
        SUBSTRING(' am pm' FROM (MOD(cast((end_tm/1200) as INT), 2) * 3) + 1 FOR 3)
      FROM mtg_rec
      where mtg_no = 39;
4

7 回答 7

8

请注意,SO 440061中有关于在 12 小时和 24 小时符号之间转换时间(与此转换相反)的有用信息;这不是微不足道的,因为凌晨 12:45 比凌晨 1:15 早了半小时。

接下来请注意,Informix (IDS — Informix Dynamic Server) 7.31 版终于在 2009 年 9 月 30 日终止服务;它不再是受支持的产品。

您应该更准确地使用您的版本号;例如,7.30.UC1 和 7.31.UD8 之间存在相当大的差异。

但是,您应该能够根据需要使用TO_CHAR()函数来格式化时间。尽管此参考是IDS 12.10 Information Center,但我相信您将能够在 7.31 中使用它(不一定在 7.30 中,但在过去十年的大部分时间里您不应该使用它)。

它说,有一个 24 小时制的“%R”格式说明符。它还为您提供“ GL_DATETIME ”,其中“%I”为您提供 12 小时时间,“%p”为您提供上午/下午指示符。我还找到了一个 7.31.UD8 的 IDS 实例来验证这一点:

select to_char(datetime(2009-01-01 16:15:14) year to second, '%I:%M %p')
    from dual;

04:15 PM

select to_char(datetime(2009-01-01 16:15:14) year to second, '%1.1I:%M %p')
    from dual;

4:15 PM

我从重新阅读问题中看到,您实际上有 SMALLINT 值在 0000..2359 范围内,并且需要将它们转换。通常,我会指出 Informix 有一种用于存储此类值的类型 - DATETIME HOUR TO MINUTE - 但我承认它在磁盘上占用 3 个字节而不是 2 个字节,因此它不如 SMALLINT 表示法紧凑。

Steve Kass 展示了 SQL Server 表示法:

select
  cast((@milTime/100+11)%12+1 as varchar(2))
 +':'
 +substring(cast((@milTime%100+100) as char(3)),2,2)
 +' '
 +substring('ap',@milTime/1200%2+1,1)
 +'m';

正确计时的诀窍很巧妙 - 谢谢史蒂夫!

转换为 IDS 11.50 的 Informix,假设该表为:

CREATE TEMP TABLE times(begin_tm SMALLINT NOT NULL);

SELECT  begin_tm,
        (MOD(begin_tm/100 + 11, 12) + 1)::VARCHAR(2) || ':' ||
        SUBSTRING((MOD(begin_tm, 100) + 100)::CHAR(3) FROM 2) || ' ' ||
        SUBSTRING("ampm" FROM (MOD((begin_tm/1200)::INT, 2) * 2) + 1 FOR 2)
      FROM times
      ORDER BY begin_tm;

使用 FROM 和 FOR 的 SUBSTRING 表示法是标准 SQL 表示法 - 很奇怪,但确实如此。

示例结果:

     0    12:00 am 
     1    12:01 am 
    59    12:59 am 
   100    1:00 am  
   559    5:59 am  
   600    6:00 am  
   601    6:01 am  
   959    9:59 am  
  1000    10:00 am 
  1159    11:59 am 
  1200    12:00 pm 
  1201    12:01 pm 
  1259    12:59 pm 
  1300    1:00 pm  
  2159    9:59 pm  
  2200    10:00 pm 
  2359    11:59 pm 
  2400    12:00 am 

注意:值 559-601 在列表中,因为在没有强制转换为整数的情况下,我遇到了舍入而不是截断的问题。

现在,这是在 IDS 11.50 上测试的;IDS 7.3x 没有强制转换符号。但是,这不是问题。下一条评论将处理这个问题......

作为一个练习如何在没有条件等的情况下用 SQL 编写表达式,这很有趣,但是如果有人在整个套件中不止一次地写过,我会因为缺乏模块化而对他们开枪。显然,这需要一个存储过程 - 并且存储过程不需要(显式)强制转换或其他一些技巧,尽管分配强制执行隐式强制转换:

CREATE PROCEDURE ampm_time(tm SMALLINT) RETURNING CHAR(8);
    DEFINE hh SMALLINT;
    DEFINE mm SMALLINT;
    DEFINE am SMALLINT;
    DEFINE m3 CHAR(3);
    DEFINE a3 CHAR(3);
    LET hh = MOD(tm / 100 + 11, 12) + 1;
    LET mm = MOD(tm, 100) + 100;
    LET am = MOD(tm / 1200, 2);
    LET m3 = mm;
    IF am = 0
    THEN LET a3 = ' am';
    ELSE LET a3 = ' pm';
    END IF;
    RETURN (hh || ':' || m3[2,3] || a3);
END PROCEDURE;

Informix '[2,3]' 表示法是子字符串运算符的原始形式;原始的,因为(出于我仍然无法理解的原因)下标必须是文字整数(不是变量,不是表达式)。它恰好在这里有用;总的来说,这令人沮丧。

此存储过程应该适用于您可以使用的任何版本的 Informix(OnLine 5.x、SE 7.x、IDS 7.x 或 9.x、10.00、11.x、12.x)。

为了说明表达式和存储过程的等价性(一个小变体):

SELECT  begin_tm,
        (MOD(begin_tm/100 + 11, 12) + 1)::VARCHAR(2) || ':' ||
        SUBSTRING((MOD(begin_tm, 100) + 100)::CHAR(3) FROM 2) ||
        SUBSTRING(' am pm' FROM (MOD((begin_tm/1200)::INT, 2) * 3) + 1 FOR 3),
        ampm_time(begin_tm)
      FROM times
      ORDER BY begin_tm;

产生结果:

     0  12:00 am        12:00 am
     1  12:01 am        12:01 am
    59  12:59 am        12:59 am
   100  1:00 am         1:00 am 
   559  5:59 am         5:59 am 
   600  6:00 am         6:00 pm 
   601  6:01 am         6:01 pm 
   959  9:59 am         9:59 pm 
  1000  10:00 am        10:00 pm
  1159  11:59 am        11:59 pm
  1200  12:00 pm        12:00 pm
  1201  12:01 pm        12:01 pm
  1259  12:59 pm        12:59 pm
  1300  1:00 pm         1:00 pm 
  2159  9:59 pm         9:59 pm 
  2200  10:00 pm        10:00 pm
  2359  11:59 pm        11:59 pm
  2400  12:00 am        12:00 am

现在可以在 ACE 报告中的单个 SELECT 语句中多次使用此存储过程,无需多言。


[在原始发布者关于不工作的评论之后...... ]

IDS 7.31 不处理传递给 MOD() 函数的非整数值。因此,除法必须存储在显式整数变量中 - 因此:

CREATE PROCEDURE ampm_time(tm SMALLINT) RETURNING CHAR(8);
    DEFINE i2 SMALLINT;
    DEFINE hh SMALLINT;
    DEFINE mm SMALLINT;
    DEFINE am SMALLINT;
    DEFINE m3 CHAR(3);
    DEFINE a3 CHAR(3);
    LET i2 = tm / 100;
    LET hh = MOD(i2 + 11, 12) + 1;
    LET mm = MOD(tm, 100) + 100;
    LET i2 = tm / 1200;
    LET am = MOD(i2, 2);
    LET m3 = mm;
    IF am = 0
    THEN LET a3 = ' am';
    ELSE LET a3 = ' pm';
    END IF;
    RETURN (hh || ':' || m3[2,3] || a3);
END PROCEDURE;

这已在 Solaris 10 上的 IDS 7.31.UD8 上进行了测试,并且工作正常。我不明白报告的语法错误;但是存在版本依赖性的外部机会 -报告版本号和平台以防万一总是至关重要的。请注意,我很小心地记录了各种工作的位置;这不是偶然的,也不是大惊小怪的——这是基于多年的经验。

于 2009-10-16T06:46:33.643 回答
3

这是Steve Kass 的Informix解决方案的未经测试的端口。

Steve 的解决方案本身在 MS SQL Server 下经过了很好的测试。我比我以前的解决方案更喜欢它,因为到上午/下午时间的转换完全是在代数上完成的,不需要任何分支的帮助(使用 CASE 语句等)。

如果数字“军事时间”来自数据库,则用列名替换 @milTime。@ 变量仅用于测试。

--declare @milTime int
--set @milTime = 1359
SELECT
  CAST(MOD((@milTime /100 + 11), 12) + 1 AS VARCHAR(2))
  ||':'
  ||SUBSTRING(CAST((@milTime%100 + 100) AS CHAR(3)) FROM 2 FOR 2)
  ||' '
  || SUBSTRING('ap' FROM (MOD(@milTime / 1200, 2) + 1) FOR 1)
  || 'm';

供参考,这是我的 [fixed],基于 CASE 的 SQL Server 解决方案

SELECT 
  CASE ((@milTime / 100) % 12)
      WHEN 0 THEN '12'
      ELSE CAST((@milTime % 1200) / 100 AS varchar(2))
  END 
  + ':' + RIGHT('0' + CAST((@milTime % 100) AS varchar(2)), 2)
  + CASE (@milTime / 1200) WHEN 0 THEN ' am' ELSE ' pm' END
于 2009-10-15T15:06:50.680 回答
3

mjv 的第二次尝试仍然不起作用。(例如,对于 0001,它给出 0:1 am。)

这是一个应该工作得更好的 T-SQL 解决方案。它可以通过使用适当的连接和SUBSTRING语法来适应其他方言。

它也适用于军用时间 2400(上午 12:00),这可能很有用。

select
  cast((@milTime/100+11)%12+1 as varchar(2))
 +':'
 +substring(cast((@milTime%100+100) as char(3)),2,2)
 +' '
 +substring('ap',@milTime/1200%2+1,1)
 +'m';
于 2009-10-15T16:31:35.470 回答
3

啊,Jenzabar 的一位用户(乔纳森,不要对模式太残忍。它们实际上已经有几十年的历史了)。很惊讶你没有在 CX-Tech 列表上问这个问题。我会向您发送一个用于 CX 的 RCS 就绪存储过程。

-sw

{
 Revision Information (Automatically maintained by 'make' - DON'T CHANGE)
 -------------------------------------------------------------------------
 $Header$
 -------------------------------------------------------------------------
}
procedure       se_get_inttime
privilege       owner
description     "Get time from an integer field and return as datetime"
inputs          param_time integer      "Integer formatted time"
returns         datetime hour to minute "Time in datetime format"
notes           "Get time from an integer field and return as datetime"

begin procedure

DEFINE tm_str VARCHAR(255);
DEFINE h INTEGER;
DEFINE m INTEGER;

IF (param_time < 0 OR param_time > 2359) THEN
RAISE EXCEPTION -746, 0, "Invalid time format. Should be: 0 - 2359";
END IF

LET tm_str = LPAD(param_time, 4, 0);

LET h = SUBSTR(tm_str, 1, 2);

IF (h < 0 OR h > 23) THEN
RAISE EXCEPTION -746, 0, "Invalid time format. Should be: 0 - 2359";
END IF

LET m = SUBSTR(tm_str, 3, 4);

IF (m < 0 OR m > 59) THEN
RAISE EXCEPTION -746, 0, "Invalid time format. Should be: 0 - 2359";
END IF

RETURN TO_DATE(h || ':' || m , '%R');

end procedure

grant
    execute to (group public)
于 2010-03-30T18:25:53.457 回答
1

不确定informix,这就是我在Oracle 中会做的事情(一些例子,但我在家里未经测试):

  1. 将整数转为字符串:To_Char (milTime)例如 1->'1', 545 -> '545', 1215 -> '1215'
  2. 确保我们总是有一个四个字符的字符串:Right('0000'||To_Char(milTime), 4)例如 1-> '0001', 545 -> '0545', 1215 -> '1215'
  3. 变成日期时间:To_Date (Right('0000'||To_Char(milTime), 4), 'HH24:MI')
  4. 输出为所需格式:To_Char(To_Date(..),'HH:MI AM')例如 1->'00:01 AM', 545 -> '05:45 AM', 1215 -> '12:15 PM'

Oracle 的 To_Date 和 To_Char 是专有的,但我确信有标准的 SQL 或 Informix 函数可以实现相同的结果,而无需求助于“计算”。

于 2009-10-15T19:58:52.477 回答
1

CheeseWithCheese 说必须在 ACE 报告中完成,所以这是我的 ACE 报告...

ACE 中军时 smallint 转换为 AM/PM 格式的示例:

select beg_tm, end_tm ...

define
variable utime char(4) 
variable ftime char(7)
end

format

on every row

let utime = beg_tm  {cast beg_tm to char(4). do same for end_tm} 

if utime[1,2] = "00" then let ftime[1,3] = "12:"
if utime[1,2] = "01" then let ftime[1,3] = " 1:"
if utime[1,2] = "02" then let ftime[1,3] = " 2:"
if utime[1,2] = "03" then let ftime[1,3] = " 3:"
if utime[1,2] = "04" then let ftime[1,3] = " 4:"
if utime[1,2] = "05" then let ftime[1,3] = " 5:"
if utime[1,2] = "06" then let ftime[1,3] = " 6:"
if utime[1,2] = "07" then let ftime[1,3] = " 7:"
if utime[1,2] = "08" then let ftime[1,3] = " 8:"
if utime[1,2] = "09" then let ftime[1,3] = " 9:"
if utime[1,2] = "10" then let ftime[1,3] = "10:"
if utime[1,2] = "11" then let ftime[1,3] = "11:"

if utime[1,2] = "12" then let ftime[1,3] = "12:"
if utime[1,2] = "13" then let ftime[1,3] = " 1:"
if utime[1,2] = "14" then let ftime[1,3] = " 2:"
if utime[1,2] = "15" then let ftime[1,3] = " 3:"
if utime[1,2] = "16" then let ftime[1,3] = " 4:"
if utime[1,2] = "17" then let ftime[1,3] = " 5:"
if utime[1,2] = "18" then let ftime[1,3] = " 6:"
if utime[1,2] = "19" then let ftime[1,3] = " 7:"
if utime[1,2] = "20" then let ftime[1,3] = " 8:"
if utime[1,2] = "21" then let ftime[1,3] = " 9:"
if utime[1,2] = "22" then let ftime[1,3] = "10:"
if utime[1,2] = "23" then let ftime[1,3] = "11:"

let ftime[4,5] = utime[3,4]   

if utime[1,2] = "00"
or utime[1,2] = "01"
or utime[1,2] = "02"
or utime[1,2] = "03"
or utime[1,2] = "04"
or utime[1,2] = "05"
or utime[1,2] = "06"
or utime[1,2] = "07"
or utime[1,2] = "08"
or utime[1,2] = "09"
or utime[1,2] = "10"
or utime[1,2] = "11" then let ftime[6,7] = "AM"

if utime[1,2] = "12"
or utime[1,2] = "13"
or utime[1,2] = "14"
or utime[1,2] = "15"
or utime[1,2] = "16"
or utime[1,2] = "17"
or utime[1,2] = "18"
or utime[1,2] = "19"
or utime[1,2] = "20"
or utime[1,2] = "21"
or utime[1,2] = "22"
or utime[1,2] = "23" then let ftime[6,7] = "PM"

print column 1, "UNFORMATTED TIME: ", utime," = FORMATTED TIME: ", ftime 
于 2010-01-30T00:57:48.770 回答
0

长期的方法......但有效

select  substr((mtg_rec.beg_tm-1200),0,1)||":"||substr((mtg_rec.beg_tm-1200),2,2)||" pm" beg_tm,
            substr((mtg_rec.end_tm-1200),0,1)||":"||substr((mtg_rec.end_tm-1200),2,2)||" pm" end_tm
    from    mtg_rec
    where   mtg_rec.beg_tm between 1300 and 2159
            and mtg_rec.end_tm between 1300 and 2159
    union
    select  substr((mtg_rec.beg_tm-1200),0,1)||":"||substr((mtg_rec.beg_tm-1200),2,2)||" pm" beg_tm,
            substr((mtg_rec.end_tm-1200),0,2)||":"||substr((mtg_rec.end_tm-1200),3,2)||" pm" end_tm
    from    mtg_rec
    where   mtg_rec.beg_tm between 1300 and 2159
            and mtg_rec.end_tm between 2159 and 2400
    union
    select  substr((mtg_rec.beg_tm-1200),0,2)||":"||substr((mtg_rec.beg_tm-1200),3,2)||" pm" beg_tm,
            substr((mtg_rec.end_tm-1200),0,2)||":"||substr((mtg_rec.end_tm-1200),3,2)||" pm" end_tm
            mtg_rec.days
    from    mtg_rec
    where   mtg_rec.beg_tm between 2159 and 2400
            and mtg_rec.end_tm between 2159 and 2400
    union
     select substr((mtg_rec.beg_tm),0,1)||":"||(substr((mtg_rec.beg_tm),2,2))||" am" beg_tm,
            substr((mtg_rec.end_tm),0,1)||":"||(substr((mtg_rec.end_tm),2,2))||" am" end_tm
            mtg_rec.days
    from    mtg_rec
    where   mtg_rec.beg_tm between 0 and 959
            and mtg_rec.end_tm between 0 and 959
    union
     select substr((mtg_rec.beg_tm),0,2)||":"||(substr((mtg_rec.beg_tm),3,2))||" am" beg_tm,
            substr((mtg_rec.end_tm),0,2)||":"||(substr((mtg_rec.end_tm),3,2))||" am" end_tm
            mtg_rec.days
    from    mtg_rec
    where   mtg_rec.beg_tm between 1000 and 1259
            and mtg_rec.end_tm between 1000 and 1259
    union
     select cast(beg_tm as varchar(4)),
            cast(end_tm as varchar(4))
    from    mtg_rec
    where   mtg_rec.beg_tm = 0
            and mtg_rec.end_tm = 0
    into temp time_machine with no log;
于 2009-10-15T20:36:58.077 回答