문제

Java & Oracle 둘 다 a 타임 스탬프 날짜라고 불리는 유형. 개발자는 마치 마치 마치 마치 마치 마치 마치 조작하는 경향이 있습니다 달력 내가 본 날짜는 불쾌한 일회성 버그를 일으킨다.

  1. 기본 날짜 수량의 경우 입력시 시간 부분을 차단할 수 있습니다. 즉, 정밀도를 줄입니다. 그러나 날짜 범위로 그렇게한다면 (예 : 9/29-9/30),이 두 값의 차이는 2가 아닌 1 일입니다. 또한 범위 비교에는 1) 잘린 작업이 필요합니다. start < trunc(now) <= end, 또는 2) 산술 : start < now < (end + 24hrs). 끔찍하지는 않지만 그렇지 않습니다 마른.

  2. 대안은 진정한 타임 스탬프를 사용하는 것입니다. 9/29 00:00:00 - 10/1 00:00:00. (자정에서 사고 자정이므로 10 월의 일부는 포함되지 않습니다). 이제 지속 시간은 본질적으로 정확하며 범위 비교는 더 간단합니다. start <= now < end. 확실히 내부 처리를 위해 더 깨끗하지만, 최종 입력 (+1)과 출력 (-1)에 대한 최종 날짜는 사용자 수준의 캘린더 날짜 은유를 가정 할 때 변환해야합니다.

프로젝트의 날짜 범위를 어떻게 처리합니까? 다른 대안이 있습니까? 나는 당신이 방정식의 자바와 오라클 측면에서 이것을 어떻게 처리하는지에 관심이 있습니다.

도움이 되었습니까?

해결책

우리가하는 방법은 다음과 같습니다.

  1. 타임 스탬프를 사용하십시오.

  2. 비교를 위해 하프 오픈 간격을 사용하십시오. start <= now < end.

SQL이 성공적으로 든 필수적이라고 주장하는 Whiners를 무시하십시오.

이를 통해 일련의 날짜 범위는 감사하기 쉽습니다. 데이터베이스 값 9/30 to 10/1 하루를 포함하십시오 (9/30). 다음 간격의 시작은 이전 간격의 끝과 같아야합니다. 저것 interval[n-1].end == interval[n].start 규칙은 감사에 편리합니다.

표시되면 원하는 경우 형식을 표시 할 수 있습니다. start 그리고 end-1. 당신은 밝혀졌습니다 ~할 수 있다 사람들이 "끝"이 실제로 규칙이 더 이상 사실이 아니라는 것을 이해하도록 교육하십시오. 따라서 "9/30 ~ 10/1"은 "유효한 시작 9/30, 더 이상 유효한 시작 10/1"을 의미합니다.

다른 팁

오라클은 다음과 같습니다 타임 스탬프 데이터 유형. 날짜 데이터 유형의 연도, 월 및 날, 시간, 분, 두 번째 및 분수 두 번째 값을 저장합니다.

여기에 있습니다 AskTom.oracle.com의 스레드 날짜 산술에 대해.

나는 S.Lott가 설명한 것을 두 번째합니다. 우리는 날짜 시간 범위를 광범위하게 사용하는 제품 스위트를 보유하고 있으며, 이와 같은 범위에서 일하는 데 대한 수업 중 하나였습니다. 그건 그렇고, 우리는 종료 날짜를 호출합니다 독점적인 더 이상 범위의 일부가 아닌 경우 종료 날짜 (iow, 반 개방 간격). 대조적으로, 그것은입니다 포함한 시간 부분이없는 경우에만 적합한 범위의 일부로 계산되는 경우 종료 날짜.

사용자는 일반적으로 포괄적 인 날짜 범위의 입력/출력을 기대합니다. 여하튼, 사용자 입력을 최대한 빨리 배타적 인 종료 날짜 범위로 변환하고 사용자에게 표시 될 때 가능한 한 늦게 날짜 범위를 변환하십시오.

데이터베이스에서 항상 독점 종료 날짜 범위를 저장하십시오. 포괄적 인 종료 날짜 범위가있는 레거시 데이터가있는 경우 가능한 경우 DB에서 마이그레이션하거나 데이터를 읽을 때 가능한 빨리 독점 종료 날짜 범위로 변환하십시오.

Oracle의 날짜 데이터 유형을 사용하고 경계 조건에 영향을 미치는 시간 구성 요소 문제에 대해 개발자에게 교육합니다.

