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 の式アプローチで使用されるキャスト構文のEDITバリエーション

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 分より 30 分前なので、これは些細なことではありません。

次に、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 ではなく、過去 10 年間ほとんど使用していなかったはずです)。

24 時間制の '%R' 形式指定子があります。また、' GL_DATETIME 'も参照します。ここで、'%I' は 12 時間制の時刻を示し、'%p' は午前/午後のインジケータを示します。これを検証するために、IDS の 7.31.UD8 インスタンスも見つけました。

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

質問を読み直すと、実際には 0000..2359 の範囲の SMALLINT 値があり、それらを変換する必要があることがわかります。Informix にはそのような値を格納するための型 (DATETIME HOUR TO MINUTE) があることをよく指摘しますが、ディスク上で 2 バイトではなく 3 バイトを占有するため、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で式を書く方法の演習として、これは興味深いですが、スイート全体で誰かがそれを2回以上書いた場合、私はそれらをモジュール化の欠如のために撃ちます. 明らかに、これにはストアド プロシージャが必要です。また、ストアド プロシージャには (明示的な) キャストやその他のトリッキーは必要ありませんが、割り当てによって暗黙的なキャストが強制されます。

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

ああ、仲間の Jenzabar ユーザーです (Jonathan、スキーマについてあまり冷酷にならないでください。スキーマは文字通り何十年も前のものです)。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 に答える
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';

参考までに、SQL Server の [修正済み]、CASE ベースのソリューションを次に示します。

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 の 2 回目の試行はまだ機能しません。(たとえば、0001 の場合、午前 0:1 になります。)

これは、より適切に機能するはずの 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 に答える
1

Informixについてはよくわかりませんが、Oracleで行うことは次のとおりです(いくつかの例ですが、自宅にいるためテストされていません):

  1. 整数を文字列に変換:To_Char (milTime)例: 1->'1', 545 -> '545', 1215 -> '1215'
  2. 常に 4 文字の文字列があることを確認してください: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 に答える