다음을 통해 공유


DateTime, DateOnly, DateTimeOffset, TimeSpan, TimeOnly 및 TimeZoneInfo 중에서 선택

.NET 애플리케이션은 여러 가지 방법으로 날짜 및 시간 정보를 사용할 수 있습니다. 날짜 및 시간 정보의 일반적인 용도는 다음과 같습니다.

  • 날짜만 반영하기 때문에 시간 정보가 중요하지 않습니다.
  • 날짜 정보가 중요하지 않도록 시간만 반영합니다.
  • 특정 시간과 장소에 연결되지 않은 추상적인 날짜 및 시간을 반영합니다(예: 국제 체인의 대부분의 매장은 평일 오전 9시에 영업).
  • .NET 외부의 원본에서 날짜 및 시간 정보를 검색하려면 일반적으로 날짜 및 시간 정보가 간단한 데이터 형식으로 저장됩니다.
  • 단일 시점을 고유하고 명확하게 식별합니다. 일부 애플리케이션에서는 호스트 시스템에서만 날짜와 시간이 모호해야 합니다. 다른 앱에서는 시스템 간에 모호하지 않도록 하는 것이 필요합니다. 즉, 한 시스템에서 직렬화된 날짜가 다른 시스템에서 의미 있게 역직렬화되어 세계 어디서나 사용될 수 있습니다.
  • 여러 관련 시간(예: 요청자의 현지 시간 및 웹 요청에 대한 서버의 수신 시간)을 유지합니다.
  • 날짜 및 시간 연산을 수행하여, 하나의 시점을 명확하고 고유하게 식별할 수 있는 결과를 얻을 수 있습니다.

.NET에는 날짜 및 시간을 사용하는 애플리케이션을 빌드하는 데 사용할 수 있는 DateTime, DateOnly, DateTimeOffset, TimeSpan, TimeOnlyTimeZoneInfo 형식이 포함됩니다.

메모

이 문서에서는 해당 기능이 거의 전적으로 TimeZone 클래스에 통합되어 있으므로 TimeZoneInfo 설명하지 않습니다. 가능하면 TimeZoneInfo 클래스 대신 TimeZone 클래스를 사용합니다.

DateTimeOffset 구조체

DateTimeOffset 구조체는 날짜 및 시간 값을 UTC와 얼마나 다른지 나타내는 오프셋과 함께 나타냅니다. 따라서 값은 항상 단일 시점을 명확하게 식별합니다.

DateTimeOffset 형식에는 표준 시간대 인식과 함께 DateTime 형식의 모든 기능이 포함됩니다. 이렇게 하면 다음과 같은 애플리케이션에 적합합니다.

  • 단일 시점을 고유하고 명확하게 식별합니다. DateTimeOffset 형식을 사용하여 "now"의 의미를 명확하게 정의하고, 트랜잭션 시간을 기록하고, 시스템 또는 애플리케이션 이벤트의 시간을 기록하고, 파일 생성 및 수정 시간을 기록할 수 있습니다.
  • 일반 날짜 및 시간 산술 연산을 수행합니다.
  • 이러한 시간이 두 개의 별도 값 또는 구조체의 두 멤버로 저장되어 있는 한, 관련된 여러 시간을 보존합니다.

메모

DateTimeOffset 값에 대한 이러한 사용은 DateTime 값보다 훨씬 일반적입니다. 따라서 DateTimeOffset 애플리케이션 개발의 기본 날짜 및 시간 유형으로 간주합니다.

DateTimeOffset 값은 특정 표준 시간대에 연결되지 않지만 다양한 표준 시간대에서 비롯할 수 있습니다. 다음 예제에서는 여러 DateTimeOffset 값(현지 태평양 표준시 포함)이 속할 수 있는 표준 시간대를 나열합니다.

using System;
using System.Collections.ObjectModel;

