java - 更新 - setlastmodified android




どのように実際にタイムスタンプである日付範囲を保存しますか (8)

JavaとOracleの両方にはDateというタイムスタンプ型があります。 開発者は、 カレンダーの日付であるかのようにこれらを操作する傾向があります。

  1. 基本的な日付の数量については、入力時に時間の部分を単に切り捨てることができます。つまり精度を低下させることができます。 しかし、日付範囲(例えば: 9 / 29-9 / 30 )でそれを行うと、これらの2つの値の差は2ではなく1日です。また、範囲の比較には、1)切り捨て操作: start < trunc(now) <= end 、または2)arithmetic: start < now < (end + 24hrs) 。 恐ろしいことではありませんが、 DRYはありません。

  2. 代わりに、9 / 29 00:00:00 - 10/1 00:00:00という、実際のタイムスタンプを使用することもできます。 (真夜中〜真夜中、10月の一部を含まない)。 現在の期間は本質的に正しいので、範囲の比較はより簡単です: start <= now < end 。 確かに内部処理のためのクリーナーですが、終了日は最初の入力(+1)時に変換し、出力(-1)ではユーザーレベルでのカレンダー日付の比喩を仮定する必要があります。

プロジェクトの日付範囲をどのように扱いますか? 他の選択肢はありますか? 私は、方程式のJava側とOracle側の両方でこれをどのように扱うかに特に関心があります。


Intervalデータ型はまだ投稿されていません。

Oracleには、正確なシナリオのためのデータ型もあります。 Oracleには、INTERVAL YEAR TO MONTHとINTERVAL DAY TO SECONDのデータ型もあります。

10gR2のドキュメントから。

INTERVAL YEAR TO MONTHは、YEARおよびMONTH日時フィールドを使用して期間を保管します。 このデータ型は、年と月の値のみが重要な場合に、2つのdatetime値の差を表すのに便利です。

INTERVAL YEAR [(year_precision)] TO MONTH

ここでyear_precisionはYEAR日時フィールドの桁数です。 year_precisionのデフォルト値は2です。

インターナショナルデーから第2のデータ型

INTERVAL DAY TO SECONDは、日、時間、分、および秒単位で期間を保管します。 このデータ型は、2つのdatetime値の正確な差を表すのに便利です。

このデータ型を次のように指定します。

INTERVAL DAY [(day_precision)] TO SECOND [(fractional_seconds_precision)]

どこで

day_precisionは、DAY datetimeフィールドの桁数です。 指定できる値は0〜9です。デフォルトは2です。

fractional_seconds_precisionは、SECOND日時フィールドの小数部の桁数です。 指定できる値は0〜9です。デフォルトは6です。

インターバル値をリテラルとして指定するときは、柔軟性が非常に高くなります。 インターバル値をリテラルとして指定する方法の詳細については、「インターバルリテラル」を参照してください。 間隔を使用した例については、「日時と間隔の例」も参照してください。



ここで私たちはそれをやっています。

  1. タイムスタンプを使用する。

  2. 比較のためにハーフオープン間隔を使用してください。 start <= now < end

BETWEENが何らかの形で成功したSQLに不可欠であると主張する喧嘩者は無視してください。

これにより、一連の日付範囲を監査するのが本当に簡単です。 9 9/30 to 10/1のデータベース値は1日(9/30)を含みます。 次の間隔の開始は、前の間隔の終了と同じでなければなりません。 そのinterval[n-1].end == interval[n].start 。開始規則は、監査に便利です。

表示すると、必要に応じて、フォーマットされたstartおよびend -1を表示できます。 あなた 、「終わり」は実際にルールがもはや真実ではない最初の日であることを人々に理解させることができます。 したがって、「9/30〜10/1」は「有効な開始9/30、有効ではなくなった開始10/1」を意味します。


すべての日付をミリ秒単位で格納する。 タイムスタンプ/日時フィールドはまったく使用しません。

だから、私はそれを長い間操作しなければならない。 つまり、私はSQLクエリで 'before'、 'after'、 'now'のキーワードを使用しません。


私の経験に基づいて、それを行う主な4つの方法があります:

1)日付を1970年1月1日以降の時代整数に変換し、整数としてデータベースに格納する。

2)日付をYYYYMMDDHHMMSS整数に変換し、整数としてデータベースに格納します。

3)日付として保存する

4)文字列として格納する

私はいつも素早く簡単な算術演算を行い、基礎となるデータベース機能に依存しないので、常に1と2に固執しています。


Oracleの日付データ型を使用して、境界条件に影響を及ぼす時間コンポーネントの問題について開発者に教育します。

また、データベース制約は、カラムを持たないカラムに時間成分が誤って指定されるのを防ぎ、オプティマイザに値のいずれも時間成分を持たないことを通知します。

