Quick Reference
한 시점을 저장·전송·비교할 때는 UTC DateTimeOffset, 날짜만 또는 시간만 의미가 있을 때는 DateOnly/TimeOnly, 반복 일정처럼 지역 시각 규칙이 필요할 때는 DateOnly·TimeOnly과 TimeZoneInfo를 함께 둡니다. DateTimeOffset의 offset은 한 시점을 식별하지만 time-zone ID나 일광 절약 시간 규칙까지 보관하지는 않습니다.
DateTimeOffset recordedAt = DateTimeOffset.UtcNow;
DateOnly birthday = new DateOnly(2026, 3, 25);
TimeOnly openingTime = new TimeOnly(9, 30);
// API/저장소: round-trip 가능한 UTC 시점
string payload = recordedAt.ToString("O", CultureInfo.InvariantCulture);
DateTimeOffset parsed = DateTimeOffset.ParseExact(
payload, "O", CultureInfo.InvariantCulture, DateTimeStyles.None);
// 화면 표시: 선택한 사용자의 time zone으로 변환한 뒤 형식화
TimeZoneInfo displayZone = TimeZoneInfo.Utc; // 실제 앱에서는 사용자 설정으로 선택
DateTimeOffset local = TimeZoneInfo.ConvertTime(parsed, displayZone);문법
DateTime 생성 방법
DateTime은 날짜와 시간을 함께 다루는 구조체입니다. 직접 값을 지정하려면 new DateTime(year, month, day) 을 사용하고, 현재 시각이 필요하면 DateTime.Now(로컬 시간) 또는 DateTime.UtcNow(UTC 시간)를 씁니다. DateTime.Today 는 시·분·초가 모두 0인 오늘 날짜를 반환합니다.
DateTime dt = new DateTime(2026, 3, 25); // 날짜만 지정
DateTime now = DateTime.Now; // 로컬 시간
DateTime utc = DateTime.UtcNow; // UTC 시간
DateTime today = DateTime.Today; // 오늘 00:00:00DateOnly / TimeOnly (C# 10+)
날짜와 시간을 분리해서 다뤄야 할 때 DateOnly 와 TimeOnly 를 사용합니다. 생년월일처럼 시간 정보가 의미 없는 값에는 DateOnly가, 알람 시각처럼 날짜와 무관한 값에는 TimeOnly가 적합합니다. 기존 DateTime에서 변환하거나 직접 생성할 수 있습니다.
DateOnly date = new DateOnly(2026, 3, 25);
TimeOnly time = new TimeOnly(9, 30, 0);
DateOnly fromDt = DateOnly.FromDateTime(DateTime.Now);
TimeOnly fromDt2 = TimeOnly.FromDateTime(DateTime.Now);TimeSpan — 시간 간격과 산술 연산
TimeSpan 은 두 DateTime 을 빼거나 TimeSpan.FromXxx() 팩토리 메서드로 만들 수 있는 시간 간격 타입입니다. DateTime에 더하거나 빼서 날짜 연산을 수행합니다.
TimeSpan oneHour = TimeSpan.FromHours(1);
TimeSpan twoWeeks = TimeSpan.FromDays(14);
DateTime deadline = DateTime.Now + twoWeeks;
TimeSpan elapsed = DateTime.Now - new DateTime(2026, 1, 1);
Console.WriteLine(elapsed.TotalDays); // 경과 일수 (소수점 포함)
Console.WriteLine(elapsed.Days); // 경과 일수 (정수)포맷 — ToString과 표준 서식 지정자
ToString() 에 서식 문자열을 전달해 원하는 형태로 출력합니다. 사용자 정의 서식(yyyy-MM-dd)과 표준 서식 지정자("D", "F" 등) 두 가지 방식이 있습니다.
DateTime dt = new DateTime(2026, 3, 25, 9, 30, 0);
dt.ToString("yyyy-MM-dd") // "2026-03-25"
dt.ToString("HH:mm:ss") // "09:30:00"
dt.ToString("yyyy-MM-dd HH:mm:ss") // "2026-03-25 09:30:00"
dt.ToString("D") // "2026년 3월 25일 수요일" (문화권에 따라 다름)
dt.ToString("F") // "2026년 3월 25일 수요일 오전 9:30:00"
dt.ToString("O") // ISO 8601 라운드트립 서식파싱 — Parse, TryParse, ParseExact
문자열을 DateTime으로 변환할 때 DateTime.Parse 는 형식이 맞지 않으면 예외를 던집니다. 입력이 신뢰할 수 없는 경우 DateTime.TryParse 로 안전하게 처리합니다. 형식이 고정된 경우 ParseExact 와 CultureInfo.InvariantCulture 를 조합해 문화권 독립적으로 파싱하는 것이 권장됩니다.
// 기본 파싱 — 실패 시 예외
DateTime dt = DateTime.Parse("2026-03-25");
// 안전한 파싱 — 실패 시 false 반환
if (DateTime.TryParse("2026-03-25", out DateTime result))
Console.WriteLine(result);
// 엄격한 형식 지정 파싱 (API 응답, 파일명 등)
DateTime exact = DateTime.ParseExact(
"25/03/2026",
"dd/MM/yyyy",
CultureInfo.InvariantCulture);DateTimeOffset — offset이 있는 한 시점
DateTimeOffset 은 UTC와의 offset을 함께 저장하므로 한 시점을 모호하지 않게 식별합니다. 서로 다른 시스템의 이벤트 시각을 비교하거나 DB/API에 저장할 때 적합합니다. 다만 +09:00은 서울 같은 time-zone ID가 아니며, DST 규칙·향후 offset 변경·반복 일정의 지역 의미를 보존하지 않습니다.
DateTimeOffset now = DateTimeOffset.Now; // 로컬 + 오프셋
DateTimeOffset utcNow = DateTimeOffset.UtcNow; // UTC + 00:00
// 오프셋 직접 지정
DateTimeOffset seoul = new DateTimeOffset(
2026, 3, 25, 9, 0, 0,
TimeSpan.FromHours(9)); // +09:00
Console.WriteLine(seoul.Offset); // 09:00:00
Console.WriteLine(seoul.UtcDateTime); // UTC 기준 DateTimeDateTime.Kind와 지역 일정
DateTime에는 Kind 속성이 있어 Unspecified, Local, Utc 세 가지 값을 가집니다. DateTime.Now는 Local, DateTime.UtcNow는 Utc이지만, new DateTime(...)으로 직접 만들면 Unspecified가 됩니다. SpecifyKind는 시각을 변환하지 않고 label만 바꾸므로, 실제 지역 시각을 UTC로 바꾸는 API처럼 사용하면 안 됩니다.
DateTime local = DateTime.Now; // Kind = Local
DateTime utc = DateTime.UtcNow; // Kind = Utc
DateTime raw = new DateTime(2026, 3, 25); // Kind = Unspecified
Console.WriteLine(local.Kind); // Local
Console.WriteLine(raw.Kind); // Unspecified ← 주의매주 "사용자 지역 오전 9시"처럼 반복되는 일정은 offset 하나로 저장하지 않습니다. 날짜·시각·time-zone ID를 분리하고, 해당 지역에서 DST로 생기지 않는 시각 또는 두 번 생기는 시각을 확인한 뒤 서비스 정책으로 처리합니다.
DateOnly date = new DateOnly(2026, 3, 8);
TimeOnly time = new TimeOnly(2, 30);
DateTime localClock = date.ToDateTime(time, DateTimeKind.Unspecified);
// zoneId는 시스템/플랫폼에서 유효한 ID여야 한다.
TimeZoneInfo zone = TimeZoneInfo.FindSystemTimeZoneById(zoneId);
if (zone.IsInvalidTime(localClock))
throw new ArgumentException("DST 전환으로 존재하지 않는 지역 시각입니다.");
if (zone.IsAmbiguousTime(localClock))
throw new ArgumentException("DST 전환으로 offset 선택 정책이 필요합니다.");
DateTimeOffset occurrence = new DateTimeOffset(
localClock, zone.GetUtcOffset(localClock));시간 타입과 포맷
| 타입 | 용도 | 주요 생성 방법 |
|---|---|---|
DateTime | 날짜 + 시간과 Kind label | DateTime.Now, DateTime.UtcNow, new DateTime(y,m,d) |
DateOnly | 날짜만 (C# 10+) | new DateOnly(y,m,d), DateOnly.FromDateTime() |
TimeOnly | 시간만 (C# 10+) | new TimeOnly(h,m,s), TimeOnly.FromDateTime() |
TimeSpan | 시간 간격 | TimeSpan.FromDays(), dt1 - dt2 |
DateTimeOffset | 날짜 + 시간 + UTC 오프셋 | DateTimeOffset.Now, DateTimeOffset.UtcNow |
| 포맷 지정자 | 출력 예시 |
|---|---|
"yyyy-MM-dd" | 2026-03-25 |
"HH:mm:ss" | 09:30:00 |
"D" | 2026년 3월 25일 수요일 |
"F" | 2026년 3월 25일 수요일 오전 9:30:00 |
"O" | 2026-03-25T09:30:00.0000000 (ISO 8601) |
주의할 점
DateTime.Now는 로컬 시간을 반환하므로 서버 코드에서 혼용하면 위험합니다. 서버 간 시간 비교나 DB 저장에는 UTC DateTimeOffset.UtcNow 또는 UTC DateTime 한 정책을 정해 사용하세요. 날짜·시간 산술이나 경과 시간 측정에 wall-clock 값을 쓰면 시간대/DST/시스템 시계 변경이 영향을 줄 수 있으므로, 성능 경과 시간은 Stopwatch를 사용합니다.
타임존 정보가 중요한 애플리케이션에서 이벤트 시점은 DateTimeOffset, 반복 지역 일정은 TimeZoneInfo와 지역 시각을 함께 사용합니다. offset만으로 zone ID를 복원할 수 없고, Kind가 Unspecified인 DateTime은 어떤 zone의 시각인지 호출 경계에서 명시해야 합니다.
참고 링크
4 sources