public class TimeOffsets
{
   public static void Main()
   {
      DateTime thisDate = new DateTime(2007, 3, 10, 0, 0, 0);
      DateTime dstDate = new DateTime(2007, 6, 10, 0, 0, 0);
      DateTimeOffset thisTime;

      thisTime = new DateTimeOffset(dstDate, new TimeSpan(-7, 0, 0));
      ShowPossibleTimeZones(thisTime);

      thisTime = new DateTimeOffset(thisDate, new TimeSpan(-6, 0, 0));
      ShowPossibleTimeZones(thisTime);

      thisTime = new DateTimeOffset(thisDate, new TimeSpan(+1, 0, 0));
      ShowPossibleTimeZones(thisTime);
   }

   private static void ShowPossibleTimeZones(DateTimeOffset offsetTime)
   {
      TimeSpan offset = offsetTime.Offset;
      ReadOnlyCollection<TimeZoneInfo> timeZones;

      Console.WriteLine("{0} could belong to the following time zones:",
                        offsetTime.ToString());
      // Get all time zones defined on local system
      timeZones = TimeZoneInfo.GetSystemTimeZones();
      // Iterate time zones
      foreach (TimeZoneInfo timeZone in timeZones)
      {
         // Compare offset with offset for that date in that time zone
         if (timeZone.GetUtcOffset(offsetTime.DateTime).Equals(offset))
            Console.WriteLine("   {0}", timeZone.DisplayName);
      }
      Console.WriteLine();
   }
}
// This example displays the following output to the console:
//       6/10/2007 12:00:00 AM -07:00 could belong to the following time zones:
//          (GMT-07:00) Arizona
//          (GMT-08:00) Pacific Time (US & Canada)
//          (GMT-08:00) Tijuana, Baja California
//
//       3/10/2007 12:00:00 AM -06:00 could belong to the following time zones:
//          (GMT-06:00) Central America
//          (GMT-06:00) Central Time (US & Canada)
//          (GMT-06:00) Guadalajara, Mexico City, Monterrey - New
//          (GMT-06:00) Guadalajara, Mexico City, Monterrey - Old
//          (GMT-06:00) Saskatchewan
//
//       3/10/2007 12:00:00 AM +01:00 could belong to the following time zones:
//          (GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna
//          (GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague
//          (GMT+01:00) Brussels, Copenhagen, Madrid, Paris
//          (GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb
//          (GMT+01:00) West Central Africa
Imports System.Collections.ObjectModel

