포멧은 ISO 8601 기본 규칙 을 따른다
ISO 8601은 국제 표준(YYYY-MM-DDThh:mm:ss±hh:mm)으로, 시간대(오프셋), 밀리초, **UTC 여부(Z)**까지 표현할 수 있습니다.
1. ISO 8601 기본 규칙
구성 요소 | 예시 | 설명 |
---|---|---|
연도(YYYY) | 2025 | 4자리 연도 |
월(MM) | 09 | 01~12 |
일(DD) | 04 | 01~31 |
구분자 | T | 날짜와 시간 사이에 T 필수 |
시(HH) | 21 | 00~23 |
분(mm) | 30 | 00~59 |
초(ss) | 45 | 00~59 |
밀리초(.fff) | .123 | 선택적, 최대 7자리(틱까지) |
시간대 | Z , +09:00 | Z 는 UTC, +09:00 은 한국 |
날짜 + 시간 (로컬 시간, 오프셋 없음)
형식 | 예시 | 설명 |
---|---|---|
YYYY-MM-DDTHH:mm | 2025-09-04T21:30 | ISO 기본형 |
YYYY-MM-DDTHH:mm:ss | 2025-09-04T21:30:15 | 초까지 포함 |
YYYY-MM-DDTHH:mm:ss.fff | 2025-09-04T21:30:15.123 | 밀리초 포함 |
YYYY-MM-DDTHH:mm:ss.fffffff | 2025-09-04T21:30:15.1234567 | 틱(100ns)까지 |
1 2 3 |
DateTime dt1 = DateTime.Parse("2025-09-04T21:30:15.123"); |
UTC 시간(Zulu 시간)
형식 | 예시 | 설명 |
---|---|---|
YYYY-MM-DDTHH:mmZ | 2025-09-04T12:30Z | Z → UTC |
YYYY-MM-DDTHH:mm:ssZ | 2025-09-04T12:30:15Z | 초 포함 |
YYYY-MM-DDTHH:mm:ss.fffZ | 2025-09-04T12:30:15.123Z | 밀리초 포함 |
1 2 3 4 |
DateTimeOffset dto1 = DateTimeOffset.Parse("2025-09-04T12:30:15Z"); Console.WriteLine(dto1.UtcDateTime); // UTC 시각 |
오프셋(시간대) 포함
형식 | 예시 | 설명 |
---|---|---|
±hh:mm | +09:00 | 한국 |
±hh:mm | -07:00 | LA(서머타임) |
±hhmm | +0900 | 구버전 ISO 8601 |
±hh | +09 | 드물게 사용 |
예시 | 의미 |
---|---|
2025-09-04T21:30+09:00 | 한국시간 21:30 |
2025-09-04T14:30+02:00 | 파리시간 14:30 |
2025-09-04T05:30-07:00 | LA시간 05:30 |
1 2 3 4 |
DateTimeOffset dto2 = DateTimeOffset.Parse("2025-09-04T14:30:00+02:00"); Console.WriteLine(dto2.UtcDateTime); // 한국 시간과 변환 가능 |
밀리초 + 오프셋
형식 | 예시 | 설명 |
---|---|---|
YYYY-MM-DDTHH:mm:ss.fff±hh:mm | 2025-09-04T21:30:15.123+09:00 | 한국, 밀리초 포함 |
YYYY-MM-DDTHH:mm:ss.fffZ | 2025-09-04T12:30:15.123Z | UTC, 밀리초 포함 |
CultureInfo.InvariantCulture
문화권(로캘)에 영향을 받지 않는, 변하지 않는 중립 문화권.
날짜/숫자 “기계가 읽는 포맷”을 파싱/서식화할 때(로그, 파일, 프로토콜, DB 키 등). 사용자의 PC가 한국/미국 등 어떤 문화권이어도 동일하게 동작합니다.
1 2 3 4 5 6 7 8 9 10 11 |
using System; using System.Globalization; var s = "2025-09-04 13:45:00"; var dt = DateTime.ParseExact(s, "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture); // 문화권과 무관하게 정확히 파싱됨 string iso = dt.ToString("yyyy-MM-ddTHH:mm:ss", CultureInfo.InvariantCulture); // 항상 같은 포맷으로 출력 |
DateTimeStyles.RoundtripKind
문자열 → DateTime
파싱 시 Kind
(Utc/Local/Unspecified)를 최대한 “원본 그대로” 보존하도록 지시하는 옵션.ToString("o")
(ISO 8601 Round-trip)로 직렬화한 뒤 다시 Parse
로 복원할 때, 원본의 Kind를 유지하고 싶을 때.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
using System; using System.Globalization; // 1) Utc 예시 var utc = new DateTime(2025, 9, 4, 12, 0, 0, DateTimeKind.Utc); string sUtc = utc.ToString("o", CultureInfo.InvariantCulture); // "...Z" 붙음 DateTime parsedUtc = DateTime.Parse(sUtc, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind); // parsedUtc.Kind == DateTimeKind.Utc // 2) Unspecified 예시 var unspec = new DateTime(2025, 9, 4, 12, 0, 0, DateTimeKind.Unspecified); string sUnspec = unspec.ToString("o", CultureInfo.InvariantCulture); // 오프셋/‘Z’ 없음 DateTime parsedUnspec = DateTime.Parse(sUnspec, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind); // parsedUnspec.Kind == DateTimeKind.Unspecified // 포인트: RoundtripKind를 쓰면 직렬화-역직렬화에서 Kind 보존이 잘 됩니다. // (DateTime은 특정 오프셋을 저장하지 못하므로, 오프셋을 다뤄야 하면 DateTimeOffset을 권장) |
Tip: 실제 시간대/오프셋을 다뤄야 할 때는 DateTimeOffset
+ TimeZoneInfo
사용이 정석입니다. DateTime
은 Kind만 있고 구체 오프셋을 담지 못합니다.
TimeSpan.FromHours
시간”을 나타내는 TimeSpan
을 시간 단위(소수 포함)로 생성.
오프셋 지정, 시간 더하기/빼기, 타임아웃 등.
1 2 3 4 5 6 7 8 9 |
using System; TimeSpan fiveAndHalf = TimeSpan.FromHours(5.5); // 05:30:00 TimeSpan minusSeven = TimeSpan.FromHours(-7); // -07:00:00 var t1 = DateTime.UtcNow + fiveAndHalf; // 5시간 30분 후 var t2 = DateTime.UtcNow + minusSeven; // 7시간 전 |
DateTimeOffset.ToOffset(TimeSpan)
같은 “순간(UTC 시각)”을 유지한 채, 표현 오프셋만 바꿔 새 DateTimeOffset
을 반환. (로컬 시계시각은 자동으로 재계산됨)
동일한 UTC 순간을 다른 시간대/오프셋 기준의 “현지시각”으로 보여줄 때.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
sing System; var seoul = new DateTimeOffset(2025, 9, 4, 10, 30, 00, TimeSpan.FromHours(9)); // 2025-09-04 10:30 +09:00 var utc = seoul.UtcDateTime; // 2025-09-04 01:30 (UTC) // 단순히 "오프셋을 -07:00로 바꿔서" 같은 순간을 LA(PDT 가정) 현지시각으로 표시 var laByFixedOffset = seoul.ToOffset(TimeSpan.FromHours(-7)); // 결과: 2025-09-03 18:30 -07:00 (UTC는 동일: 01:30) Console.WriteLine(seoul); // 2025-09-04 10:30:00 +09:00 Console.WriteLine(laByFixedOffset); // 2025-09-03 18:30:00 -07:00 |
중요: ToOffset
은 “고정된 오프셋”을 적용할 뿐 “어느 시간대인지” 계산해 주지 않습니다. 서머타임(DST) 같은 규칙 반영은 **TimeZoneInfo
**로 먼저 해당 시점의 올바른 오프셋을 구한 뒤 그 오프셋을 ToOffset
에 넣어야 합니다.
1 2 3 4 5 6 7 8 9 |
using System; var seoul = new DateTimeOffset(2025, 9, 4, 10, 30, 00, TimeSpan.FromHours(9)); var pst = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time"); // Windows ID TimeSpan laOffsetAtThatMoment = pst.GetUtcOffset(seoul.UtcDateTime); var la = seoul.ToOffset(laOffsetAtThatMoment); // DST 반영된 정확한 오프셋 적용 |
DateTimeOffset.Offset (속성)
해당 DateTimeOffset
이 갖는 오프셋(TimeSpan) 값을 돌려줍니다.
현재 값이 어느 오프셋인지 확인하거나, 다른 값에 재사용할 때.
1 2 3 4 5 6 7 8 9 10 |
using System; var seoul = new DateTimeOffset(2025, 9, 4, 10, 30, 00, TimeSpan.FromHours(9)); TimeSpan off = seoul.Offset; // +09:00 Console.WriteLine(off); // 09:00:00 // 이 오프셋을 그대로 다른 시각에 적용 var other = new DateTimeOffset(2025, 10, 1, 8, 0, 0, off); // 2025-10-01 08:00 +09:00 |
실전 팁 요약
- 기계 파싱/출력:
CultureInfo.InvariantCulture
+ 명시적 포맷("o"
/"yyyy-MM-ddTHH:mm:ss"
) - DateTime 직렬화 왕복:
"o"
+DateTimeStyles.RoundtripKind
- 오프셋 계산/적용:
TimeSpan.FromHours(...)
- 다른 시간대 표시:
TimeZoneInfo.GetUtcOffset(특정시각)
→DateTimeOffset.ToOffset(그 오프셋)
- 현재 오프셋 확인:
dateTimeOffset.Offset