.NET Framework DateTime을 사용하는 코딩 모범 사례

 

댄 로저스
Microsoft Corporation

2004년 2월

적용 대상
   Microsoft® .NET Framework
   Microsoft® ASP.NET Web Services
   XML serialization

요약: Microsoft .NET Framework DateTime 형식을 사용하여 시간 값을 저장, 계산 및 직렬화하는 프로그램을 작성하려면 Windows 및 .NET에서 사용할 수 있는 시간 표현과 관련된 다양한 문제를 인식해야 합니다. 이 문서에서는 시간과 관련된 주요 테스트 및 개발 시나리오에 중점을 두고 Microsoft 에서 DateTime 형식을 사용하는 프로그램을 작성하기 위한 모범 사례 권장 사항을 정의합니다. NET 기반 애플리케이션 및 어셈블리. (인쇄된 페이지 18개)

콘텐츠

배경
   DateTime이란?
   규칙
스토리지 전략
   모범 사례 #1
   모범 사례 #2
계산 수행
   다시 속지 마세요
   모범 사례 #3
   DateTime 메서드 정렬
XML의 특수 사례
   모범 사례 #4
   모범 사례 #5
클래스 코더 Quandary
   모범 사례 #6
일광 절약 시간 처리
   모범 사례 #7
User-Ready 값 서식 지정 및 구문 분석
   향후 고려 사항
DateTime.Now() 메서드 관련 문제
   모범 사례 #8
거의 알려진 엑스트라의 몇 가지
결론

배경

많은 프로그래머가 날짜 및 시간 정보를 포함하는 데이터를 정확하게 저장하고 처리해야 하는 할당이 발생합니다. 언뜻 보기에 CLR(공용 언어 런타임) DateTime 데이터 형식은 이러한 작업에 적합합니다. 그러나 프로그래머에게는 드문 일이 아니지만 테스터가 프로그램에서 올바른 시간 값을 추적하지 못하는 경우가 발생할 가능성이 높습니다. 이 문서에서는 DateTime과 관련된 논리와 관련된 문제에 초점을 맞추고, 이를 통해 DateTime 정보를 캡처, 저장, 검색 및 전송하는 프로그램을 작성하고 테스트하는 모범 사례를 파악합니다.

DateTime이란?

NET Framework 클래스 라이브러리 설명서를 보면 "CLR System.DateTime 값 형식은 0001년 1월 1일 자정부터 9999년 12월 31일 오후 11:59:59까지의 날짜 및 시간을 나타냅니다." 더 자세히 살펴보면 당연히 DateTime 값이 특정 시점의 인스턴트를 나타내며 일반적으로 UCT(협정 세계시)(그리니치 표준시(GMT))에서 특정 시점 값을 기록하는 것이 일반적이라는 것을 알아봅니다.

언뜻 보기에 프로그래머는 DateTime 형식이 비즈니스 애플리케이션과 같은 현재 프로그래밍 문제에서 발생할 수 있는 시간 값을 저장하는 데 매우 능숙하다는 것을 알게 됩니다. 이러한 자신감으로 많은 의심하지 않는 프로그래머가 코딩을 시작하고, 앞으로 나아갈 때 필요한 만큼 배울 수 있다고 확신합니다. 이 "learn-as-you-go" 접근 방식은 몇 가지 문제로 이어질 수 있으므로 식별을 시작하겠습니다. 설명서의 문제부터 프로그램 디자인에 고려해야 하는 동작에 이르기까지 다양합니다.

System.DateTime에 대한 V1.0 및 1.1 설명서에서는 의심하지 않는 프로그래머가 트랙에서 벗어날 수 있는 몇 가지 일반화를 만듭니다. instance 설명서에는 현재 DateTime 클래스에 있는 메서드와 속성은 계산 또는 비교를 수행할 때 값이 로컬 머신 현지 표준 시간대를 나타낸다는 가정을 항상 사용한다고 설명합니다. 이 일반화는 GMT를 가정하는 특정 형식의 날짜 및 시간 계산과 현지 표준 시간대 보기를 가정하는 다른 형식이 있기 때문에 사실이 아닌 것으로 밝혀졌습니다. 이러한 영역은 이 문서의 뒷부분에서 설명합니다.

따라서 코드가 처음 제대로 작동하는 데 도움이 되는 일련의 규칙 및 모범 사례를 요약하여 DateTime 형식을 탐색해 보겠습니다.