Module TimeOffsets
    Public Sub Main()
        Dim thisTime As DateTimeOffset

        thisTime = New DateTimeOffset(#06/10/2007#, New TimeSpan(-7, 0, 0))
        ShowPossibleTimeZones(thisTime)

        thisTime = New DateTimeOffset(#03/10/2007#, New TimeSpan(-6, 0, 0))
        ShowPossibleTimeZones(thisTime)

        thisTime = New DateTimeOffset(#03/10/2007#, New TimeSpan(+1, 0, 0))
        ShowPossibleTimeZones(thisTime)
    End Sub

    Private Sub ShowPossibleTimeZones(offsetTime As DateTimeOffset)
        Dim offset As TimeSpan = offsetTime.Offset
        Dim timeZones As ReadOnlyCollection(Of TimeZoneInfo)

        Console.WriteLine("{0} could belong to the following time zones:", _
                          offsetTime.ToString())
        ' Get all time zones defined on local system
        timeZones = TimeZoneInfo.GetSystemTimeZones()
        ' Iterate time zones
        For Each timeZone As TimeZoneInfo In timeZones
            ' Compare offset with offset for that date in that time zone
            If timeZone.GetUtcOffset(offsetTime.DateTime).Equals(offset) Then
                Console.WriteLine("   {0}", timeZone.DisplayName)
            End If
        Next
        Console.WriteLine()
    End Sub
End Module
' This example displays the following output to the console:
'       6/10/2007 12:00:00 AM -07:00 could belong to the following time zones:
'          (GMT-07:00) Arizona
'          (GMT-08:00) Pacific Time (US & Canada)
'          (GMT-08:00) Tijuana, Baja California
'       
'       3/10/2007 12:00:00 AM -06:00 could belong to the following time zones:
'          (GMT-06:00) Central America
'          (GMT-06:00) Central Time (US & Canada)
'          (GMT-06:00) Guadalajara, Mexico City, Monterrey - New
'          (GMT-06:00) Guadalajara, Mexico City, Monterrey - Old
'          (GMT-06:00) Saskatchewan
'       
'       3/10/2007 12:00:00 AM +01:00 could belong to the following time zones:
'          (GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna
'          (GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague
'          (GMT+01:00) Brussels, Copenhagen, Madrid, Paris
'          (GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb
'          (GMT+01:00) West Central Africa

출력은 이 예제의 각 날짜 및 시간 값이 세 개 이상의 서로 다른 표준 시간대에 속할 수 있음을 보여 줍니다. 2007년 6월 10일의 DateTimeOffset 값은 날짜 및 시간 값이 서머타임을 나타내는 경우 그 UTC 오프셋이 원래 시간대의 기본 UTC 오프셋이나 표시 이름에 나와 있는 UTC 오프셋과 반드시 일치하지 않을 수 있음을 보여 줍니다. 단일 DateTimeOffset 값은 표준 시간대와 긴밀하게 결합되지 않으므로 일광 절약 시간제로의 표준 시간대 전환을 반영할 수 없습니다. 날짜 및 시간 산술 연산을 사용하여 DateTimeOffset 값을 조작할 때 문제가 될 수 있습니다. 시간대의 조정 규칙을 고려하여 날짜 및 시간 산술 연산을 수행하는 방법에 대한 논의는 날짜 및 시간에 대한 산술 연산 수행을 참조하세요.

DateTime 구조체

DateTime 값은 특정 날짜 및 시간을 정의합니다. 여기에는 해당 날짜와 시간이 속한 표준 시간대에 대한 제한된 정보를 제공하는 Kind 속성이 포함되어 있습니다. DateTimeKind 속성에서 반환된 Kind 값은 DateTime 값이 현지 시간(DateTimeKind.Local), UTC(협정 세계시)(DateTimeKind.Utc) 또는 지정되지 않은 시간(DateTimeKind.Unspecified)을 나타내는지 여부를 나타냅니다.

DateTime 구조는 다음 특성 중 하나 이상을 가진 애플리케이션에 적합합니다.

  • 추상 날짜 및 시간을 사용합니다.
  • 시간대 정보가 없는 날짜 및 시간을 처리합니다.
  • UTC 날짜 및 시간만 사용합니다.
  • 날짜와 시간 계산을 수행하지만, 일반적인 결과에 중점을 둡니다. 예를 들어 특정 날짜 및 시간에 6개월을 추가하는 추가 작업에서는 일광 절약 시간제에 맞게 결과를 조정할지 여부는 중요하지 않은 경우가 많습니다.

특정 DateTime 값이 UTC를 나타내지 않는 한 해당 날짜 및 시간 값은 종종 모호하거나 이식성이 제한됩니다. 예를 들어 DateTime 값이 현지 시간을 나타내는 경우 해당 현지 표준 시간대 내에서 이식 가능합니다(즉, 동일한 표준 시간대의 다른 시스템에서 값이 역직렬화되는 경우 해당 값은 여전히 단일 시점을 명확하게 식별함). 현지 표준 시간대 외부에서 해당 DateTime 값은 여러 해석을 가질 수 있습니다. 값의 Kind 속성이 DateTimeKind.Unspecified인 경우, 이식성이 더욱 떨어집니다. 이제 동일한 표준 시간대 내에서도 모호할 수 있으며, 심지어 처음 직렬화된 동일한 시스템에서도 모호할 수 있습니다. DateTime 값이 UTC를 나타내는 경우에만 해당 값은 값이 사용되는 시스템 또는 표준 시간대에 관계없이 단일 시점을 명확하게 식별합니다.

중요하다

DateTime 데이터를 저장하거나 공유할 때 UTC를 사용하고 DateTime 값의 Kind 속성을 DateTimeKind.Utc설정합니다.

DateOnly 구조체

DateOnly 구조체는 시간 없이 특정 날짜를 나타냅니다. 시간 구성 요소가 없으므로 하루의 시작부터 종료까지의 날짜를 나타냅니다. 이 구조는 생년월일, 기념일, 휴일 또는 비즈니스 관련 날짜와 같은 특정 날짜를 저장하는 데 적합합니다.

시간 구성 요소를 무시하고 DateTime를 사용할 수도 있지만, DateOnly대신 DateTime을 사용하는 데에는 몇 가지 이점이 있습니다.

  • DateTime 구조체가 표준 시간대로 오프셋된 경우 이전 또는 다음 날로 롤아웃될 수 있습니다. DateOnly 표준 시간대로 오프셋할 수 없으며 항상 설정된 날짜를 나타냅니다.
  • DateTime 구조체 직렬화에는 데이터의 의도를 모호하게 할 수 있는 시간 구성 요소가 포함됩니다. 또한 DateOnly 적은 데이터를 직렬화합니다.
  • 코드가 SQL Server와 같은 데이터베이스와 상호 작용하는 경우 전체 날짜는 일반적으로 시간을 포함하지 않는 date 데이터 형식으로 저장됩니다. DateOnly 데이터베이스 형식과 더 잘 일치합니다.

DateOnly대한 자세한 내용은 DateOnly 및 TimeOnly 구조체사용하는 방법을 참조하세요.

중요하다

DateOnly .NET Framework에 사용할 수 없습니다.

TimeSpan 구조체

TimeSpan 구조체는 시간 간격을 나타냅니다. 일반적인 두 가지 용도는 다음과 같습니다.

  • 두 날짜와 시간 값 사이의 시간 간격을 반영합니다. 예를 들어 한 DateTime 값을 다른 값에서 빼면 TimeSpan 값이 반환됩니다.
  • 경과된 시간 측정 예를 들어 Stopwatch.Elapsed 속성은 경과된 시간을 측정하기 시작하는 TimeSpan 메서드 중 하나에 대한 호출 이후 경과된 시간 간격을 반영하는 Stopwatch 값을 반환합니다.

TimeSpan 값은 해당 값이 특정 날짜에 대한 참조 없이 시간을 반영하는 경우 DateTime 값의 대체로 사용할 수도 있습니다. 이 사용은 날짜에 대한 참조 없이 시간을 나타내는 DateTime.TimeOfDay 값을 반환하는 DateTimeOffset.TimeOfDayTimeSpan 속성과 비슷합니다. 예를 들어 TimeSpan 구조체를 사용하여 매장의 일별 영업 또는 폐점 시간을 반영하거나 일반 이벤트가 발생하는 시간을 나타내는 데 사용할 수 있습니다.

다음 예제에서는 저장소 열기 및 닫는 시간에 대한 StoreInfo 개체와 저장소의 표준 시간대를 나타내는 TimeSpan 개체를 포함하는 TimeZoneInfo 구조를 정의합니다. 또한 구조에는 IsOpenNowIsOpenAt두 가지 메서드가 포함되어 있으며, 이는 로컬 표준 시간대에 있는 것으로 간주되는 사용자가 지정한 시간에 저장소가 열려 있는지 여부를 나타냅니다.

using System;

public struct StoreInfo
{
   public String store;
   public TimeZoneInfo tz;
   public TimeSpan open;
   public TimeSpan close;

   public bool IsOpenNow()
   {
      return IsOpenAt(DateTime.Now.TimeOfDay);
   }

   public bool IsOpenAt(TimeSpan time)
   {
      TimeZoneInfo local = TimeZoneInfo.Local;
      TimeSpan offset = TimeZoneInfo.Local.BaseUtcOffset;

      // Is the store in the same time zone?
      if (tz.Equals(local)) {
         return time >= open & time <= close;
      }
      else {
         TimeSpan delta = TimeSpan.Zero;
         TimeSpan storeDelta = TimeSpan.Zero;

         // Is it daylight saving time in either time zone?
         if (local.IsDaylightSavingTime(DateTime.Now.Date + time))
            delta = local.GetAdjustmentRules()[local.GetAdjustmentRules().Length - 1].DaylightDelta;

         if (tz.IsDaylightSavingTime(TimeZoneInfo.ConvertTime(DateTime.Now.Date + time, local, tz)))
            storeDelta = tz.GetAdjustmentRules()[tz.GetAdjustmentRules().Length - 1].DaylightDelta;

         TimeSpan comparisonTime = time + (offset - tz.BaseUtcOffset).Negate() + (delta - storeDelta).Negate();
         return comparisonTime >= open && comparisonTime <= close;
      }
   }
}
Public Structure StoreInfo
    Dim store As String
    Dim tz As TimeZoneInfo
    Dim open As TimeSpan
    Dim close As TimeSpan

    Public Function IsOpenNow() As Boolean
        Return IsOpenAt(Date.Now.TimeOfDay)
    End Function

    Public Function IsOpenAt(time As TimeSpan) As Boolean
        Dim local As TimeZoneInfo = TimeZoneInfo.Local
        Dim offset As TimeSpan = TimeZoneInfo.Local.BaseUtcOffset

        ' Is the store in the same time zone?
        If tz.Equals(local) Then
            Return time >= open AndAlso time <= close
        Else
            Dim delta As TimeSpan = TimeSpan.Zero
            Dim storeDelta As TimeSpan = TimeSpan.Zero

            ' Is it daylight saving time in either time zone?
            If local.IsDaylightSavingTime(Date.Now.Date + time) Then
                delta = local.GetAdjustmentRules(local.GetAdjustmentRules().Length - 1).DaylightDelta
            End If
            If tz.IsDaylightSavingTime(TimeZoneInfo.ConvertTime(Date.Now.Date + time, local, tz))
                storeDelta = tz.GetAdjustmentRules(tz.GetAdjustmentRules().Length - 1).DaylightDelta
            End If
            Dim comparisonTime As TimeSpan = time + (offset - tz.BaseUtcOffset).Negate() + (delta - storeDelta).Negate
            Return (comparisonTime >= open AndAlso comparisonTime <= close)
        End If
    End Function
End Structure

StoreInfo 구조체는 다음과 같은 클라이언트 코드에서 사용할 수 있습니다.

public class Example
{
   public static void Main()
   {
      // Instantiate a StoreInfo object.
      var store103 = new StoreInfo();
      store103.store = "Store #103";
      store103.tz = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
      // Store opens at 8:00.
      store103.open = new TimeSpan(8, 0, 0);
      // Store closes at 9:30.
      store103.close = new TimeSpan(21, 30, 0);

      Console.WriteLine("Store is open now at {0}: {1}",
                        DateTime.Now.TimeOfDay, store103.IsOpenNow());
      TimeSpan[] times = { new TimeSpan(8, 0, 0), new TimeSpan(21, 0, 0),
                           new TimeSpan(4, 59, 0), new TimeSpan(18, 31, 0) };
      foreach (var time in times)
         Console.WriteLine("Store is open at {0}: {1}",
                           time, store103.IsOpenAt(time));
   }
}
// The example displays the following output:
//       Store is open now at 15:29:01.6129911: True
//       Store is open at 08:00:00: True
//       Store is open at 21:00:00: True
//       Store is open at 04:59:00: False
//       Store is open at 18:31:00: True
Module Example
    Public Sub Main()
        ' Instantiate a StoreInfo object.
        Dim store103 As New StoreInfo()
        store103.store = "Store #103"
        store103.tz = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time")
        ' Store opens at 8:00.
        store103.open = new TimeSpan(8, 0, 0)
        ' Store closes at 9:30.
        store103.close = new TimeSpan(21, 30, 0)

        Console.WriteLine("Store is open now at {0}: {1}",
                          Date.Now.TimeOfDay, store103.IsOpenNow())
        Dim times() As TimeSpan = {New TimeSpan(8, 0, 0),
                                    New TimeSpan(21, 0, 0),
                                    New TimeSpan(4, 59, 0),
                                    New TimeSpan(18, 31, 0)}
        For Each time In times
            Console.WriteLine("Store is open at {0}: {1}",
                              time, store103.IsOpenAt(time))
        Next
    End Sub
End Module
' The example displays the following output:
'       Store is open now at 15:29:01.6129911: True
'       Store is open at 08:00:00: True
'       Store is open at 21:00:00: False
'       Store is open at 04:59:00: False
'       Store is open at 18:31:00: False

TimeOnly 구조체

TimeOnly 구조는 매일 알람 시계 또는 매일 점심을 먹는 시간과 같은 하루 중 시간 값을 나타냅니다. TimeOnly은 특정 시간인 00:00:00.0000000 - 23:59:59.9999999범위로 제한됩니다.

TimeOnly 형식이 도입되기 전에 프로그래머는 일반적으로 DateTime 형식 또는 TimeSpan 형식을 사용하여 특정 시간을 나타냅니다. 그러나 이러한 구조를 사용하여 날짜가 없는 시간을 시뮬레이션하면 몇 가지 문제가 발생할 수 있습니다. 이러한 문제는 TimeOnly가 해결합니다.

  • TimeSpan 스톱워치로 측정된 시간과 같은 경과된 시간을 나타냅니다. 상한 범위는 29,000년 이상이며 해당 값은 시간이 지나면 뒤로 이동함을 나타내기 위해 음수일 수 있습니다. TimeSpan이 음수일 경우, 하루 중 특정 시간을 나타내지 않습니다.
  • TimeSpan 하루 중 시간으로 사용되는 경우 24시간 외의 값으로 조작할 수 있는 위험이 있습니다. TimeOnly 이러한 위험이 없습니다. 예를 들어 직원의 근무 교대 근무가 18:00에 시작되고 8시간 동안 지속되는 경우 TimeOnly 구조에 8시간을 추가하면 2:00으로 롤오버됩니다.
  • 하루 중 시간에 DateTime 사용하려면 임의의 날짜를 시간과 연결한 다음 나중에 무시해야 합니다. DateTime.MinValue(0001-01-01)를 날짜로 선택하는 것이 일반적이지만 DateTime 값에서 시간을 빼면 OutOfRange 예외가 발생할 수 있습니다. TimeOnly는 시간이 24시간 동안 앞뒤로 조정될 수 있기 때문에 이 문제가 없습니다.
  • DateTime 구조체 직렬화에는 데이터의 의도를 모호하게 할 수 있는 날짜 구성 요소가 포함됩니다. 또한 TimeOnly 적은 데이터를 직렬화합니다.

TimeOnly대한 자세한 내용은 DateOnly 및 TimeOnly 구조체사용하는 방법을 참조하세요.

중요하다

TimeOnly .NET Framework에 사용할 수 없습니다.

TimeZoneInfo 클래스

TimeZoneInfo 클래스는 지구의 표준 시간대를 나타내며, 한 표준 시간대의 날짜와 시간을 다른 표준 시간대의 해당 날짜와 시간으로 변환할 수 있습니다. TimeZoneInfo 클래스를 사용하면 날짜 및 시간 값을 사용하여 단일 시점을 명확하게 식별할 수 있습니다. TimeZoneInfo 클래스도 확장할 수 있습니다. Windows 시스템에 대해 제공되고 레지스트리에 정의된 표준 시간대 정보에 따라 달라지지만 사용자 지정 표준 시간대 만들기를 지원합니다. 표준 시간대 정보의 직렬화 및 역직렬화도 지원합니다.

경우에 따라 TimeZoneInfo 클래스를 최대한 활용하려면 추가 개발 작업이 필요할 수 있습니다. 날짜 및 시간 값이 속한 표준 시간대와 긴밀하게 결합되지 않은 경우 추가 작업이 필요합니다. 애플리케이션에서 날짜 및 시간을 연결된 표준 시간대와 연결하는 메커니즘을 제공하지 않는 한 특정 날짜 및 시간 값이 해당 표준 시간대에서 분리되기 쉽습니다. 이 정보를 연결하는 한 가지 방법은 날짜 및 시간 값과 관련 표준 시간대 개체를 모두 포함하는 클래스 또는 구조를 정의하는 것입니다.

.NET에서 표준 시간대 지원을 활용하려면 해당 날짜 및 시간 개체가 인스턴스화될 때 날짜 및 시간 값이 속하는 표준 시간대를 알고 있어야 합니다. 표준 시간대는 특히 웹 또는 네트워크 앱에서 잘 알려지지 않는 경우가 많습니다.

참고