본문 바로가기
C#

[C#] DateTime & DateTimeOffset

by DANEW 2023. 7. 6.

DateTime & DateTimeOffset (System.DateTime & System.DateTimeOffset)

 

DateTime Struct (System)

Represents an instant in time, typically expressed as a date and time of day.

learn.microsoft.com

public struct DateTime : IComparable, IComparable<DateTime>, IConvertible, IEquatable<DateTime>, IFormattable, System.Runtime.Serialization.ISerializable
struct DateTimeOffset : IComparable, IComparable<DateTimeOffset>, IEquatable<DateTimeOffset>, IFormattable, System.Runtime.Serialization.IDeserializationCallback, System.Runtime.Serialization.ISerializable
 
DateTime과 DateTimeOffset (.NET 3.5)
- 날짜와 시간(선택적)을 나타내는 불변이 구조체
- 해상도: 100ns
- 범위: 0001년 ~ 9999년
반응형
DateTime과 DateTimeOffset의 선택
- DateTime의 플래그: 현재 컴퓨터의 지역 시간 / UTC (협정세계시) / 미 명시 
- DateTimeOffset의 오프셋: UTC 시간으로부터의 오프셋
 
- DateTime은 상등 비교시 3상태 플래그를 무시하고 두 값의 연, 월, 일, 시, 분등을 비교하여 해당 값들이 같으면 둘이 같다고 간주한다
- DateTimeOffset은 두 값이 시간상의 같은 지점을 가리킬 때에만 같다고 간주한다
- 여러가지 시간대를 고려하거나 일광절약시간제를 실행하는 경우 이 구분이 중요해진다.
 
- 전 세계에서 각자의 로컬 타임 기준(예로 들자면 각자의 새벽 3시)으로 같은 시간에 무언가를 해야 할 경우 DateTime
- 전 세계에서 동시에 같은 시간(UTC+0 기준 새벽 3시)에 무언가를 해야 할 경우 DateTimeOffset
 
 
DateTime 객체 생성
- 내부 저장 데이터: 62비트의 틱 수, 2비트 열거형 값(DateTimeKind)
- 생성자(틱 수나 날짜 정보, 시간, DateTimeKind, Calender)
- 생성자에서 생략시 기본 값
 * 시간: 00:00:00:000
 * DateTimeKind: Unspecified (Kind로 확인 가능)
 * Calender: GregorianCalendar
- FromFileTime, FromFileTimeUtc, FromOADate, Parse, ParseExact
- DateTimeOffset을 통한 생성(UtcDateTime, LocalDateTime, DateTime)
 
 
DateTimeOffset 객체 생성
- 내부 저장 데이터: DateTime(UTC) + 16비트 정수(ITC Offset)
- 생성자(틱 수, 날짜+시간 정보, Calender, TimeSpan, DateTime)
- 생성자에서 생략시 기본 값
 * 밀리초: 0 (시간을 째로 생략하는 것은 불가능하다)
 * Calender: GregorianCalendar
- DateTime을 통해서 DateTimeOffset을 생성 가능 - DateTimeOffset op_Implicit (DateTime)
- TimeSpan 생략시
 * DateTimeKind가 Utc이면 오프셋은 0
 * DateTimeKind가 Local / Unspecified면 현재 지역 시간대의 오프셋
- FromFileTime, Parse, ParseExact
 
 
현재 DateTime/DateTimeOffset
- Now, UtcNow
- DateTime: Today
 
 
날짜와 시간 다루기
- Year, Month, Day, DayOfWeek, DayOfYear, Hour, Minute, Second, Millisecond, Ticks, TimeOfDay
- DateTimeOffset: Offest
- AddYears, AddMonths, AddDays, AddHours, AddMinutes, AddSeconds, AddMilliseconds, AddTicks
- Add, op_Addition (DateTime, TimeSpan), op_Addition (DateTimeOffset, TimeSpan);
- Subtract, op_Subtraction (DateTime, DateTime), op_Subtraction (DateTimeOffset, DateTimeOffset)
 
 
파싱과 서식화
- ToString
- DateTime: ToShortDateString, ToLongDateString
- Parse, TryParse, ParseExact, TryParseExact
 
 
널 값을 가진 DateTime과 DateTimeOffset
- 구조체이므로 널 값을 가질 수 없기에 우회책을 사용
1. Nullable (DateTime? / DateTimeOffset?)
2. DateTime.MinValue나 DateTimeOffset.MinValue를 널처럼 사용
- ToUniversalTime이나 ToLocalTime에 의해 MinValue였던 인스턴스가 다른 값으로 변할 수 있으므로 가급적 Nullable을 사용하여 해결
 
 
DateTime과 시간대
- 두 DateTime을 비교할 때는 틱수만 비교, DaTeTimeKind는 무시한다.
- 시간대의 변경과 값의 변화
 1. Local과 Unspecified에 대해 ToUniversalTime (Local -> UTC)

 

 2. UTC에 Unspecified에 대해 ToLocalTime (UTC -> Local)
- SpecifyKind를 사용하면 주어진 DateTime은 그대로 두고 Kind만 다른 새 DateTime을 생성 가능
 
 
DateTimeOffset과 시간대
- 두 DateTimeOffset을 비교할 때는 DateTime(UTC)만 비교하고, Offset은 무시한다.
- ToUniversalTime, ToLocalTime은 DateTime과는 달리 offset을 변화시키지만 DateTime(UTC)에는 영향을 끼치지 않는다.
- EqualsExact - 오프셋까지 비교
 
 
 

// DateTime and DateTimeOffset (.NET 3.5)
// 날짜와 시간을 나타내는 불변이 구조체
// 해상도: 100ns
// 범위: 0001년 ~ 9999년

// DateTime 객체 생성

// 내부 저장 데이터
// 1. 0001-01-01에서부터 흐른 틱 수를 뜻하는 62비트 수치
// 2. DateTimeKind를 나타내는 2비트 열거형 값
DateTime[] dt =
{
    // 기본값
    // 시간: 00:00:00:000
    // DateTimeKind: Unspecified
    // 캘린더: GregorianCalendar

    // 틱 (100ns)
    new DateTime(625000000000000000), 

    // 년, 월, 일
    new DateTime(2017, 6, 26),                 
    
    // 년, 월, 일, 시, 분, 초, 밀리초
    new DateTime(2017, 6, 26, 13, 26, 30, 100),

    // 년, 월, 일, 시, 분, 초, 밀리초, UTC 시간대
    new DateTime(2017, 6, 26, 13, 26, 30, 100, DateTimeKind.Utc),

    // 히브리력 5767년 1월 1일
    new DateTime(5767, 1, 1, new System.Globalization.HebrewCalendar()),

    // 단기 4350년 2월 2일 13시 26분 30초 100밀리초, UTC
    // 밀리초까지 기입하면 Calendar와 DateTimeKind를 동시에 입력 가능
    // 나머지 생성자는 Calendar를 넣거나 DateTimeK
    new DateTime(4350, 2, 2, 13, 26, 30, 100, new System.Globalization.KoreanCalendar(), DateTimeKind.Utc),
};

// 내용 출력
foreach (var item in dt)
{
    Console.WriteLine(item);
}
Console.WriteLine();


// DateTime이 가진 DateTimeKind 값
Console.WriteLine(dt[0].Kind);
Console.WriteLine();


// DateTime <-> Windows 파일 시간(long)
long ft = dt[2].ToFileTime();
long ftUtc = dt[2].ToFileTimeUtc();

DateTime cdt = DateTime.FromFileTime(ft);
DateTime cdtUtc = DateTime.FromFileTimeUtc(ftUtc);

Console.WriteLine(ft);
Console.WriteLine(ftUtc);
Console.WriteLine(cdt);
Console.WriteLine(cdtUtc);
Console.WriteLine();


// DateTime <-> OLE 자동화/시간(double)
double olet = dt[2].ToOADate();

DateTime cdt2 = DateTime.FromOADate(olet);

Console.WriteLine(olet);
Console.WriteLine(cdt2);
Console.WriteLine();


// DateTime <-> String
string dtString = dt[2].ToString();

DateTime cdt3 = DateTime.Parse(dtString);

Console.WriteLine(dtString);
Console.WriteLine(cdt3);
Console.WriteLine();



// DateTimeOffset 객체 생성

// 내부 저장 데이터
// 1. DateTime (UTC)
// 2. 16비트 정수: 분 단위 UTC Offset
DateTimeOffset[] dto =
{
    // 년, 월, 일, 시간을 생략하는 생성자는 없음
    // TimeSpan은 최소 분 단위로 입력해줘야 함
    
    // 틱 (100ns)
    new DateTimeOffset(625000000000000000, TimeSpan.FromHours(0)), 
    
    // 년, 월, 일, 시, 분, 초, 오프셋
    new DateTimeOffset(2017, 6, 26, 13, 26, 30, TimeSpan.FromHours(5)),

    // 년, 월, 일, 시, 분, 초, 밀리초, 오프셋
    new DateTimeOffset(2017, 6, 26, 13, 26, 30, 100, TimeSpan.FromHours(0)),
    
    // 년, 월, 일, 시, 분, 초, 밀리초, 캘린더, 오프셋
    new DateTimeOffset(4350, 6, 26, 13, 26, 30, 100, new System.Globalization.KoreanCalendar(), TimeSpan.FromHours(5)),
    
    // DateTime에서 복사해오기
    // offset은 생략 가능. 기본 값은 -
    // 1. DateTimeKind가 Utc면 offset은 0
    // 2. DateTimeKind가 Local이나 Unspecified면 현재 지역 시간대의 오프셋
    
    // 오프셋을 생략
    new DateTimeOffset(dt[2]),

    // 오프셋을 지정
    new DateTimeOffset(dt[2], TimeSpan.FromHours(5))
};

// DateTime에서 이렇게 복사해올 수도 있다
// Date​Time​Offset.​Implicit Operator            
DateTimeOffset dto2 = dt[2];

// 내용 출력
foreach (var item in dto)
{
    Console.WriteLine(item);
}
Console.WriteLine();

// Parse, ParseExact, FromFileTime 있음
DateTimeOffset dtoParse = DateTimeOffset.Parse("2017-06-26 13:26:30 +01:00");
//DateTimeOffset dtoParsed2 = DateTimeOffset.ParseExact();
DateTimeOffset dtoFromFileTime = DateTimeOffset.FromFileTime(ft);

Console.WriteLine(dtoParse);
Console.WriteLine(dtoFromFileTime);
Console.WriteLine();



// 현재 DateTime/DateTimeOffset
// 정밀도: OS에 따라 다름. 대체로 10~20ms

// Now - 현재 날짜 및 시간
Console.WriteLine(DateTime.Now);
Console.WriteLine(DateTimeOffset.Now);
Console.WriteLine();

// Today - 현재 날짜만, DateTime에만 있음
Console.WriteLine(DateTime.Today);
Console.WriteLine();

// UtcNow - UTC 기준 현재 날짜 및 시간
Console.WriteLine(DateTime.UtcNow);
Console.WriteLine(DateTimeOffset.UtcNow);
Console.WriteLine();



// 날짜와 시간 다루기

// 값 받아오기
Console.WriteLine(DateTime.Now.Year);          // 년
Console.WriteLine(DateTime.Now.Month);         // 월
Console.WriteLine(DateTime.Now.Day);           // 일
Console.WriteLine(DateTime.Now.DayOfWeek);     // 요일
Console.WriteLine(DateTime.Now.DayOfYear);     // 주차(년에서)

Console.WriteLine(DateTime.Now.Hour);          // 시간
Console.WriteLine(DateTime.Now.Minute);        // 분
Console.WriteLine(DateTime.Now.Second);        // 초
Console.WriteLine(DateTime.Now.Millisecond);   // 밀리초
Console.WriteLine(DateTime.Now.Ticks);         // 틱
Console.WriteLine(DateTime.Now.TimeOfDay);     // TimeSpan (자정으로부터 지난 시간)

Console.WriteLine(DateTimeOffset.Now.Offset);  // TimeSpan, 오프셋 (DateTimeOffset Only)
Console.WriteLine();

// 값 더하기(음수를 더한다면 빼기)
Console.WriteLine(dt[2]);
dt[2] = dt[2].AddYears(1);          // 년
dt[2] = dt[2].AddMonths(-2);        // 월
dt[2] = dt[2].AddDays(3);           // 일
dt[2] = dt[2].AddHours(-4);         // 시간
dt[2] = dt[2].AddMinutes(5);        // 분
dt[2] = dt[2].AddSeconds(-6);       // 초
dt[2] = dt[2].AddMilliseconds(7);   // 밀리초
dt[2] = dt[2].AddTicks(-8);         // 틱
Console.WriteLine(dt[2]);

TimeSpan ts = TimeSpan.FromMinutes(90);
dt[2] = dt[2].Add(ts);              // Timespan
dt[2] = dt[2] + ts;                 // Timespan (위와 동일)
Console.WriteLine(dt[2]);
Console.WriteLine();


// 더하기는 안 되고 마이너스는 됨
// 두 DateTime(DateTimeOffset)의 차는 TimeSpan
ts = dt[2] - dt[3];
Console.WriteLine(ts);
Console.WriteLine();



// 파싱과 서식화
// ToString
Console.WriteLine(dt[2]);
Console.WriteLine(dto[2]);
Console.WriteLine();

// 서식화가 다른 ToString들 (DateTime Only)
Console.WriteLine(dt[2].ToShortDateString());
Console.WriteLine(dt[2].ToLongDateString());
Console.WriteLine();

// Parse, TryParse, ParseExact, TryParseExact
// ToString의 반대, String -> DateTime(Offset)



// 널 값을 가진 DateTime과 DateTimeOffset
DateTime? dtn = null;
DateTimeOffset? dton = null;

if (dtn == null)
{
    Console.WriteLine("dtn is null");
}

if (dton == null)
{
    Console.WriteLine("dton is null");
}
Console.WriteLine();



// DateTime과 시간대

// 두 DateTime을 비교할 때는 틱 수만 비교하고, DateTimeKind는 무시한다.
// 따라서 아래 두 시간대를 비교하면 True로 표시된다.
DateTime dtLocal = new DateTime(2015, 1, 1, 10, 20, 30, DateTimeKind.Local);
DateTime dtUtc = new DateTime(2015, 1, 1, 10, 20, 30, DateTimeKind.Utc);
DateTime dtUnspecified = new DateTime(2015, 1, 1, 10, 20, 30);
Console.WriteLine(dtLocal);
Console.WriteLine(dtUtc);
Console.WriteLine(dtLocal == dtUtc);
Console.WriteLine();

// Local과 Unspecified에 대해 ToUniversalTime (Local -> UTC),
// UTC에 Unspecified에 대해 ToLocalTime (UTC -> Local),
// 위 행위는 값을 변화시킨다.
Console.WriteLine(dtLocal.ToUniversalTime());
Console.WriteLine(dtUnspecified.ToUniversalTime());
Console.WriteLine(dtUtc.ToLocalTime());
Console.WriteLine(dtUnspecified.ToLocalTime());
Console.WriteLine();

// SpecifyKind를 사용하면 주어진 DateTime은 그대로 두고
// Kind만 다른 새 DateTime을 생성 가능            
Console.WriteLine(DateTime.SpecifyKind(dtLocal, DateTimeKind.Utc));
Console.WriteLine();



// DateTimeOffset과 시간대

// 두 DateTimeOffset을 비교할 때는 DateTime(UTC)만 비교하고, Offset은 무시한다.
// DateTimeOffset은 생성 당시에 Offset에 의해 UTC를 기준으로 변동되기 때문에
// 아래 두 시간대를 비교하면 False로 표시된다.
DateTimeOffset dtoLocal = new DateTimeOffset(2015, 1, 1, 10, 20, 30, TimeSpan.FromHours(9));
DateTimeOffset dtoUtc = new DateTimeOffset(2015, 1, 1, 10, 20, 30, TimeSpan.FromHours(0));
Console.WriteLine(dtoLocal);
Console.WriteLine(dtoUtc);
Console.WriteLine(dtoLocal == dtoUtc);
Console.WriteLine();

// ToUniversalTime, ToLocalTime은 DateTime과는 달리
// offset을 변화시키지만 DateTime(UTC)에는 영향을 끼치지 않는다.
// 아래 두 시간대를 비교하면 True로 표시된다.
DateTimeOffset dtoLocalNow = DateTimeOffset.Now;
DateTimeOffset dtoUtcNow = dtoLocalNow.ToUniversalTime();

Console.WriteLine(dtoLocalNow);
Console.WriteLine(dtoUtcNow);
Console.WriteLine(dtoLocalNow == dtoUtcNow);
Console.WriteLine();

// EqualsExact - 오프셋까지 고려한 비교 
Console.WriteLine(dtoLocalNow.EqualsExact(dtoUtcNow));
Console.WriteLine();


// 일광절약시간제와 DateTime
Console.WriteLine(DateTime.Now.IsDaylightSavingTime());
Console.WriteLine(DateTime.UtcNow.IsDaylightSavingTime());
Console.WriteLine(DateTimeOffset.Now.LocalDateTime.IsDaylightSavingTime());

반응형

'C#' 카테고리의 다른 글

[C#] 표준 서식 문자열과 파싱 플래그  (1) 2023.08.13
[C#] Time​Zone​Info.​Adjustment​Rule & Time​Zone​Info.​Transition​Time  (1) 2023.08.12
[C#] TimeZoneInfo  (1) 2023.08.11
[C#] TimeZone  (1) 2023.08.10
[C#] TimeZoneInfo  (2) 2023.07.05
[C#] TimeZone  (1) 2023.07.04
[C#] TimeSpan  (1) 2023.07.01
[C#] Encoding  (1) 2023.06.30