규칙

  1. DateTime 인스턴스의 계산 및 비교는 비교되거나 사용되는 인스턴스가 동일한 표준 시간대 관점에서 시간의 포인트 표현인 경우에만 의미가 있습니다.
  2. 개발자는 일부 외부 메커니즘을 통해 DateTime 값과 연결된 표준 시간대 정보를 추적할 책임이 있습니다. 일반적으로 DateTime 값 형식을 저장할 때 표준 시간대 정보를 기록하는 데 사용하는 다른 필드 또는 변수를 정의하여 이 작업을 수행합니다. 이 방법(DateTime 값과 함께 표준 시간대 센스 저장)이 가장 정확하며 프로그램 수명 주기의 여러 지점에서 다양한 개발자가 항상 DateTime 값의 의미를 명확하게 이해할 수 있습니다. 또 다른 일반적인 방법은 모든 시간 값이 특정 표준 시간대 컨텍스트에 저장되도록 디자인에서 "규칙"으로 만드는 것입니다. 이 방법은 표준 시간대 컨텍스트에 대한 사용자의 보기를 저장하기 위해 추가 스토리지가 필요하지 않지만, 규칙을 인식하지 못하는 개발자가 시간 값을 잘못 해석하거나 잘못 저장할 위험이 있습니다.
  3. 머신 현지 시간을 나타내는 값에 대한 날짜 및 시간 계산을 수행해도 항상 올바른 결과가 생성되는 것은 아닙니다. 일광 절약 시간을 연습하는 표준 시간대 컨텍스트에서 시간 값에 대한 계산을 수행하는 경우 날짜 산술 계산을 수행하기 전에 값을 범용 시간 표현으로 변환해야 합니다. 작업 및 적절한 표준 시간대 컨텍스트의 특정 목록은 DateTime 메서드 정렬 섹션의 표를 참조하세요.
  4. DateTime 값의 instance 계산해도 instance 값이 수정되지 않으므로 MyDateTime.ToLocalTime()을 호출해도 DateTime의 instance 값이 수정되지 않습니다. Date(Visual Basic®의 경우) 및 DateTime(.NET CLR의 경우) 클래스와 연결된 메서드는 계산 또는 작업의 결과를 나타내는 새 인스턴스를 반환합니다.
  5. .NET Framework 버전 1.0 및 1.1을 사용하는 경우System.XML 통해 UCT 시간을 나타내는 DateTime 값을 보내지 마세요 . Serialization. 이는 Date, Time 및 DateTime 값에 사용됩니다. System.DateTime과 관련된 웹 서비스 및 기타 형식의 XML 직렬화의 경우 항상 DateTime 값의 값이 현재 컴퓨터 로컬 시간을 나타내는지 확인합니다. 직렬 변환기는 GMT(오프셋 값 = 0)로 인코딩된 XML 스키마 정의 DateTime 값을 제대로 디코딩하지만 로컬 컴퓨터 시간 뷰포인트로 디코딩합니다.
  6. 일반적으로 시간 제한 측정, 산술 연산 수행 또는 다른 DateTime 값 비교와 같은 절대 경과 시간을 처리하는 경우 가능한 경우 표준 시간대 및/또는 일광 절약에 영향을 주지 않고 최상의 정확도를 얻을 수 있도록 유니버설 시간 값을 사용해야 합니다.
  7. 일정 예약과 같은 높은 수준의 사용자 지향 개념을 처리할 때 매일 사용자의 관점에서 24시간이 있다고 안전하게 가정할 수 있습니다. 로컬 시간에 산술 연산 등을 수행하여 규칙 #6에 대응하는 것이 괜찮을 수 있습니다.

이 문서 전체에서 이 간단한 규칙 목록은 날짜를 처리하는 애플리케이션을 작성하고 테스트하기 위한 모범 사례 집합의 기초 역할을 합니다.

지금까지 여러분 중 몇몇은 이미 코드를 살펴보고 "아, 내가 기대했던 일을 하고 있지 않다"고 말하고 있는데, 이는 이 문서의 목적입니다. 지금까지 읽기에서 주현절이 없었던 사람들을 위해 에서 DateTime 값 처리와 관련된 문제를 살펴보겠습니다 (지금부터는 이것을 "날짜"로 단축할 것입니다). NET 기반 애플리케이션.

스토리지 전략

위의 규칙에 따르면 날짜 값에 대한 계산은 처리 중인 날짜 값과 관련된 표준 시간대 정보를 이해하는 경우에만 의미가 있습니다. 즉, 클래스 멤버 변수에 일시적으로 값을 저장하거나 수집한 값을 데이터베이스 또는 파일에 저장하도록 선택하든 프로그래머가 나중에 관련 표준 시간대 정보를 이해할 수 있도록 하는 전략을 적용해야 합니다.

모범 사례 #1

코딩할 때 DateTime 형식과 연결된 표준 시간대 정보를 외래 변수에 저장합니다.

대안이지만 덜 신뢰할 수 있는 전략은 저장된 날짜가 저장되기 전에 GMT와 같은 특정 표준 시간대로 항상 변환된다는 확고한 규칙을 만드는 것입니다. 이것은 합리적으로 보일 수 있으며, 많은 팀이 작동할 수 있습니다. 그러나 데이터베이스의 테이블에 있는 특정 DateTime 열이 특정 표준 시간대에 있다는 오버트 신호가 없으면 프로젝트의 이후 반복에서 해석에 오류가 발생합니다.

다른 에 대한 비공식 설문 조사에서 볼 수 있는 일반적인 전략입니다. NET 기반 애플리케이션은 항상 날짜가 범용(GMT) 시간으로 표현되도록 하려는 것입니다. 나는 이것이 항상 실용적인 것은 아니기 때문에 "욕망"을 말한다. 웹 서비스를 통해 DateTime 멤버 변수가 있는 클래스를 직렬화할 때 특정 시점이 발생합니다. 그 이유는 DateTime 값 형식이 XSD:DateTime 형식에 매핑되고(예상대로) XSD 형식이 모든 표준 시간대의 시간 지점을 나타내기 때문입니다. 나중에 XML 사례에 대해 설명합니다. 더 흥미롭게도, 이러한 프로젝트의 좋은 비율은 실제로 목표를 달성하지 못했고, 이를 실현하지 않고 서버 표준 시간대에 날짜 정보를 저장하고 있었습니다.

이러한 경우 흥미로운 사실은 테스터가 시간 변환 문제를 보지 못했기 때문에 로컬 날짜 정보를 UCT 시간으로 변환해야 하는 코드가 실패했음을 아무도 발견하지 못했다는 것입니다. 이러한 특정 사례에서 데이터는 나중에 XML을 통해 직렬화되었고 날짜 정보가 시작할 컴퓨터 현지 시간에 있었기 때문에 제대로 변환되었습니다.

작동하지 않는 몇 가지 코드를 살펴보겠습니다.

Dim d As DateTime
d = DateTime.Parse("Dec 03, 2003 12:00:00 PM") 'date assignment
d.ToUniversalTime()

위의 프로그램은 변수 d 의 값을 가져와서 저장된 값이 시간의 UCT 뷰를 나타낼 것으로 예상하여 데이터베이스에 저장합니다. 이 예제에서는 일부 기본 문화권이 구문 분석 메서드 패밀리에 대한 선택적 인수로 사용되지 않는 한 Parse 메서드가 결과를 현지 시간으로 렌더링한다는 것을 인식합니다.