데이터베이스 제약 조건은 또한 열에서 시간 구성 요소의 우발적 사양을 방해하지 않으며 값에 시간 구성 요소가 없음을 최적화기에 알려줍니다.

예를 들어, 제약 조건 확인 (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 일까지 ABND 01-JAN-2008 사이의 날짜는 어떻습니까? 또한 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)의 사용에 유의하십시오. 오라클에서는 하루의 정의 된 분수를 3 시간 동안 3/24, 27/24/60 27 분 동안 추가하여 날짜 산술을 수행하는 것이 일반적입니다. 이 유형의 Oracle Math는 정확하고 반올림 오류를 겪지 않으므로 :

select 27/24/60 from dual;

... 0.01874999999999가 아닌 0.01875를 제공합니다.

구간 데이터 유형이 아직 게시되지 않았습니다.

Oracle에는 정확한 시나리오에 대한 데이터 유형이 있습니다. Oracle에는 매년 간격과 매일 간격의 날에서 두 번째 데이터 타입도 있습니다.

10GR2 문서에서.

해마다 간격은 연도 및 월 DateTime 필드를 사용하여 기간을 저장합니다. 이 데이터 유형은 연도와 월 값 만 중요 할 때 두 개의 DateTime 값의 차이를 나타내는 데 유용합니다.

간격 연도 [(Year_Precision)]에서 월까지

여기서 year_precision은 DateTime 필드의 해의 숫자 수입니다. Year_Precision의 기본값은 2입니다.

간격의 날에서 두 번째 데이터 유형

간격의 날에서 두 번째는 일, 시간, 몇 분 및 초 측면에서 기간을 저장합니다. 이 데이터 유형은 두 데이터 타임 값 사이의 정확한 차이를 나타내는 데 유용합니다.

이 데이터 유형을 다음과 같이 지정하십시오.

인터벌 데이 [(day_precision)] ~ 두 번째 [(fractional_seconds_precision)

어디

day_precision은 DATETIME 필드의 숫자 수입니다. 허용 값은 0에서 9입니다. 기본값은 2입니다.

fractional_seconds_precision은 두 번째 DateTime 필드의 분수 부분의 숫자 수입니다. 허용 값은 0에서 9입니다. 기본값은 6입니다.

간격 값을 리터럴로 지정할 때 많은 유연성이 있습니다. 간격 값을 리터럴로 지정하는 자세한 정보는 "Interval Literals"를 참조하십시오. 간격을 사용하여 예제는 "DateTime 및 간격 예"를 참조하십시오.

내 경험을 바탕으로 4 가지 주요 방법이 있습니다.

1) 날짜를 Epoch Integer (1970 년 1 월 1 일 이후 초)로 변환하여 데이터베이스에 정수로 저장하십시오.

2) 날짜를 yyyymmddhhmmss 정수로 변환하고 데이터베이스에 정수로 저장하십시오.

3) 날짜로 보관하십시오

4) 문자열로 보관하십시오

나는 항상 1과 2를 고수했습니다. 왜냐하면 그것은 당신이 날짜와 신속하고 간단한 산술을 수행하고 기본 데이터베이스 기능에 의존하지 않기 때문입니다.

첫 번째 문장을 바탕으로, 당신은 Java의 숨겨진 "기능"(즉, 버그) 중 하나를 넘어 뜨리고 있습니다. java.util.Date 불변 이었지만 그렇지 않았습니다. (Java 7은 새로운 날짜/시간 API 로이 문제를 해결할 것을 약속합니다.) 거의 모든 엔터프라이즈 앱은 다양한 시간적 패턴, 그리고 어느 시점에서 당신은 날짜와 시간에 산술을해야합니다.

이상적으로는 사용할 수 있습니다 조다 시간, Google 캘린더에서 사용하는 것. 당신이 이것을 할 수 없다면, 나는 주위의 래퍼로 구성된 API를 생각합니다. java.util.Date Grails/Rails와 유사한 계산 방법 및 다양한 래퍼 (즉, 기간의 시작 및 끝을 나타내는 순서 쌍)가 충분합니다.

