Пакет time -- один из самых используемых в Go: от измерения производительности до планирования задач. Криптографические пакеты обеспечивают хеширование, HMAC, шифрование и генерацию случайных чисел.
time.Time -- моменты времени
import "time"
// Current time
now := time.Now()
fmt.Println(now) // 2025-06-15 10:30:45.123456789 +0300 MSK
// Components
year := now.Year() // 2025
month := now.Month() // June (time.Month type)
day := now.Day() // 15
hour := now.Hour() // 10
minute := now.Minute() // 30
second := now.Second() // 45
nano := now.Nanosecond() // 123456789
weekday := now.Weekday() // Sunday (time.Weekday type)
// Comparison
t1 := time.Now()
t2 := t1.Add(time.Hour)
t1.Before(t2) // true
t2.After(t1) // true
t1.Equal(t1) // true
// Zero value check
var t time.Time
t.IsZero() // true
time.Since() и time.Until()
// Measure elapsed time
start := time.Now()
doWork()
elapsed := time.Since(start) // Shortcut for time.Now().Sub(start)
fmt.Printf("Took %v\n", elapsed)
// Time until deadline
deadline := time.Date(2025, 12, 31, 23, 59, 59, 0, time.UTC)
remaining := time.Until(deadline) // Shortcut for deadline.Sub(time.Now())
fmt.Printf("Days remaining: %.0f\n", remaining.Hours()/24)
time.Duration -- длительности
// Duration constants
time.Nanosecond // 1
time.Microsecond // 1000 nanoseconds
time.Millisecond // 1000 microseconds
time.Second // 1000 milliseconds
time.Minute // 60 seconds
time.Hour // 60 minutes
// Creating durations
d := 5 * time.Second // 5s
d = 2*time.Hour + 30*time.Minute // 2h30m0s
// Parse duration from string
d, err := time.ParseDuration("1h30m45s") // 1h30m45s
d, err = time.ParseDuration("500ms") // 500ms
d, err = time.ParseDuration("2.5s") // 2.5s
// Duration methods
d = 90 * time.Second
d.Hours() // 0.025
d.Minutes() // 1.5
d.Seconds() // 90
d.Milliseconds() // 90000
d.String() // "1m30s"
// Arithmetic
t := time.Now()
later := t.Add(2 * time.Hour) // Add duration
diff := later.Sub(t) // Get duration between times
time.Parse и time.Format -- эталонное время
Go использует уникальный подход к форматированию: эталонное время вместо символов-заполнителей.
Эталонное время: Mon Jan 2 15:04:05 MST 2006
Это время выбрано специально: 01/02 03:04:05PM '06 -0700
// Parsing
t, err := time.Parse("2006-01-02", "2025-06-15")
t, err = time.Parse("2006-01-02 15:04:05", "2025-06-15 10:30:00")
t, err = time.Parse(time.RFC3339, "2025-06-15T10:30:00Z")
// Formatting
now := time.Now()
s := now.Format("2006-01-02") // "2025-06-15"
s = now.Format("02.01.2006 15:04") // "15.06.2025 10:30"
s = now.Format("Monday, January 2, 2006") // "Sunday, June 15, 2025"
s = now.Format(time.RFC3339) // "2025-06-15T10:30:00+03:00"
s = now.Format(time.RFC822) // "15 Jun 25 10:30 MSK"
// Predefined layouts
time.RFC3339 // "2006-01-02T15:04:05Z07:00"
time.RFC3339Nano // "2006-01-02T15:04:05.999999999Z07:00"
time.DateTime // "2006-01-02 15:04:05" (Go 1.20+)
time.DateOnly // "2006-01-02" (Go 1.20+)
time.TimeOnly // "15:04:05" (Go 1.20+)
Распространённые форматы
| Формат | Шаблон Go | Пример |
|---|---|---|
| ISO 8601 | "2006-01-02T15:04:05Z07:00" |
2025-06-15T10:30:00+03:00 |
| Дата | "2006-01-02" |
2025-06-15 |
| Время | "15:04:05" |
10:30:00 |
| Русский | "02.01.2006" |
15.06.2025 |
| Лог | "2006/01/02 15:04:05" |
2025/06/15 10:30:00 |
| 12h | "3:04 PM" |
10:30 AM |
Таймеры и тикеры
time.After и time.Sleep
// Sleep -- block for duration
time.Sleep(2 * time.Second)
// After -- channel that fires once after duration
select {
case <-time.After(5 * time.Second):
fmt.Println("Timeout!")
case result := <-resultCh:
fmt.Println("Got result:", result)
}
time.NewTimer -- однократный таймер
timer := time.NewTimer(5 * time.Second)
defer timer.Stop() // Always stop when done!
select {
case <-timer.C:
fmt.Println("Timer fired")
case <-ctx.Done():
fmt.Println("Cancelled")
if !timer.Stop() {
<-timer.C // Drain the channel
}
}
// Reset timer
timer.Reset(10 * time.Second)
time.NewTicker -- периодический тикер
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop() // ALWAYS stop tickers to avoid goroutine leak!
for {
select {
case t := <-ticker.C:
fmt.Println("Tick at", t)
case <-ctx.Done():
fmt.Println("Done")
return
}
}
time.AfterFunc -- отложенный вызов
// Execute function after delay in a new goroutine
timer := time.AfterFunc(5*time.Second, func() {
fmt.Println("Executed after 5 seconds")
})
// Cancel if needed
timer.Stop()
Часовые пояса
// UTC
utcTime := time.Now().UTC()
// Local timezone
localTime := time.Now() // Uses system timezone
// Load specific timezone
loc, err := time.LoadLocation("Europe/Moscow")
if err != nil {
log.Fatal(err)
}
moscowTime := time.Now().In(loc)
// Fixed offset timezone
msk := time.FixedZone("MSK", 3*60*60) // UTC+3
t := time.Now().In(msk)
// Convert between timezones
nyLoc, _ := time.LoadLocation("America/New_York")
nyTime := moscowTime.In(nyLoc)
// Parse with timezone
t, err = time.ParseInLocation("2006-01-02 15:04", "2025-06-15 10:30", loc)
Правило: Храните время в UTC, конвертируйте в локальное только для отображения.
Монотонные часы
Go автоматически использует монотонные часы для измерения длительностей:
start := time.Now() // Contains BOTH wall clock AND monotonic clock
// Even if system clock changes (NTP sync, DST, etc.),
// Sub/Since use monotonic component
time.Sleep(time.Second)
elapsed := time.Since(start) // Uses monotonic clock -- always accurate
// Strip monotonic reading
wallOnly := start.Round(0) // Now only wall clock
Монотонные часы гарантируют корректные измерения времени даже при синхронизации NTP или смене часового пояса.
crypto/sha256 и crypto/sha512
import (
"crypto/sha256"
"crypto/sha512"
"encoding/hex"
)
// SHA-256
data := []byte("Hello, World!")
hash := sha256.Sum256(data)
fmt.Println(hex.EncodeToString(hash[:])) // 64-char hex string
// SHA-256 with streaming (for large data)
h := sha256.New()
h.Write([]byte("Hello, "))
h.Write([]byte("World!"))
hash2 := h.Sum(nil)
fmt.Println(hex.EncodeToString(hash2))
// SHA-512
hash512 := sha512.Sum512(data)
fmt.Println(hex.EncodeToString(hash512[:])) // 128-char hex string
// Hash a file
func hashFile(path string) (string, error) {
f, err := os.Open(path)
if err != nil {
return "", fmt.Errorf("opening file: %w", err)
}
defer f.Close()
h := sha256.New()
if _, err := io.Copy(h, f); err != nil {
return "", fmt.Errorf("hashing: %w", err)
}
return hex.EncodeToString(h.Sum(nil)), nil
}
crypto/hmac -- подпись сообщений
HMAC (Hash-based Message Authentication Code) проверяет целостность и аутентичность данных:
import "crypto/hmac"
// Create HMAC
func signMessage(message, secret []byte) []byte {
mac := hmac.New(sha256.New, secret)
mac.Write(message)
return mac.Sum(nil)
}
// Verify HMAC (constant-time comparison!)
func verifyMessage(message, secret, signature []byte) bool {
expected := signMessage(message, secret)
return hmac.Equal(signature, expected) // Constant-time comparison
}
// Example: webhook verification
func verifyWebhook(body []byte, signature string, secret string) bool {
sig, err := hex.DecodeString(signature)
if err != nil {
return false
}
return verifyMessage(body, []byte(secret), sig)
}
Критично: Всегда используйте
hmac.Equal()вместоbytes.Equal()для сравнения подписей.hmac.Equalработает за постоянное время, предотвращая timing-атаки.
crypto/rand -- криптографически безопасные числа
import "crypto/rand"
// Random bytes
buf := make([]byte, 32)
_, err := rand.Read(buf) // Fills buf with random bytes
if err != nil {
log.Fatal(err) // Should never happen on modern systems
}
// Random UUID (v4)
func generateUUID() string {
uuid := make([]byte, 16)
rand.Read(uuid)
uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant 10
return fmt.Sprintf("%x-%x-%x-%x-%x",
uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:])
}
// Random token (URL-safe)
func generateToken(length int) (string, error) {
bytes := make([]byte, length)
if _, err := rand.Read(bytes); err != nil {
return "", err
}
return base64.URLEncoding.EncodeToString(bytes), nil
}
math/rand/v2 -- псевдослучайные числа (Go 1.22+)
math/rand/v2 -- обновлённый пакет для некриптографических случайных чисел:
import "math/rand/v2"
// Top-level functions (auto-seeded since Go 1.20)
n := rand.IntN(100) // [0, 100) -- random int
f := rand.Float64() // [0.0, 1.0) -- random float
n = rand.N(100) // Same as IntN but generic (Go 1.22+)
// Shuffle
items := []string{"a", "b", "c", "d", "e"}
rand.Shuffle(len(items), func(i, j int) {
items[i], items[j] = items[j], items[i]
})
// Custom source for reproducible results
src := rand.NewPCG(42, 0) // PCG source with seed 42
rng := rand.New(src)
rng.IntN(100) // Always same sequence for same seed
// ChaCha8 source (cryptographic quality but faster than crypto/rand)
src2 := rand.NewChaCha8([32]byte{1, 2, 3}) // Seed
rng2 := rand.New(src2)
Правило:
crypto/randдля токенов, ключей, пароли, сессии.math/rand/v2для тестов, шаффлы, случайные задержки, игры.
crypto/tls -- TLS-соединения
import "crypto/tls"
// HTTPS server
cert, err := tls.LoadX509KeyPair("cert.pem", "key.pem")
if err != nil {
log.Fatal(err)
}
tlsConfig := &tls.Config{
Certificates: []tls.Certificate{cert},
MinVersion: tls.VersionTLS12,
}
server := &http.Server{
Addr: ":443",
TLSConfig: tlsConfig,
}
log.Fatal(server.ListenAndServeTLS("", ""))
// TLS client
client := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{
MinVersion: tls.VersionTLS12,
},
},
}
crypto/aes и cipher.AEAD -- симметричное шифрование
AES-GCM -- рекомендуемый алгоритм для симметричного шифрования:
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
)
// Encrypt with AES-GCM
func encrypt(key, plaintext []byte) ([]byte, error) {
block, err := aes.NewCipher(key) // key must be 16, 24, or 32 bytes
if err != nil {
return nil, fmt.Errorf("creating cipher: %w", err)
}
aead, err := cipher.NewGCM(block)
if err != nil {
return nil, fmt.Errorf("creating GCM: %w", err)
}
// Generate random nonce
nonce := make([]byte, aead.NonceSize())
if _, err := rand.Read(nonce); err != nil {
return nil, fmt.Errorf("generating nonce: %w", err)
}
// Encrypt: nonce is prepended to ciphertext
return aead.Seal(nonce, nonce, plaintext, nil), nil
}
// Decrypt with AES-GCM
func decrypt(key, ciphertext []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, fmt.Errorf("creating cipher: %w", err)
}
aead, err := cipher.NewGCM(block)
if err != nil {
return nil, fmt.Errorf("creating GCM: %w", err)
}
nonceSize := aead.NonceSize()
if len(ciphertext) < nonceSize {
return nil, errors.New("ciphertext too short")
}
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
return aead.Open(nil, nonce, ciphertext, nil)
}
// Usage
func main() {
key := make([]byte, 32) // AES-256
rand.Read(key)
encrypted, err := encrypt(key, []byte("Secret message"))
if err != nil {
log.Fatal(err)
}
decrypted, err := decrypt(key, encrypted)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(decrypted)) // "Secret message"
}
Практический пример: подписанные токены
type Token struct {
UserID string `json:"user_id"`
ExpiresAt time.Time `json:"expires_at"`
}
func createToken(userID string, secret []byte, ttl time.Duration) (string, error) {
token := Token{
UserID: userID,
ExpiresAt: time.Now().Add(ttl),
}
payload, err := json.Marshal(token)
if err != nil {
return "", fmt.Errorf("marshaling token: %w", err)
}
// Sign with HMAC
mac := hmac.New(sha256.New, secret)
mac.Write(payload)
signature := mac.Sum(nil)
// Combine: base64(payload).base64(signature)
return base64.URLEncoding.EncodeToString(payload) + "." +
base64.URLEncoding.EncodeToString(signature), nil
}
func verifyToken(tokenStr string, secret []byte) (*Token, error) {
parts := strings.SplitN(tokenStr, ".", 2)
if len(parts) != 2 {
return nil, errors.New("invalid token format")
}
payload, err := base64.URLEncoding.DecodeString(parts[0])
if err != nil {
return nil, fmt.Errorf("decoding payload: %w", err)
}
signature, err := base64.URLEncoding.DecodeString(parts[1])
if err != nil {
return nil, fmt.Errorf("decoding signature: %w", err)
}
// Verify signature
mac := hmac.New(sha256.New, secret)
mac.Write(payload)
expected := mac.Sum(nil)
if !hmac.Equal(signature, expected) {
return nil, errors.New("invalid signature")
}
// Parse token
var token Token
if err := json.Unmarshal(payload, &token); err != nil {
return nil, fmt.Errorf("unmarshaling token: %w", err)
}
// Check expiration
if time.Now().After(token.ExpiresAt) {
return nil, errors.New("token expired")
}
return &token, nil
}