이전에 표시된 코드는 작성된 대로 샘플이 규칙 #4(DateTime 클래스의 메서드가 기본 값을 변환하지 않음)를 위반하기 때문에 DateTime 변수 d 의 값을 세 번째 줄의 유니버설 시간으로 변환하지 못합니다. 참고: 이 코드는 테스트된 실제 애플리케이션에서 볼 수 있습니다.

어떻게 통과했습니까? 테스트하는 동안 모든 데이터가 동일한 표준 시간대로 설정된 컴퓨터에서 제공되었기 때문에 관련된 애플리케이션은 저장된 날짜를 성공적으로 비교할 수 있었기 때문에 규칙 #1이 충족되었습니다(비교 및 계산되는 모든 날짜는 동일한 표준 시간대 관점으로 지역화됨). 이 코드의 버그는 검색하기 어려운 종류입니다. 실행되지만 아무 작업도 수행하지 않는 문입니다(힌트: 예제의 마지막 문은 작성된 대로 no-op임).

모범 사례 #2

테스트할 때 검사 저장된 값이 원하는 표준 시간대에서 의도한 지정 시간 값을 나타내는지 확인합니다.

코드 샘플을 쉽게 수정할 수 있습니다.

Dim d As DateTime
d = DateTime.Parse("Dec 03, 2003 12:00:00 PM").ToUniversalTime()

DateTime 값 형식과 연결된 계산 메서드는 기본 값에 영향을 주지 않고 대신 계산 결과를 반환하므로 프로그램은 변환된 값을 저장해야 합니다(원하는 경우). 다음으로 이 겉보기에 적절한 계산조차도 일광 절약 시간과 관련된 특정 상황에서 예상되는 결과를 달성하지 못하는 방법을 살펴보겠습니다.

계산 수행

언뜻 보기에 System.DateTime 클래스와 함께 제공되는 계산 함수는 정말 유용합니다. 시간 값에 간격을 추가하고, 시간 값에 대한 산술 연산을 수행하고, .NET 시간 값을 Win32® API 호출 및 OLE Automation 호출에 적합한 해당 값 형식으로 변환하기 위한 지원이 제공됩니다. DateTime 형식을 둘러싸는 지원 방법을 살펴보면 MS-DOS® 및 Windows®가 수년에 걸쳐 시간과 타임스탬프를 처리하기 위해 발전해 온 다양한 방법을 향수를 불러 일으킵니다.

이러한 모든 구성 요소가 운영 체제의 다양한 부분에 여전히 존재한다는 사실은 Microsoft에서 유지 관리하는 이전 버전과의 호환성 요구 사항과 관련이 있습니다. 즉, 프로그래머에게 파일, 디렉터리에서 타임스탬프를 나타내는 데이터를 이동하거나 날짜 및 DateTime 값과 관련된 COM/OLE Interop을 수행하는 경우 Windows에 있는 다양한 세대 간의 변환을 처리하는 데 능숙해야 합니다.

다시 속지 마세요

표준 시간대 오프셋(그리고 태평양 표준시 또는 PST와 같은 표준 시간대의 사용자 눈 보기)을 저장해야 하는 오버헤드를 방지하기 위해 "UCT 시간에 모든 것을 저장합니다" 전략을 채택한다고 가정해 보겠습니다. UCT 시간을 사용하여 계산을 수행하는 데는 몇 가지 이점이 있습니다. 그 중 최고는 보편적 인 시간에 표현 될 때, 매일 고정 된 길이를 가지고 있으며, 처리 할 표준 시간대 오프셋이 없다는 사실이다.

하루에 길이가 다를 수 있다는 사실에 놀랐다면 일광 절약 시간을 허용하는 표준 시간대에서 일년 중 2일(일반적으로) 일의 길이가 다르다는 점에 유의하세요. 따라서 PST(Pacific Standard Time)와 같은 현지 시간 값을 사용하는 경우에도 특정 DateTime instance 값에 시간 범위를 추가하려고 하면 추가되는 간격이 일광 절약 시간이 시작되거나 끝나는 날짜의 변경 시간을 지나야 한다고 생각한 결과를 얻지 못할 수 있습니다.

미국 태평양 표준 시간대에서 작동하지 않는 코드의 예를 살펴보겠습니다.

Dim d As DateTime
d = DateTime.Parse("Oct 26, 2003 12:00:00 AM") 'date assignment
d = d.AddHours(3.0)
' - displays 10/26/2003 03:00:00 AM – an ERROR!
MsgBox(d.ToString)

이 계산에서 표시되는 결과는 언뜻 보기에 올바르게 보일 수 있습니다. 그러나 2003년 10월 26일 오전 1:59 PST 이후 1분 후에 일광 절약 시간 변경이 적용되었습니다. 정답은 2003년 10월 26일 오전 02:00:00이어야 하므로 현지 시간 값을 기반으로 한 이 계산이 올바른 결과를 산출하지 못했습니다. 그러나 규칙 #3을 되돌아 보면 모순이 있는 것 같지만 그렇지 않습니다. 일광 절약 시간을 축하하는 표준 시간대에서 Add/Subtract 메서드를 사용하기 위한 특별한 사례라고 하겠습니다.

모범 사례 #3

코딩할 때 일광 절약 시간을 연습하는 표준 시간대를 나타내는 값에 대해 DateTime 계산(추가/빼기)을 수행해야 하는 경우 주의해야 합니다. 예기치 않은 계산 오류가 발생할 수 있습니다. 대신 현지 시간 값을 유니버설 시간으로 변환하고, 계산을 수행하고, 다시 변환하여 최대 정확도를 달성합니다..

이 손상된 코드를 수정하는 것은 간단합니다.

Dim d As DateTime
d = DateTime.Parse("Oct 26, 2003 12:00:00 AM") 'date assignment
d = d.ToUniversalTime().AddHours(3.0).ToLocalTime()
' - displays 10/26/2003 02:00:00 AM – Correct!
MsgBox(d.ToString)
  