たとえば、制約CHECK(MY_DATE = TRUNC(MY_DATE))は、00:00:00以外の値がmy_date列に配置されないようにします。また、OracleはMY_DATE = TO_DATE( ' 2008-09-12 15:00:00 ')は決して真実ではなく、したがって、次のように展開することができるため、テーブルから行が返されません。

MY_DATE = TO_DATE('2008-09-12 15:00:00') AND
TO_DATE('2008-09-12 15:00:00') = TRUNC(TO_DATE('2008-09-12 15:00:00'))

もちろんこれは自動的に間違っています。

20080915のような日付を日付として格納することは、クエリの最適化の問題を引き起こす可能性があります。 たとえば、20,071,231と20,070,101の間にいくつの正当な値がありますか? 2007年12月31日〜2008年1月31日の間にはいかがですか? また、20070100など、不正な値を入力することもできます。

したがって、時間コンポーネントを持たない日付がある場合、範囲の定義は簡単になります。

select ...
from   ...
where  my_date Between date '2008-01-01' and date '2008-01-05'

時間コンポーネントがある場合は、次のいずれかを実行できます。

select ...
from   ...
where  my_date >= date '2008-01-01' and
       my_date  < date '2008-01-06'

または

select ...
from   ...
where  my_date Between date '2008-01-01'
                   and date '2008-01-05'-(1/24/60/60)

マジックナンバーの代わりに(1/24/60/60)を使用することに注意してください。 1日の定義された分数を3/24時間に、27/24/60分に27分加算することで、日付計算を実行するのはかなり一般的です。 このタイプのOracleの数学は正確であり、丸め誤差に悩まされないため、次のようになります。

select 27/24/60 from dual;

... 0.01874999999999などではなく、0.01875となります。


getTime()の結果を長整数として格納することによって、すべての日付をGMTタイムスタンプ(つまり、タイムゾーンや夏時間の頭痛など)として明白に格納できます。

データベースクエリで日、週、月などの操作が必要な場合や、クエリのパフォーマンスが重要な場合は、タイムスタンプ(ミリ秒よりも細かく正規化されたもの)をその日の列を持つ日付ブレークダウンテーブルにリンクできます、週、月などの値が含まれているため、高価な日付/時刻関数をクエリで使用する必要はありません。


Java側では、次のいずれかを使用します。

両方ともjava.sql.Timestampに前後に変換できます。

ジョーダンタイム

Joda-Timeには、期間、期間、期間の3つのクラスがあります。

ISO 8601規格では、 DurationIntervalを表す文字列をどのようにフォーマットするかを規定しています。 Joda-Timeはこのような文字列を解析して生成します。

タイムゾーンは重要な考慮事項です。 あなたのデータベースは、その日時の値をUTCで保存する必要があります。 しかし、ビジネスロジックはタイムゾーンを考慮する必要があります。 「日」の始まりはタイムゾーンによって異なります。 ちなみに、3または4文字コードではなく、 適切なタイムゾーン名を使用してください

S.Lott氏の正解は、 Half-Openロジックを使用することを推奨します。これは通常、日時制作に最適です。 エンディングは排他的ですが、時間の始まりは包括 です。 Joda-Timeは、そのメソッドで半開きロジックを使用します。

DateTimeZone timeZone_NewYork = DateTimeZone.forID( "America/New_York" );
DateTime start = new DateTime( 2014, 9, 29, 15, 16, 17, timeZone_NewYork );
DateTime stop = new DateTime( 2014, 9, 30, 1, 2, 3, timeZone_NewYork );

int daysBetween = Days.daysBetween( start, stop ).getDays();

Period period = new Period( start, stop );

Interval interval = new Interval( start, stop );
Interval intervalWholeDays = new Interval( start.withTimeAtStartOfDay(), stop.plusDays( 1 ).withTimeAtStartOfDay() );

DateTime lateNight29th = new DateTime( 2014, 9, 29, 23, 0, 0, timeZone_NewYork );
boolean containsLateNight29th = interval.contains( lateNight29th );

コンソールにダンプ...

System.out.println( "start: " + start );
System.out.println( "stop: " + stop );
System.out.println( "daysBetween: " + daysBetween );
System.out.println( "period: " + period ); // Uses format: PnYnMnDTnHnMnS
System.out.println( "interval: " + interval );
System.out.println( "intervalWholeDays: " + intervalWholeDays );
System.out.println( "lateNight29th: " + lateNight29th );
System.out.println( "containsLateNight29th: " + containsLateNight29th );

実行時...

start: 2014-09-29T15:16:17.000-04:00
stop: 2014-09-30T01:02:03.000-04:00
daysBetween: 0
period: PT9H45M46S
interval: 2014-09-29T15:16:17.000-04:00/2014-09-30T01:02:03.000-04:00
intervalWholeDays: 2014-09-29T00:00:00.000-04:00/2014-10-01T00:00:00.000-04:00
lateNight29th: 2014-09-29T23:00:00.000-04:00
containsLateNight29th: true






date-range