Menu

Golang time: Now, Sleep, Format, Parse, Duration

Go에서 날짜와 시간을 다루는 방법: time.Now와 time.Sleep, Duration 산술, Format과 Parse를 위한 2006-01-02 15:04:05 기준 레이아웃, 시간대, Unix 타임스탬프, 타이머와 티커를 다룹니다.

이 페이지에는 실행 가능한 에디터가 있습니다 - 편집하고 실행하면 결과를 바로 볼 수 있습니다.

프로그램 하나로 보는 기본

time.Now()는 현재 지역 시간을 반환하고, time.Sleep은 현재 고루틴을 멈추고, time.Since는 경과 시간을 재고, time.Date는 특정 시각을 만듭니다. 이 페이지의 나머지는 각 부분을 차례로 다룹니다. 예제는 어디서 실행하든 출력이 같도록 고정된 날짜와 UTC를 씁니다.

Sleep과 Duration

time.Duration은 나노초를 세는 int64입니다. 패키지는 읽기 좋은 값을 만드는 상수를 정의합니다:

상수
time.Nanosecond1
time.Microsecond1000 ns
time.Millisecond1000 µs
time.Second1000 ms
time.Minute60 s
time.Hour60 min

time.Day는 없습니다. 서머타임으로 시간대가 바뀔 때 하루가 항상 24시간은 아니기 때문입니다. 달력의 날짜에는 AddDate를 쓰세요(아래 참고).

Duration이 정수라서 생기는 흔한 버그가 두 가지 있습니다:

  • time.Sleep(5)는 5나노초 동안 잠듭니다. 항상 단위를 곱하세요: time.Sleep(5 * time.Second).
  • nint 변수라면 time.Sleep(n * time.Second)는 컴파일되지 않습니다. 변환하세요: time.Duration(n) * time.Second. 5 같은 상수는 타입 없는 상수가 Duration 타입에 맞춰지므로 변환 없이 동작합니다.

ParseDurationns, us(또는 µs), ms, s, m, h를 받으며 "2h45m"이나 "1.5s"처럼 조합할 수 있습니다. 일 단위는 없습니다.

형식화: 2006-01-02 기준 레이아웃

Go는 %Y-%m-%dyyyy-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 / January03 / 3 / Mar / March
02 / 2 / _2일(0으로 채움, 그대로, 공백으로 채움)05 / 5 / " 5"
Mon / Monday요일Thu / Thursday
15시, 24시간제09
03 / 3시, 12시간제09 / 9
04 / 407 / 7
05 / 503 / 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.RFC33392006-01-02T15:04:05Z07:00 (API와 JSON에는 이것을 쓰세요)
time.RFC3339Nano2006-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.Kitchen3:04PM
time.RFC1123Mon, 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을 호출하지 않아도 가비지 컬렉션되고, 멈추거나 재설정한 타이머의 채널은 더 이상 오래된 값을 전달하지 않습니다. 그래도 deferStop을 호출하는 것이 티커의 수명이 끝났다는 것을 분명히 보여 주는 방법입니다. 시간 제한이 대기 하나가 아니라 작업 전체에 적용된다면 대개 타이머보다 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년으로 출력되는 제로 시간을 반환하고, 실패한 LoadLocationt.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으로 줍니다.

Coddy programming languages illustration

Coddy로 코딩 배우기

시작하기