시간 범위를 안정적으로 추가하는 가장 쉬운 방법은 로컬 시간 기반 값을 범용 시간으로 변환하고 계산을 수행한 다음 값을 다시 변환하는 것입니다.

DateTime 메서드 정렬

이 문서에서는 다양한 System.DateTime 클래스 메서드에 대해 설명합니다. 일부는 기본 instance 현지 시간을 나타내는 경우, 일부는 유니버설 시간을 나타내고 다른 instance 여전히 기본 instance 필요로 하지 않는 경우 올바른 결과를 생성합니다. 또한 일부는 표준 시간대(예: AddYear, AddMonth)에 완전히 독립적입니다. 가장 일반적으로 발생하는 DateTime 지원 메서드 뒤에 있는 가정에 대한 전반적인 이해를 간소화하기 위해 다음 표가 제공됩니다.

테이블을 읽으려면 시작(입력) 및 끝(반환된 값) 뷰포인트를 고려합니다. 모든 경우에 메서드 호출의 끝 상태는 메서드에 의해 반환됩니다. 데이터의 기본 instance 변환되지 않습니다. 예외 또는 유용한 지침을 설명하는 주의 사항도 제공됩니다.

메서드 이름 시작 뷰포인트 끝 뷰포인트 제한 사항
ToUniversalTime 현지 시간 UTC 이미 유니버설 타임을 나타내는 DateTime instance 호출하지 마세요.
ToLocalTime UTC 현지 시간 이미 현지 시간을 나타내는 DateTime instance 호출하지 마세요.
ToFileTime 현지 시간   메서드는 Win32 파일 시간(UCT 시간)을 나타내는 INT64를 반환합니다.
FromFileTime   현지 시간 정적 메서드- instance 필요하지 않습니다. INT64 UCT 시간을 입력으로 사용합니다.
ToFileTimeUtc

(V1.1에만 해당)

UTC   메서드는 Win32 파일 시간(UCT 시간)을 나타내는 INT64를 반환합니다.
FromFileTimeUtc

(V1.1에만 해당)

  UTC 메서드는 INT64 Win32 파일 시간을 DateTime UCT instance 변환합니다.
지금   현지 시간 정적 메서드- instance 필요하지 않습니다. 로컬 머신 시간의 현재 시간을 나타내는 DateTime을 반환합니다.
UtcNow   UTC 정적 메서드 - instance 필요 없음
IsLeapYear 현지 시간   현지 시간 instance 연도 부분이 윤년인 경우 true를 나타내는 부울을 반환합니다.
오늘   현지 시간 정적 메서드- instance 필요하지 않습니다. 로컬 머신 시간에서 현재 날짜의 자정으로 설정된 DateTime을 반환합니다.

XML의 특수 사례

최근에 이야기한 몇몇 사람들은 DateTime을 나타내는 XML이 GMT(예: 오프셋 0)로 포맷되도록 웹 서비스를 통해 시간 값을 직렬화하는 디자인 목표를 가지고 있었습니다. 클라이언트에 표시할 텍스트 문자열로 필드를 구문 분석하는 것부터 서버에 있는 "UCT에 저장된" 가정을 웹 서비스 호출자에게 보존하려는 욕구에서부터 이 정도까지 와이어의 마샬링 형식을 제어할 만한 이유가 있다고 확신하지 못했습니다. 그 이유는 DateTime 형식에 대한 XML 인코딩은 빠른 시간을 나타내는 데 완벽하게 적합하고, .NET Framework 기본 제공되는 XML serializer는 시간 값과 관련된 직렬화 및 역직렬화 문제를 관리하는 훌륭한 작업을 수행하기 때문입니다.

또한, 그것은 System.XML 강요하는 것으로 나타났습니다. 와이어의 GMT에서 날짜 값을 인코딩하는 직렬화 직렬 변환기는 .NET에서 가능하지 않습니다. 프로그래머, 디자이너 또는 프로젝트 관리자로서 작업은 애플리케이션에 전달되는 데이터가 최소한의 비용으로 정확하게 수행되도록 합니다.

이 논문에 포함된 연구에서 제가 이야기한 몇몇 그룹은 특수 클래스를 정의하고 자체 XML 직렬 변환기를 작성하는 전략을 채택하여 와이어의 DateTime 값이 XML에서 어떻게 생겼는지 완전히 제어할 수 있도록 했습니다. 나는 개발자가이 용감한 사업에 도약 할 때 가지고있는 뽑아 존경하는 동안, 일광 절약 시간 및 표준 시간대 변환 문제를 혼자 처리하는 뉘앙스는 좋은 관리자가 말해야한다 안심, "방법," 특히 .NET Framework 제공 된 메커니즘은 이미 시간 값을 직렬화의 완벽하게 정확한 일을 할 때.

알아야 할 트릭은 하나뿐이며 디자이너로서 이를 이해하고 규칙을 준수해야 합니다(규칙 #5 참조).

작동하지 않는 코드:

먼저 DateTime 멤버 변수를 사용하여 간단한 XML 클래스를 정의해 보겠습니다. 완전성을 위해 이 클래스는 문서의 뒷부분에 설명된 권장 접근 방식과 동일한 간소화된 클래스입니다.

<XmlType(TypeName:="timeTestDef", _
    Namespace:= "http://tempuri.org/Timetester.xsd")>), _
    XmlRoot(), Serializable()> _
Public Class timeTestDef
    Private __timeVal As DateTime

    <XmlIgnore()> _
    Public timeValSpecified As Boolean

    <XmlElement(ElementName:="timeVal", IsNullable:=False, _
        Form:=XmlSchemaForm.Qualified, DataType:="dateTime", _
        Namespace:="http://tempuri.org/Timetester.xsd")> _
    Public Property timeVal() As DateTime
        Get
            timeVal = __timeVal
        End Get
        Set(ByVal Value As DateTime)
            __timeVal = Value
            timeValSpecified = True
        End Set
    End Property
