프로그램 하나로 보는 기본
time.Now()는 현재 지역 시간을 반환하고, time.Sleep은 현재 고루틴을 멈추고, time.Since는 경과 시간을 재고, time.Date는 특정 시각을 만듭니다. 이 페이지의 나머지는 각 부분을 차례로 다룹니다. 예제는 어디서 실행하든 출력이 같도록 고정된 날짜와 UTC를 씁니다.
Sleep과 Duration
time.Duration은 나노초를 세는 int64입니다. 패키지는 읽기 좋은 값을 만드는 상수를 정의합니다:
| 상수 | 값 |
|---|---|
time.Nanosecond | 1 |
time.Microsecond | 1000 ns |
time.Millisecond | 1000 µs |
time.Second | 1000 ms |
time.Minute | 60 s |
time.Hour | 60 min |
time.Day는 없습니다. 서머타임으로 시간대가 바뀔 때 하루가 항상 24시간은 아니기 때문입니다. 달력의 날짜에는 AddDate를 쓰세요(아래 참고).
Duration이 정수라서 생기는 흔한 버그가 두 가지 있습니다:
time.Sleep(5)는 5나노초 동안 잠듭니다. 항상 단위를 곱하세요:time.Sleep(5 * time.Second).n이int변수라면time.Sleep(n * time.Second)는 컴파일되지 않습니다. 변환하세요:time.Duration(n) * time.Second.5같은 상수는 타입 없는 상수가 Duration 타입에 맞춰지므로 변환 없이 동작합니다.
ParseDuration은 ns, us(또는 µs), ms, s, m, h를 받으며 "2h45m"이나 "1.5s"처럼 조합할 수 있습니다. 일 단위는 없습니다.
형식화: 2006-01-02 기준 레이아웃
Go는 %Y-%m-%d나 yyyy-MM-dd를 쓰지 않습니다. 레이아웃은 기준 시간
Mon Jan 2 15:04:05 MST 2006
을 원하는 출력 모양대로 쓴 것입니다. 값은 각각 고유하도록 정해져 있습니다. 월 1, 일 2, 시 3(또는 15), 분 4, 초 5, 연도 6(2006), 시간대 오프셋 -7(-0700). 01/02 03:04:05PM '06 -0700으로 읽으면 됩니다.
가장 많이 쓰게 될 레이아웃 토큰:
| 토큰 | 의미 | 예시 |
|---|---|---|
2006 / 06 | 연도, 4자리 또는 2자리 | 2026 / 26 |
01 / 1 / Jan / January | 월 | 03 / 3 / Mar / March |
02 / 2 / _2 | 일(0으로 채움, 그대로, 공백으로 채움) | 05 / 5 / " 5" |
Mon / Monday | 요일 | Thu / Thursday |
15 | 시, 24시간제 | 09 |
03 / 3 | 시, 12시간제 | 09 / 9 |
04 / 4 | 분 | 07 / 7 |
05 / 5 | 초 | 03 / 3 |
PM / pm | 오전/오후 표시 | AM |
.000 / .999 | 초 이하 소수(고정 / 뒤쪽 0 제거) | .250 / .25 |
MST | 시간대 약어 | UTC |
-0700 / -07:00 | 숫자 시간대 오프셋 | +0000 / +00:00 |
Z07:00 | -07:00과 같지만 UTC면 Z를 출력 | Z |
미리 정의된 레이아웃:
| 상수 | 레이아웃 |
|---|---|
time.RFC3339 | 2006-01-02T15:04:05Z07:00 (API와 JSON에는 이것을 쓰세요) |
time.RFC3339Nano | 2006-01-02T15:04:05.999999999Z07:00 |
time.DateTime (Go 1.20) | 2006-01-02 15:04:05 |
time.DateOnly (Go 1.20) | 2006-01-02 |
time.TimeOnly (Go 1.20) | 15:04:05 |
time.Kitchen | 3:04PM |
time.RFC1123 | Mon, 02 Jan 2006 15:04:05 MST (HTTP 날짜에는 대신 http.TimeFormat을 씀) |
전형적인 실수는 "2023-01-01"처럼 틀린 숫자로 레이아웃을 쓰는 것입니다. Go는 이를 거부하지 않습니다. 알아보지 못하는 문자는 그대로 복사하고 알아보는 문자는 치환합니다. 각 2는 일, 3은 12시간제의 시, 01 두 개는 모두 월이므로, 3월 5일 9시 7분은 5059-03-03으로 형식화됩니다. "YYYY-MM-DD" 같은 레이아웃에는 토큰이 하나도 없어서 그대로 출력됩니다. 형식화된 날짜가 이상해 보이면 레이아웃이 기준 값을 정확히 쓰는지 확인하세요.
문자열을 시간으로 파싱하기
time.Parse(layout, value)는 같은 레이아웃을 쓰며, 반드시 확인해야 하는 오류를 반환합니다:
입력에 시간대가 없으면 Parse는 UTC를 반환합니다. 같은 벽시계 문자열도 시간대에 따라 다른 순간을 뜻할 수 있기 때문에 ParseInLocation이 있습니다. 오류 메시지는 일치하지 않은 부분을 알려 주므로 레이아웃을 디버깅할 때 도움이 됩니다.
시간대
time.Time은 순간과 표시에 쓰이는 위치(location)를 합친 것입니다. In으로 위치를 바꾸면 출력 모양이 바뀔 뿐, 가리키는 순간은 바뀌지 않습니다.
time.UTC는 항상 쓸 수 있습니다. 시간은 UTC(또는 오프셋이 있는 RFC 3339)로 저장하고 전송하며, 표시할 때만 변환하세요.time.Local은 머신의 시간대입니다. 서버와 컨테이너에서는 UTC인 경우가 많고 노트북에서는 그렇지 않으므로, 이에 의존하는 코드는 곳마다 다르게 동작합니다.time.LoadLocation("Asia/Seoul")은 운영체제에서 IANA 데이터베이스를 읽습니다. 최소한의 컨테이너 이미지에는 이 데이터베이스가 없는 경우가 많고, 그러면 호출이 오류를 반환합니다. 빈 import_ "time/tzdata"는 데이터베이스를 바이너리에 넣어서(약 450KB) 항상 동작하게 합니다. 오류는 항상 처리하세요.time.FixedZone(name, offsetSeconds)는 오프셋이 일정한 시간대를 만듭니다. 서머타임 규칙이 없으므로 이름 있는 지역이 아니라 받은 오프셋에 쓰세요.
산술과 비교
출력에서 주목할 것:
- 1월 31일에
AddDate(0, 1, 0)을 하면 2월 28일이 아니라 3월 3일이 됩니다. Go는 한 달을 더해서 "2월 31일"을 만든 뒤 넘친 부분을 정규화합니다. "다음 달 같은 날, 넘치면 말일로"가 필요하다면 그 로직을 직접 작성하세요. Sub는 최대 약 292년까지인Duration을 반환합니다. 달력상 날짜 차이가 필요하면 UTC 자정의 날짜를 비교하고 24시간으로 나누세요.- 하루의 시작을 얻으려면
Date()로 시간을 다시 만드세요.t.Truncate(24 * time.Hour)는 UTC의 제로 시간을 기준으로 내림하므로 UTC가 아닌 모든 시간대에서 틀린 답을 줍니다.
==가 아니라 Equal, Before, After로 비교하세요. time.Now()는 벽시계가 조정되어도 time.Since가 올바르게 유지되도록 단조 시계 값을 포함합니다. ==는 그 값과 위치까지 비교하므로 같은 순간을 나타내는 두 Time 값이 다를 수 있습니다. 같은 이유로 time.Time을 정규화하지 않고 맵 키로 쓰지 마세요(t.UTC().Round(0)이 단조 시계 값을 제거합니다).
제로 값 time.Time은 1년 1월 1일 00:00 UTC입니다. t.IsZero()로 확인하세요.
경과 시간 재기
start := time.Now()
doWork()
log.Printf("doWork took %v", time.Since(start))
time.Since(start)는 time.Now().Sub(start)이고, time.Until(deadline)은 deadline.Sub(time.Now())입니다. 둘 다 가능하면 단조 시계를 쓰므로 시스템 시계 변경에 안전합니다. 코드 성능 측정에는 직접 시간을 재지 말고 testing 패키지의 벤치마크 기능을 쓰세요.
타이머와 티커
time.After(d)는 d가 지난 뒤 한 번 값을 받는 채널을 반환합니다. time.Timer는 같은 것에 Stop 메서드가 있는 것입니다. time.Ticker는 멈출 때까지 주기마다 값을 보냅니다.
20, 40, 60ms의 틱 세 번이 110ms 타이머보다 훨씬 먼저 도착하므로 프로그램은 세 번째 틱 뒤에 멈춥니다. time.AfterFunc(d, f)는 d가 지난 뒤 자기 고루틴에서 f를 실행하며, 일회성 지연 작업에 편리합니다.
Go 1.23부터는 더 이상 참조되지 않는 타이머와 티커는 Stop을 호출하지 않아도 가비지 컬렉션되고, 멈추거나 재설정한 타이머의 채널은 더 이상 오래된 값을 전달하지 않습니다. 그래도 defer로 Stop을 호출하는 것이 티커의 수명이 끝났다는 것을 분명히 보여 주는 방법입니다. 시간 제한이 대기 하나가 아니라 작업 전체에 적용된다면 대개 타이머보다 context.WithTimeout이 더 잘 읽힙니다.
JSON의 시간
time.Time은 JSON에서 나노초 정밀도로 RFC 3339 문자열과 자동으로 서로 변환됩니다:
type Event struct {
Name string `json:"name"`
At time.Time `json:"at"`
}
// {"name":"deploy","at":"2026-09-23T14:30:00Z"}
다른 형식의 JSON 문자열은 언마샬링에 실패합니다. Unix 타임스탬프나 사용자 정의 형식이라면 int64나 문자열로 저장해서 변환하거나, 자체 UnmarshalJSON을 가진 타입을 정의하세요.
흔한 실수
- 숫자만 넣은
time.Sleep(1)이나time.Sleep(n). 나노초입니다. - 숫자가 틀린 레이아웃.
"2023-01-01"이나"YYYY-MM-DD"는 레이아웃이 아닙니다."2006-01-02"를 쓰세요. - 분과 월을 혼동함. 분은
04, 월은01입니다."15:01"은 시 다음에 분이 아니라 월을 출력합니다. ==로 비교함.Equal을 쓰세요.time.Local에 의존함. 머신마다 다릅니다.time.UTC나 불러온 위치를 명시하세요.LoadLocation이나Parse의 오류를 무시함. 둘 다 실제 입력에서 실패합니다. 실패한Parse는 1년으로 출력되는 제로 시간을 반환하고, 실패한LoadLocation은t.In(loc)에서 패닉을 일으키는nil위치를 반환합니다.
자주 묻는 질문
Go에서 sleep은 어떻게 하나요?
time.Duration과 함께 time.Sleep을 호출합니다: time.Sleep(2 * time.Second) 또는 time.Sleep(500 * time.Millisecond). time.Sleep(2)처럼 숫자만 쓰면 컴파일은 되지만 Duration은 나노초를 세므로 2나노초 동안 잠듭니다. Sleep은 현재 고루틴만 블록합니다.
Go는 왜 날짜 형식에 2006-01-02 15:04:05를 쓰나요?
Go는 예시로 날짜를 형식화합니다. 레이아웃은 기준 시간 Mon Jan 2 15:04:05 MST 2006을 원하는 출력 모양대로 쓴 것입니다. 각 부분은 미국식 순서로 하나씩 커집니다. 월 1, 일 2, 시 3(24시간제로 15), 분 4, 초 5, 연도 6(2006), 시간대 오프셋 7(-0700). 그래서 "2006-01-02"는 연-월-일이고 "02/01/2006"은 일/월/연입니다.
Go에서 날짜 문자열은 어떻게 파싱하나요?
기준 시간으로 쓴 레이아웃과 함께 time.Parse(layout, value)를 씁니다: t, err := time.Parse("2006-01-02", "2026-09-23"). 항상 err를 확인하세요. 문자열에 시간대 정보가 없으면 결과는 UTC입니다. 다른 시간대로 해석하려면 time.ParseInLocation을 쓰세요.
Go에서 Unix 타임스탬프는 어떻게 얻나요?
time.Now().Unix()는 1970년 1월 1일 UTC부터의 초를 int64로 반환합니다. UnixMilli(), UnixMicro(), UnixNano()는 더 작은 단위를 줍니다. 반대 방향은 time.Unix(sec, 0)이나 time.UnixMilli(ms)를 쓰세요.
Go에서 두 시간은 어떻게 비교하나요?
t1.Before(t2), t1.After(t2), t1.Equal(t2)를 씁니다. ==는 쓰지 마세요. 위치와 단조 시계 값까지 비교하므로 같은 순간을 나타내는 두 값이 다르다고 나올 수 있습니다. t2.Sub(t1)은 차이를 Duration으로 줍니다.