내 현재 프로젝트 (HR 타임 키핑 응용 프로그램)에서 우리는 모든 날짜를 Oracle과 Java의 동일한 시간대로 정상화하려고합니다. 다행히도, 우리의 현지화 요구 사항은 가벼운 것입니다 (= 1 타임 존은 충분합니다). 지속적인 물체가 하루보다 더 미세한 정밀도가 필요하지 않으면 자정 기간 동안 타임 스탬프를 사용합니다. 나는 더 나아가서 지속적인 물체가 견딜 수있는 가장 거친 입력에 여분의 밀리 초를 버릴 때 (가공을 더 간단하게 만들 것입니다).

모든 날짜는 결과를 저장하여 GMT 타임 스탬프 (예 : 시간대 또는 일광 절약 두통 없음)로 명확하게 저장할 수 있습니다. gettime () 긴 정수로.

일, 주, 월 등의 경우 데이터베이스 쿼리에 조작이 필요하고 쿼리 성능이 가장 중요 할 때 타임 스탬프 (밀리 초보다 더 높은 입상으로 정규화)는 하루의 열이있는 날짜 고장 테이블에 연결할 수 있습니다. , 주, 월 등. 값이 많은 날짜/시간 기능을 쿼리로 사용할 필요가 없습니다.

앨런은 옳습니다. 조다 시간은 훌륭합니다. java.util.date와 캘린더는 수치 스럽습니다.

타임 스탬프가 필요한 경우 시간과 함께 Oracle 날짜 유형을 사용하는 경우 _TMST와 같은 종류의 접미사로 열에 이름을 지정하십시오. 데이터를 Java로 읽으면 Joda Time DateTime 객체로 가져옵니다. 타임 존이 올바르게 확인하기 위해 Oracle에는 타임 스탬프를 시간대에 저장하는 특정 데이터 유형이 있음을 고려하십시오. 또는 테이블에 다른 열을 만들어 TimeZone ID를 저장할 수 있습니다. 시간대 ID의 값은 표준 전체 이름 ID 여야합니다. http://java.sun.com/j2se/1.4.2/docs/api/java/util/timezone.html#gettimezone%28java.lang.string%29 . TZ DTA에 다른 열을 사용하는 경우 데이터를 Java 사용 DateTime 객체로 읽지 만 whithzoneretainfields를 사용하여 DateTime 객체에서 TimeZone을 설정하여 TimeZone을 설정하십시오.

날짜 데이터 만 필요하면 (타임 스탬프 없음) 시간없이 데이터베이스의 날짜 유형을 사용하십시오. 다시 잘 지정하십시오. 이 경우 Jodatime에서 DateMidnight 객체를 사용하십시오.

결론 : 데이터베이스의 유형 시스템과 사용중인 언어를 활용하십시오. 그것들을 배우고 표현력이있는 API와 언어 구문을 갖춘 이점을 얻으려면 문제를 처리하십시오.

업데이트 : Joda-Time 프로젝트는 이제 유지 보수 모드에 있습니다. 팀은 다음으로의 마이그레이션을 조언합니다 Java.Time Java에 내장 된 수업.

조다-시간

Joda-Time은 간격, 지속 시간 및 기간을 나타내는 3 개의 클래스를 제공합니다.

ISO 8601 표준은 문자열을 나타내는 방법을 지정합니다. 지속 그리고 간격. Joda-Time은 두 줄을 모두 구문 분석하고 생성합니다.

시간대는 중요한 고려 사항입니다. 데이터베이스는 날짜 시간 값을 UTC에 저장해야합니다. 그러나 비즈니스 로직은 시간대를 고려해야 할 수도 있습니다. "일"의 시작은 시간대에 따라 다릅니다. 그건 그렇고, 사용하십시오 적절한 시간대 이름 3 ~ 4 개의 글자 코드가 아니라.

그만큼 S.Lott의 정답 일반적으로 날짜 시간 작업에 가장 적합하기 때문에 Half-Open Logic을 사용하도록 현명하게 조언합니다. 시간의 시작은입니다 포함한 결말은입니다 독점적인. Joda-Time은 메소드에서 반 오픈 로직을 사용합니다.

diagram defining a week as greater than or equal to Day 1 and less than Day 8

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

모든 날짜를 밀리 초로 저장하고 있습니다. 타임 스탬프/DateTime 필드를 전혀 사용하지 않습니다.

그래서 나는 그것을 오랫동안 조작해야합니다. 이는 SQL 쿼리의 '이전', '후', '지금'키워드를 사용하지 않는다는 것을 의미합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top