End Class

이제 이 클래스를 사용하여 파일에 XML을 작성해 보겠습니다.

' write out to the file
Dim t As Xml.XmlTextWriter
Dim ser As XmlSerializer
Dim tt As New timeTest ' a class that has a DateTime variable
' set the fields in your class
tt.timeVal = DateTime.Parse("12/12/2003 12:01:02 PM")
tt.timeVal = tt.TimeVal.ToUniversalTime()

' get a serializer for the root type, and serialize this UTC time
ser = New XmlSerializer(GetType(timeTest))
t = New Xml.XmlTextWriter("c:\timetest.xml", System.Text.Encoding.UTF8)
ser.Serialize(t, tt)
t.Close()
t = Nothing
tt = Nothing

이 코드가 실행되면 출력 파일로 직렬화된 XML에는 다음과 같은 XML DateTime 표현이 포함됩니다.

<timeVal>2003-12-12T20:01:02.0000000-08:00</timeVal> 

XML에 인코딩된 값이 8시간 동안 꺼져 있습니다. 현재 컴퓨터의 표준 시간대 오프셋이므로 의심스러워야 합니다. XML 자체를 보면 날짜가 맞고 20:01:02 날짜는 내 정오에 런던의 시계 시간에 해당하지만 오프셋 부분은 런던 기반 시계에 대해 올바르지 않습니다. XML이 런던 시간처럼 보이는 경우 오프셋은 이 코드가 달성하지 못하는 런던 관점도 나타내야 합니다.

XML serializer는 항상 serialize되는 DateTime 값이 로컬 머신 시간을 나타낸다고 가정하므로 인코딩된 XML 시간의 오프셋 부분으로 머신 로컬 표준 시간대 오프셋을 적용합니다. 이를 다른 컴퓨터로 역직렬화하면 원래 오프셋이 구문 분석되는 값에서 빼지고 현재 컴퓨터의 표준 시간대 오프셋이 추가됩니다.

로컬 시간으로 시작하면 serialization(XML DateTime으로 인코딩한 다음 로컬 머신 시간으로 디코딩)의 결과가 항상 올바르지만 직렬화되는 시작 DateTime 값이 serialization이 시작될 때 로컬 시간을 나타내는 경우에만 올바릅니다. 이 손상된 코드 예제의 경우 timeVal 멤버 변수의 DateTime 값을 UCT 시간으로 이미 조정했으므로 직렬화 및 역직렬화할 때 결과는 원래 컴퓨터의 표준 시간대 오프셋과 동일한 시간 수만큼 꺼집니다. 이것은 나쁘다.

모범 사례 #4

테스트할 때 테스트되는 시점의 머신 로컬 시간 보기를 사용하여 직렬화된 XML 문자열에 표시되는 값을 계산합니다. serialization 스트림의 XML이 다른 경우 버그를 기록합니다.

이 코드를 수정하는 것은 간단합니다. ToUniversalTime()을 호출하는 줄을 주석으로 처리합니다.

모범 사례 #5

DateTime 멤버 변수가 있는 클래스를 serialize하는 코드를 작성할 때 값은 현지 시간을 나타내야 합니다. 현지 시간이 포함되지 않은 경우 웹 서비스에서 DateTime 값을 포함하는 형식 전달 또는 반환을 포함하여 serialization 단계 전에 조정합니다.

클래스 코더 Quandary

이전에 DateTime 속성을 노출하는 매우 정교하지 않은 클래스를 살펴보았습니다. 해당 클래스에서 값이 로컬 또는 범용 시간 뷰포인트를 나타내는지 여부에 관계없이 DateTime에 저장한 내용을 직렬화했습니다. 프로그래머에게 항상 제대로 직렬화하면서 원하는 표준 시간대 가정에 대한 선택 사항을 제공하는 보다 정교한 접근 방식을 살펴보겠습니다.

DateTime 형식의 멤버 변수가 있는 클래스를 코딩하는 경우 프로그래머는 멤버 변수를 공용으로 만들거나 속성 논리를 작성하여 멤버 변수를 get/set 작업으로 래핑할 수 있습니다. 형식을 공개하도록 선택하면 DateTime 형식의 경우 클래스 개발자가 제어하지 않는 결과가 발생할 수 있는 몇 가지 단점이 있습니다.

지금까지 배운 내용을 사용하여 각 DateTime 형식에 대해 두 개의 속성을 제공하는 것이 좋습니다.

다음 예제에서는 DateTime 멤버 변수를 관리하는 데 권장되는 방법을 보여 줍니다.

<XmlType(TypeName:="timeTestDef", _
    Namespace:= "http://tempuri.org/Timetester.xsd")>), _
    XmlRoot(), Serializable(), _
    EditorBrowsable(EditorBrowsableState.Advanced)> _
Public Class timeTestDef
    Private __timeVal As DateTime

    <XmlIgnore()> _
    Public timeValSpecified As Boolean

    <XmlElement(ElementName:="timeVal", IsNullable:=False, _
        Form:=XmlSchemaForm.Qualified, DataType:="dateTime", _
        Namespace:="http://tempuri.org/Timetester.xsd")> _
    Public Property timeVal() As DateTime
        Get
            timeVal = __timeVal.ToLocalTime()
        End Get
        Set(ByVal Value As DateTime)
            __timeVal = Value.ToUniversalTime()
            timeValSpecified = True
        End Set
    End Property

    <XmlIgnore()> _
    Public Property timeValUTC() As DateTime
        Get
            timeValUTC = __timeVal
        End Get
        Set(ByVal Value As DateTime)
            __timeVal = Value
            timeValSpecified = True
        End Set
    End Property
End Class

이 예제는 이전 클래스 serialization 예제와 동일한 수정된 입니다. 두 클래스 예제(이 예제와 이전 클래스)에서 클래스는 다음 스키마로 설명된 구현입니다.

<?xml version="1.0" encoding="utf-8" ?> 
<xs:schema id="Timetester" 
     targetNamespace="http://tempuri.org/Timetester.xsd"
     elementFormDefault="qualified"
     xmlns="http://tempuri.org/Timetester.xsd"
     xmlns:mstns="http://tempuri.org/Timetester.xsd"
     xmlns:xs="http://www.w3.org/2001/XMLSchema">

     <xs:element name="timeTest" type="timeTestDef"/>
     <xs:complexType name="timeTestDef">
      <xs:sequence>
         <xs:element name="timeVal" type="xs:dateTime"/>
      </xs:sequence>
     </xs:complexType>
</xs:schema>

이 스키마 및 클래스 구현에서는 선택적 시간 값을 나타내는 멤버 변수를 정의합니다. 권장 예제에서는 getter와 setter가 모두 포함된 두 개의 속성을 제공했습니다. 하나는 유니버설 시간용이고 다른 하나는 현지 시간용입니다. 코드에 표시되는 꺾쇠 괄호로 묶인 특성은 XML serializer에 직렬화에 현지 시간 버전을 사용하도록 지시하고 일반적으로 클래스 구현을 스키마 규격 출력으로 만듭니다. instance 값이 설정되지 않은 경우 클래스가 선택적 식 부족을 제대로 처리하도록 하기 위해 속성 setter의 timeValSpecified 변수 및 연결된 논리는 XML 요소가 serialization 시간에 표현되는지 여부를 제어합니다. 이 선택적 동작은 선택적 XML 콘텐츠를 지원하도록 설계된 serialization 하위 시스템의 기능을 활용합니다.

.NET 클래스에서 DateTime 값을 관리하는 이 방법을 사용하면 계산이 정확하고 현지 시간 보기를 적절하게 직렬화할 수 있도록 범용 시간에 따라 스토리지 액세스 권한을 얻을 수 있습니다.

모범 사례 #6

코딩할 때 DateTime 멤버 변수를 비공개로 만들고 로컬 또는 범용 시간에 DateTime 멤버를 조작하기 위한 두 가지 속성을 제공합니다. getter 및 setter의 논리를 제어하여 프라이빗 멤버의 스토리지를 UCT 시간으로 편향합니다. 현지 시간 속성 선언에 XML serialization 특성을 추가하여 현지 시간 값이 serialize되는지 확인합니다(예제 참조).

이 방법에 대한 주의 사항

프라이빗 멤버 변수 내에서 유니버설 시간에서 DateTime을 관리하는 권장 방법은 코더가 가장 익숙한 시간 버전을 처리할 수 있도록 이중 속성을 제공하는 권장 사항과 마찬가지로 건전합니다. 개발자가 이 방법 또는 프로그램에 현지 시간을 노출하는 다른 접근 방식을 사용하는 한 가지 문제는 일광 절약 시간제와 관련된 25시간의 문제입니다. 이는 CLR 버전 1.0 및 1.1을 사용하는 프로그램의 문제가 계속되므로 프로그램이 이 특별한 경우(표시되는 시간에 추가되거나 누락된 시간)에 속하는지 여부를 알고 있어야 하며 수동으로 조정해야 합니다. 연간 1시간 문제 기간을 허용할 수 없는 경우 현재 권장 사항은 날짜를 문자열 또는 다른 자체 관리형 접근 방식으로 저장하는 것입니다. (Unix long 정수는 좋은 옵션입니다.)

CLR 버전 2.0(Visual Studio® 코드 이름 "Whidbey"의 향후 릴리스에서 사용 가능)의 경우 DateTime에 현지 시간이 포함되어 있는지 또는 유니버설 시간 값이 .NET Framework 추가되는지 여부를 인식합니다. 이 시점에서 권장 패턴은 계속 작동하지만 UTC 속성을 통해 멤버 변수와 상호 작용하는 프로그램의 경우 누락/추가 시간 동안 이러한 오류가 제거됩니다. 이러한 이유로 프로그램이 CLR 버전 2.0으로 완전히 마이그레이션되도록 이중 속성을 사용하여 코딩하는 것이 좋습니다.

일광 절약 시간 처리

DateTime 값에 대한 코딩 및 테스트 사례에 대한 항목을 닫고 떠날 준비를 하면서 이해해야 할 특별한 사례가 하나 남아 있습니다. 이 사례에는 일광 절약 시간제와 반복되는 연간 1시간 문제를 둘러싼 모호성이 포함됩니다. 이 문제는 주로 사용자 입력에서 시간 값을 수집하는 애플리케이션에만 영향을 주는 문제입니다.

대부분의 국가에서 일광 절약 시간이 실행되지 않기 때문에 국가 수 대다수의 사용자에게이 경우는 사소합니다. 그러나 영향을 받는 프로그램 대다수(즉, 일광 절약을 연습하는 장소에서 표현되거나 공급될 수 있는 시간을 처리해야 하는 애플리케이션이 있는 모든 사용자)의 경우 이 문제가 존재한다는 것을 알고 고려해야 합니다.

일광 절약 시간을 연습하는 세계의 지역에서, 시간이 겉보기에 건초가 간다 가을과 봄마다 한 시간이있다. 시계 시간이 표준 시간에서 일광 시간으로 이동하는 밤에는 시간이 한 시간 앞으로 이동합니다. 이것은 봄에 발생합니다. 올해의 가을에, 어느 밤에, 현지 시간 시계는 한 시간 다시 점프.

요즘에는 하루 길이가 23시간 또는 25시간인 조건이 발생할 수 있습니다. 따라서 날짜 값에서 시간 범위를 추가하거나 빼고 범위가 시계가 전환되는 이 이상한 시점을 넘으면 코드에서 수동으로 조정해야 합니다.

DateTime.Parse() 메서드를 사용하여 특정 날짜 및 시간의 사용자 입력을 기반으로 DateTime 값을 계산하는 논리의 경우 특정 값이 유효하지 않다는 것을 감지해야 합니다(23시간 일). 특정 값은 두 가지 의미가 있습니다. 특정 시간은 (25시간 일) 반복되기 때문입니다. 이렇게 하려면 관련된 날짜를 알고 이 시간을 찾아야 합니다. 사용자가 날짜를 입력하는 데 사용되는 필드를 종료할 때 해석된 날짜 정보를 구문 분석하고 다시 표시하는 것이 유용할 수 있습니다. 일반적으로 사용자가 입력에서 일광 절약 시간을 지정하지 않도록 합니다.

우리는 이미 시간 범위 계산에 대한 모범 사례를 다루었습니다. 계산을 수행하기 전에 현지 시간 보기를 범용 시간으로 변환하면 시간 정확도 문제가 지나게 됩니다. 관리하기 어려운 사례는 봄과 가을에 이 마법의 시간 동안 발생하는 사용자 입력 구문 분석과 관련된 모호성 사례입니다.

현재 사용자의 시간 보기를 나타내는 문자열을 구문 분석하고 범용 시간 값을 정확하게 할당할 수 있는 방법은 없습니다. 그 이유는 일광 절약 시간을 경험하는 사람들은 표준 시간대가 그리니치 표준시인 장소에 살지 않기 때문입니다. 따라서 미국 동부 해안에 사는 사람이 "2003년 10월 26일 오전 01:10:00"와 같은 값을 입력할 수 있습니다.

이 특정 아침, 오전 2:00에 현지 시계가 오전 1:00로 다시 설정되어 하루 25시간이 생성됩니다. 1:00 AM에서 2:00 AM 사이의 모든 클록 시간 값은 해당 특정 아침에 두 번 발생하므로 적어도 대부분의 미국 및 캐나다에서 발생합니다. 컴퓨터는 실제로 어떤 오전 1시 10분이 의미되었는지( 스위치 이전에 발생한 것 또는 일광 절약 시간 전환 후 10분 후에 발생하는 것)를 알 수 있는 방법이 없습니다.

마찬가지로, 프로그램은 특정 아침에 오전 2시 10분과 같은 시간이 없을 때 봄철에 발생하는 문제를 처리해야 합니다. 그 이유는 특정 아침 2:00에 현지 시계의 시간이 갑자기 오전 3:00로 변경하기 때문입니다. 전체 2:00 시간은 이 23시간 날에는 발생하지 않습니다.

프로그램에서 모호성을 감지할 때 사용자에게 메시지를 표시하여 이러한 경우를 처리해야 합니다. 사용자로부터 날짜-시간 문자열을 수집하고 구문 분석하지 않는 경우 이러한 문제가 없을 수 있습니다. 특정 시간이 일광 절약 시간에 속하는지 여부를 결정해야 하는 프로그램은 다음을 사용할 수 있습니다.

Timezone.CurrentTimeZone.IsDaylightSavingTime(DateTimeInstance)

또는

DateTimeInstance.IsDaylightSavingTime

모범 사례 #7

테스트할 때 프로그램에서 날짜 및 시간 값을 지정하는 사용자 입력을 수락하는 경우 "spring-ahead", "fall-back" 23일 및 25시간 동안 데이터 손실을 테스트해야 합니다. 또한 한 표준 시간대의 컴퓨터에 수집되어 다른 표준 시간대의 컴퓨터에 저장된 날짜를 테스트해야 합니다.

User-Ready 값 서식 지정 및 구문 분석

사용자의 날짜 및 시간 정보를 사용하고 이 사용자 입력을 DateTime 값으로 변환해야 하는 프로그램의 경우 프레임워크는 특정 방식으로 형식이 지정된 문자열 구문 분석을 지원합니다. 일반적으로 DateTime.ParseParseExact 메서드는 날짜 및 시간이 포함된 문자열을 DateTime 값으로 변환하는 데 유용합니다. 반대로 ToString, ToLongDateString, ToLongTimeString, ToShortDateStringToShortTimeString 메서드는 모두 DateTime 값을 사람이 읽을 수 있는 문자열로 렌더링하는 데 유용합니다.

구문 분석에 영향을 주는 두 가지 기본 문제는 문화권 및 서식 문자열입니다. DateTime FAQ(질문과 대답)는 문화권과 관련된 기본 문제를 다루므로 여기서는 DateTime 구문 분석에 영향을 주는 형식 문자열 모범 사례에 초점을 맞춥니다.

DateTime을 문자열로 변환하는 데 권장되는 형식 문자열은 다음과 같습니다.

'yyyy'-'MM'-'dd'T'HH': 'mm': 'ss.fffffff'Z' - UCT 값의 경우

'yyyy'-'MM'-'dd'T'HH': 'mm': 'ss.fffffff'zzz' - 로컬 값의 경우

'yyyy'-'MM'-'dd'T'HH': 'mm': 'ss.fffffff' - 추상 시간 값

XML DateTime 형식 사양과 호환되는 출력을 얻으려면 DateTime.ToString 메서드에 전달되는 형식 문자열 값입니다. 따옴표는 컴퓨터의 로컬 날짜-시간 설정이 서식 옵션을 재정의하지 않도록 합니다. 다른 레이아웃을 지정해야 하는 경우 상당히 유연한 날짜 렌더링 기능을 위해 다른 형식 문자열을 전달할 수 있지만 Z 표기법만 사용하여 UCT 값에서 문자열을 렌더링하고 현지 시간 값에 zzz 표기법을 사용해야 합니다.

문자열을 구문 분석하고 DateTime 값으로 변환하는 작업은 DateTime.Parse 및 ParseExact 메서드를 사용하여 수행할 수 있습니다. ParseExact는 instance 고유한 Formatter 개체를 제공해야 하므로 대부분의 경우 Parse로 충분합니다. 구문 분석은 매우 가능하고 유연하며 날짜와 시간을 포함하는 대부분의 문자열을 정확하게 변환할 수 있습니다.

마지막으로 스레드의 CultureInfo를 CultureInfo.InvariantCulture로 설정한 후에만 항상 Parse 및 ToString 메서드를 호출하는 것이 중요합니다.

향후 고려 사항

DateTime.ToString에서 현재 쉽게 수행할 수 없는 한 가지 작업은 DateTime 값을 임의의 표준 시간대로 포맷하는 것입니다. 이 기능은 .NET Framework 향후 구현을 위해 고려되고 있습니다. 문자열 "12:00:00 EST"가 "11:00:00 EDT"와 동일한지 확인할 수 있어야 하는 경우 변환 및 비교를 직접 처리해야 합니다.

DateTime.Now() 메서드 관련 문제

Now라는 메서드를 처리할 때 몇 가지 문제가 있습니다. 이 내용을 읽는 Visual Basic 개발자의 경우 Visual Basic Now 함수에도 적용됩니다. Now 메서드를 정기적으로 사용하는 개발자는 현재 시간을 가져오는 데 일반적으로 사용된다는 것을 알고 있습니다. Now 메서드에서 반환된 값은 현재 컴퓨터 표준 시간대 컨텍스트에 있으며 변경할 수 없는 값으로 처리할 수 없습니다. 일반적인 방법은 머신 간에 저장되거나 전송될 시간을 UCT(유니버설) 시간으로 변환하는 것입니다.

일광 절약 시간이 가능한 경우 피해야 하는 코딩 방법이 하나 있습니다. 검색하기 어려운 버그를 도입할 수 있는 다음 코드를 고려합니다.

Dim timeval As DateTime
timeval = DateTime.Now().ToUniversalTime()  

이 코드를 실행하여 발생하는 값은 가을에 일광 절약 시간 전환 중에 발생하는 추가 시간 동안 호출되는 경우 1시간씩 꺼집니다. (일광 절약 시간을 연습하는 표준 시간대에 있는 컴퓨터에만 적용됩니다.) 추가 시간은 오전 1:10:00과 같은 동일한 값이 해당 아침에 두 번 발생하는 위치에 속하기 때문에 반환된 값이 원하는 값과 일치하지 않을 수 있습니다.

이 문제를 해결하려면 DateTime.Now를 호출한 다음 유니버설 시간으로 변환하는 대신 DateTime.UtcNow()를 호출하는 것이 좋습니다.

Dim timeval As DateTime
timeval = DateTime.UtcNow()  

이 코드는 항상 적절한 24시간 큐브 뷰를 가지며 로컬 시간으로 안전하게 변환될 수 있습니다.

모범 사례 #8

코딩하고 현재 시간을 범용 시간으로 저장하려는 경우 DateTime.Now()를 호출한 다음 유니버설 시간으로 변환하지 마세요. 대신 DateTime.UtcNow 함수를 직접 호출합니다.

주의 사항: DateTime 값이 포함된 클래스를 serialize하려는 경우 serialize되는 값이 유니버설 시간을 나타내지 않는지 확인합니다. XML serialization은 Visual Studio의 Whidbey 릴리스까지 UCT serialization을 지원하지 않습니다.

잘 알려지지 않은 몇 가지 엑스트라

경우에 따라 API의 일부로 다이빙을 시작할 때 목표를 달성하는 데 도움이 되는 숨겨진 보석을 찾을 수 있지만, 이에 대해 말하지 않으면 일상적인 여행에서 발견되지 않습니다. .NET의 DateTime 값 형식에는 범용 시간을 보다 일관되게 사용하는 데 도움이 될 수 있는 몇 가지 gem이 있습니다.

첫 번째는 System.Globalization 네임스페이스에 있는 DateTimeStyles 열거형입니다. 열거형은 사용자가 지정한 입력 및 다른 형식의 입력 문자열 표현을 DateTime 값으로 변환하는 데 사용되는 DateTime.Parse() 및 ParseExact 함수의 동작을 제어합니다.

다음 표에서는 DateTimeStyles 열거형에서 사용할 수 있는 몇 가지 기능을 강조 표시합니다.

열거형 상수 목적 제한 사항
AdjustToUniversal Parse 또는 ParseExact 메서드의 일부로 전달되면 이 플래그로 인해 반환된 값이 범용 시간으로 반환됩니다. 설명서는 모호하지만 Parse 및 ParseExact 둘 다에서 작동합니다.
NoCurrentDateDefault 날짜 구성 요소 없이 구문 분석되는 문자열에 현재 날짜의 시간인 DateTime 값이 반환된다는 가정을 표시하지 않습니다. 이 옵션을 사용하는 경우 반환되는 DateTime 값은 1년 1월 1일의 그레고리오 날짜에 지정된 시간입니다.
AllowWhiteSpaces

AllowTrailingWhite

AllowLeadingWhite

AllowInnerWhite

이러한 옵션은 모두 구문 분석되는 날짜 문자열의 앞, 뒤 및 중간에 추가된 공백에 대한 허용 오차를 사용하도록 설정합니다. 없음

다른 흥미로운 지원 함수는 System.Timezone 클래스에서 찾을 수 있습니다. 일광 절약 시간이 DateTime 값에 영향을 미치는지 또는 로컬 머신의 현재 표준 시간대 오프셋을 프로그래밍 방식으로 확인하려는 경우 이를 검사 합니다.

결론

.NET Framework DateTime 클래스는 시간을 처리하는 프로그램을 작성하기 위한 완전한 기능을 갖춘 인터페이스를 제공합니다. 클래스 처리의 뉘앙스를 이해하는 것은 Intellisense®에서 수집할 수 있는 것 이상입니다. 여기서는 날짜와 시간을 처리하는 코딩 및 테스트 프로그램에 대한 모범 사례를 설명했습니다. 즐거운 코딩 작업이 되길 바랍니다!