Go 標準庫實戰指南

Go 標準庫核心套件實戰 — io.Reader/Writer、encoding/json、net/http、time、strings/strconv、os/exec


目錄


為什麼專講標準庫?

Go 的標準庫夠用且穩定,新人最大誤區是先找第三方套件。掌握以下七大套件後,多數 Web 服務 / CLI 工具可以完全用 stdlib 寫

  • io — 串流的抽象(Reader/Writer)
  • encoding/json — JSON 編解碼
  • net/http — HTTP client + server
  • time — 時間處理
  • strings / strconv — 字串操作與型別轉換
  • os / os/exec — 檔案系統與子行程

核心特點

  • 🎯 無外部依賴:減少 go.mod 雜訊與版本衝突
  • 穩定的 API:標準庫向下相容承諾(Go 1.x 不會 breaking change)
  • 🔧 效能穩:標準庫經過大量真實流量驗證
  • 📦 整合深:跨套件直接共用(如 net/http 內部用 io.Reader

io.Reader 與 io.Writer

io.Reader / io.Writer 是 Go 的串流通用介面,標準庫所有 I/O 都建在這兩個介面上。

介面定義

type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

任何能讀寫位元組的東西都實作這兩個介面:

  • *os.File
  • *bytes.Buffer
  • *strings.Reader
  • *http.Response.Body
  • net.Conn

常見組合

從 reader 讀全部 bytes

import "io"

data, err := io.ReadAll(reader) // 注意:對未知大小謹慎用

從 reader 拷貝到 writer

n, err := io.Copy(dst, src) // 串流拷貝,記憶體不爆

用 buffer 包裝小資料

import (
    "bytes"
    "strings"
)

// string 當 reader
r := strings.NewReader("hello")

// bytes 當 reader
r := bytes.NewReader([]byte{1, 2, 3})

// 寫到 buffer
var buf bytes.Buffer
buf.WriteString("foo")

限制讀取量

limited := io.LimitReader(r, 1024) // 最多讀 1024 bytes

Tee:邊讀邊寫

// 讀 r 的同時,把資料寫到 w
tee := io.TeeReader(r, w)
io.ReadAll(tee) // 從 r 讀,同時 w 收到 copy

範例:計算 SHA256

import (
    "crypto/sha256"
    "io"
    "os"
)

func hashFile(path string) ([]byte, error) {
    f, err := os.Open(path)
    if err != nil {
        return nil, err
    }
    defer f.Close()

    h := sha256.New()
    if _, err := io.Copy(h, f); err != nil {
        return nil, err
    }
    return h.Sum(nil), nil
}

io.Copy 串流到 h,整個檔案不會載入記憶體。

io.Closer 與 RAII

type Closer interface {
    Close() error
}

// 多數 IO 物件實作 ReadWriteCloser
type ReadWriteCloser interface {
    Reader
    Writer
    Closer
}

打開的東西要 close。慣例:

f, err := os.Open(path)
if err != nil {
    return err
}
defer f.Close() // 立刻 defer

encoding/json

Marshal / Unmarshal 基本

import "encoding/json"

type User struct {
    Name  string `json:"name"`
    Age   int    `json:"age"`
    Email string `json:"email,omitempty"`
}

// Marshal
u := User{Name: "Alice", Age: 30}
b, err := json.Marshal(u)
// b: {"name":"Alice","age":30}

// Pretty print
b, _ := json.MarshalIndent(u, "", "  ")

// Unmarshal
input := []byte(`{"name":"Bob","age":25,"email":"b@example.com"}`)
var u2 User
json.Unmarshal(input, &u2)

Struct Tag 細節

type T struct {
    A int    `json:"a"`            // 對應 key "a"
    B string `json:"b,omitempty"`  // 空值省略("", 0, nil)
    C int    `json:"-"`            // 完全不輸出
    D string `json:"d,string"`     // 字串包裝(給 JS 大數字保留精度用)
    e int                          // 小寫不匯出,不會被處理
}

處理未知欄位

// 預設:多餘欄位被忽略
input := []byte(`{"name":"a","extra":"x"}`)
var u User
json.Unmarshal(input, &u) // 不報錯,extra 被丟掉

// 嚴格模式:未知欄位報錯
dec := json.NewDecoder(bytes.NewReader(input))
dec.DisallowUnknownFields()
err := dec.Decode(&u) // 報錯

動態 JSON

// 用 map
var data map[string]any
json.Unmarshal(input, &data)

// 用 json.RawMessage 保留原樣
type Event struct {
    Type    string          `json:"type"`
    Payload json.RawMessage `json:"payload"`
}

// 之後依 type 分別 decode payload
var e Event
json.Unmarshal(input, &e)

switch e.Type {
case "user.created":
    var p UserCreated
    json.Unmarshal(e.Payload, &p)
}

Streaming encoder/decoder(大檔)

// 編碼到 writer
enc := json.NewEncoder(w)
enc.SetIndent("", "  ")
enc.Encode(data)

// 從 reader 解碼
dec := json.NewDecoder(r)
for dec.More() {
    var item Item
    if err := dec.Decode(&item); err != nil {
        break
    }
    // 處理 item
}

自訂 MarshalJSON / UnmarshalJSON

當預設行為不符需求(例如 time.Time 想用特定格式):

type Date struct {
    time.Time
}

const dateFormat = "2006-01-02"

func (d Date) MarshalJSON() ([]byte, error) {
    return []byte(`"` + d.Format(dateFormat) + `"`), nil
}

func (d *Date) UnmarshalJSON(data []byte) error {
    s := strings.Trim(string(data), `"`)
    t, err := time.Parse(dateFormat, s)
    if err != nil {
        return err
    }
    d.Time = t
    return nil
}

net/http 客戶端

最簡 GET

import (
    "io"
    "net/http"
)

resp, err := http.Get("https://api.example.com/users")
if err != nil {
    return err
}
defer resp.Body.Close()

body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))

POST JSON

import (
    "bytes"
    "encoding/json"
    "net/http"
)

payload := map[string]string{"name": "Alice"}
b, _ := json.Marshal(payload)

resp, err := http.Post(
    "https://api.example.com/users",
    "application/json",
    bytes.NewReader(b),
)

自訂 Request(加 header、timeout、context)

import (
    "context"
    "net/http"
    "time"
)

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

req, err := http.NewRequestWithContext(ctx, "GET", "https://api.example.com", nil)
if err != nil {
    return err
}
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("User-Agent", "myapp/1.0")

client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)

⚠️ 預設 client 沒 timeout

http.Get(...)  // 用 http.DefaultClient,timeout = 0 = 永不超時

生產用必須自訂 client

client := &http.Client{
    Timeout: 10 * time.Second,
    Transport: &http.Transport{
        MaxIdleConns:        100,
        MaxIdleConnsPerHost: 10,
        IdleConnTimeout:     90 * time.Second,
    },
}

重用 client

http.Client 設計成可重用、執行緒安全 — 整個 process 共用一個就好

var httpClient = &http.Client{Timeout: 10 * time.Second}

func fetchUser(ctx context.Context, id string) (*User, error) {
    req, _ := http.NewRequestWithContext(ctx, "GET", "...", nil)
    resp, err := httpClient.Do(req)
    // ...
}

處理 response body

resp, err := client.Do(req)
if err != nil {
    return err
}
defer resp.Body.Close() // ⭐ 必須 close

if resp.StatusCode != http.StatusOK {
    return fmt.Errorf("unexpected status: %s", resp.Status)
}

var data Response
if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
    return err
}

注意:即使你不讀 body,也要 io.Copy(io.Discard, resp.Body) + Close,否則 connection 不能被重用。


net/http 伺服器

最簡 server

package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "Hello, World!")
    })

    http.ListenAndServe(":8080", nil)
}

用 ServeMux 路由

mux := http.NewServeMux()
mux.HandleFunc("GET /users", listUsers)         // Go 1.22+ 支援 method
mux.HandleFunc("GET /users/{id}", getUser)      // 路徑參數
mux.HandleFunc("POST /users", createUser)

http.ListenAndServe(":8080", mux)

Go 1.22+ 內建 method-based routing 與 path parameter。

取路徑參數

func getUser(w http.ResponseWriter, r *http.Request) {
    id := r.PathValue("id") // Go 1.22+
    // ...
}

讀取 query / body

func handler(w http.ResponseWriter, r *http.Request) {
    // Query
    q := r.URL.Query().Get("q")
    limit := r.URL.Query().Get("limit")

    // JSON body
    var payload struct {
        Name string `json:"name"`
    }
    if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
        http.Error(w, "bad json", http.StatusBadRequest)
        return
    }

    // Form
    r.ParseForm()
    name := r.FormValue("name")
}

回傳 JSON

func returnJSON(w http.ResponseWriter, data any, status int) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(status)
    json.NewEncoder(w).Encode(data)
}

Middleware 模式

type Middleware func(http.Handler) http.Handler

func logging(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        next.ServeHTTP(w, r)
        log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
    })
}

func recover(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        defer func() {
            if rec := recover(); rec != nil {
                log.Printf("panic: %v", rec)
                http.Error(w, "Internal Server Error", 500)
            }
        }()
        next.ServeHTTP(w, r)
    })
}

// 串接
handler := logging(recover(mux))
http.ListenAndServe(":8080", handler)

Graceful shutdown

srv := &http.Server{
    Addr:    ":8080",
    Handler: mux,
}

go func() {
    if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
        log.Fatal(err)
    }
}()

// 等訊號
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt, syscall.SIGTERM)
<-stop

// 30 秒內讓進行中的 request 跑完
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
srv.Shutdown(ctx)

time 套件

取現在時間

now := time.Now()
fmt.Println(now)
// 2026-05-16 10:00:00.123456789 +0800 CST

fmt.Println(now.UTC())
fmt.Println(now.Format(time.RFC3339))
// 2026-05-16T02:00:00Z

格式化的詭異點:用「2006-01-02」當範本

Go 用「2006 年 1 月 2 日 3 點 4 分 5 秒」當 layout,不是用 YYYY-MM-DD

// 記憶法:1 2 3 4 5 6 7(月 日 時 分 秒 年 時區)
layout := "2006-01-02 15:04:05"
formatted := now.Format(layout)

常用 layout 常數

time.RFC3339           // "2006-01-02T15:04:05Z07:00"
time.RFC3339Nano       // 帶 nanosecond
time.DateTime          // "2006-01-02 15:04:05"
time.DateOnly          // "2006-01-02"
time.TimeOnly          // "15:04:05"

Parse 字串成 time

t, err := time.Parse(time.RFC3339, "2026-05-16T10:00:00Z")
t, err := time.Parse("2006-01-02", "2026-05-16")

// 含時區
loc, _ := time.LoadLocation("Asia/Taipei")
t, err := time.ParseInLocation("2006-01-02 15:04:05", "2026-05-16 10:00:00", loc)

Duration 操作

d := 5 * time.Second
d2 := time.Hour + 30*time.Minute

// 加減時間
future := now.Add(24 * time.Hour)
past := now.Add(-1 * time.Hour)
diff := future.Sub(now) // Duration

// 比較
if t1.Before(t2) { ... }
if t1.After(t2) { ... }
if t1.Equal(t2) { ... }

Sleep / Timer / Ticker

// 阻塞 sleep
time.Sleep(time.Second)

// 一次性 timer
<-time.After(time.Second) // 1 秒後 channel 有訊號

// 重複 ticker
tick := time.NewTicker(time.Second)
defer tick.Stop()

for {
    select {
    case <-tick.C:
        fmt.Println("每秒一次")
    case <-ctx.Done():
        return
    }
}

時區處理

loc, _ := time.LoadLocation("Asia/Taipei")
now := time.Now().In(loc) // 轉到指定時區

Production 慣例

  • 內部處理用 UTC
  • 對使用者顯示時才轉本地時區
  • 存資料庫永遠存 UTC

strings 與 strconv

strings 常用

import "strings"

strings.Contains("hello", "ell")     // true
strings.HasPrefix("hello", "he")     // true
strings.HasSuffix("hello", "lo")     // true
strings.Index("hello", "ll")         // 2
strings.Count("banana", "a")         // 3
strings.Replace("hello", "l", "L", -1) // "heLLo"
strings.ReplaceAll("hello", "l", "L")  // "heLLo"
strings.ToUpper("hello")             // "HELLO"
strings.ToLower("HELLO")             // "hello"
strings.Title("hello world")         // 已 deprecated,改用 cases.Title
strings.TrimSpace("  x  ")           // "x"
strings.Trim("###x###", "#")         // "x"
strings.Split("a,b,c", ",")          // ["a", "b", "c"]
strings.Join([]string{"a","b","c"}, ",") // "a,b,c"
strings.Fields("  a  b  c  ")        // ["a", "b", "c"](空白拆)
strings.Repeat("ab", 3)              // "ababab"

strings.Builder 高效拼字串

var b strings.Builder
for i := 0; i < 1000; i++ {
    b.WriteString("a")
}
result := b.String()

s += "a" 快很多(避免重複 allocate)。

strconv 型別轉換

import "strconv"

// string → 數
n, err := strconv.Atoi("123")
i, err := strconv.ParseInt("123", 10, 64) // base 10, 64bit
u, err := strconv.ParseUint("123", 10, 64)
f, err := strconv.ParseFloat("3.14", 64)
b, err := strconv.ParseBool("true")

// 數 → string
s := strconv.Itoa(123)
s := strconv.FormatInt(123, 10)
s := strconv.FormatFloat(3.14, 'f', 2, 64) // "3.14"
s := strconv.FormatBool(true)

// 字串轉義(給 JSON / Go literal)
s := strconv.Quote("hello\nworld") // "\"hello\\nworld\""

os 與 os/exec

環境變數

import "os"

home := os.Getenv("HOME")
path, ok := os.LookupEnv("PATH") // 區分「不存在」與「空字串」
os.Setenv("FOO", "bar")
os.Unsetenv("FOO")

命令列參數

// os.Args[0] 是執行檔路徑
fmt.Println(os.Args[1:]) // 其他參數

// 用 flag 套件處理(更標準)
import "flag"

var (
    port = flag.Int("port", 8080, "server port")
    verbose = flag.Bool("v", false, "verbose")
)
flag.Parse()

檔案操作

// 讀整檔
data, err := os.ReadFile("config.json")

// 寫整檔(覆蓋)
err := os.WriteFile("out.txt", []byte("hello"), 0o644)

// Open + 串流讀
f, err := os.Open("big.log")
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
    line := scanner.Text()
    // ...
}

// Stat 取資訊
info, err := os.Stat("file.txt")
size := info.Size()
modTime := info.ModTime()
isDir := info.IsDir()

// 建目錄
os.Mkdir("foo", 0o755)
os.MkdirAll("a/b/c", 0o755) // 遞迴

// 移除
os.Remove("file.txt")
os.RemoveAll("foo/") // 遞迴

// 移動 / 改名
os.Rename("old.txt", "new.txt")

執行外部指令

import "os/exec"

// 簡單執行(同步)
out, err := exec.Command("ls", "-la").Output()
fmt.Println(string(out))

// 含 stderr
out, err := exec.Command("foo").CombinedOutput()

// 進階:自訂 stdin/stdout
cmd := exec.Command("grep", "ERROR")
cmd.Stdin = strings.NewReader("INFO: ok\nERROR: bad\n")
var out bytes.Buffer
cmd.Stdout = &out
cmd.Run()

// 帶 context(可超時取消)
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
out, err := exec.CommandContext(ctx, "slow-cmd").Output()

// 取得 exit code
err := exec.Command("foo").Run()
if exitErr, ok := err.(*exec.ExitError); ok {
    fmt.Println("exit code:", exitErr.ExitCode())
}

訊號處理

import (
    "os"
    "os/signal"
    "syscall"
)

sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)

<-sigCh
fmt.Println("收到結束訊號")

實戰範例

範例 1:完整 HTTP API(client 呼外部 + server 回 JSON)

package main

import (
    "context"
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "time"
)

var httpClient = &http.Client{Timeout: 10 * time.Second}

type Weather struct {
    City        string  `json:"city"`
    Temperature float64 `json:"temp"`
}

func fetchWeather(ctx context.Context, city string) (*Weather, error) {
    url := fmt.Sprintf("https://api.example.com/weather?city=%s", city)
    req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)

    resp, err := httpClient.Do(req)
    if err != nil {
        return nil, err
    }
    defer resp.Body.Close()

    if resp.StatusCode != 200 {
        return nil, fmt.Errorf("unexpected status: %d", resp.StatusCode)
    }

    var w Weather
    if err := json.NewDecoder(resp.Body).Decode(&w); err != nil {
        return nil, err
    }
    return &w, nil
}

func handler(w http.ResponseWriter, r *http.Request) {
    ctx, cancel := context.WithTimeout(r.Context(), 5*time.Second)
    defer cancel()

    city := r.URL.Query().Get("city")
    if city == "" {
        http.Error(w, "city required", http.StatusBadRequest)
        return
    }

    weather, err := fetchWeather(ctx, city)
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadGateway)
        return
    }

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(weather)
}

func main() {
    http.HandleFunc("/weather", handler)
    log.Println("listening :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

範例 2:CSV 串流處理(大檔不爆記憶體)

package main

import (
    "encoding/csv"
    "fmt"
    "io"
    "os"
)

func main() {
    f, err := os.Open("big.csv")
    if err != nil {
        panic(err)
    }
    defer f.Close()

    r := csv.NewReader(f)

    // 跳 header
    if _, err := r.Read(); err != nil {
        panic(err)
    }

    for {
        record, err := r.Read()
        if err == io.EOF {
            break
        }
        if err != nil {
            panic(err)
        }
        fmt.Println(record)
    }
}

範例 3:執行 shell 指令並 timeout

package main

import (
    "context"
    "fmt"
    "os/exec"
    "time"
)

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
    defer cancel()

    out, err := exec.CommandContext(ctx, "sh", "-c", "sleep 10 && echo done").Output()
    if err != nil {
        if ctx.Err() == context.DeadlineExceeded {
            fmt.Println("超時")
            return
        }
        fmt.Println("失敗:", err)
        return
    }
    fmt.Println(string(out))
}

範例 4:定期 ticker 配合 graceful shutdown

package main

import (
    "context"
    "fmt"
    "os"
    "os/signal"
    "syscall"
    "time"
)

func main() {
    ctx, cancel := signal.NotifyContext(context.Background(),
        os.Interrupt, syscall.SIGTERM)
    defer cancel()

    tick := time.NewTicker(time.Second)
    defer tick.Stop()

    for {
        select {
        case <-ctx.Done():
            fmt.Println("\nshutting down")
            return
        case t := <-tick.C:
            fmt.Println("tick at", t.Format(time.RFC3339))
        }
    }
}

最佳實踐

1. 永遠 close response body

resp, err := client.Do(req)
if err != nil { return err }
defer resp.Body.Close() // ⭐

2. http.Client 設 timeout 並重用

// ✅
var client = &http.Client{Timeout: 10 * time.Second}

// ❌ 用預設 client(無 timeout)
http.Get(...)

3. JSON 用 streaming(大資料)

// ❌ 小資料 OK,大檔吃光記憶體
data, _ := io.ReadAll(resp.Body)
json.Unmarshal(data, &v)

// ✅ 串流
json.NewDecoder(resp.Body).Decode(&v)

4. 時間處理永遠用 UTC(內部)

// ✅
t := time.Now().UTC()
db.Save(t)

// 顯示給使用者才轉本地時區
loc, _ := time.LoadLocation("Asia/Taipei")
display := t.In(loc).Format(time.DateTime)

5. strings.Builder 取代 +=

// ❌ O(n²)
s := ""
for _, x := range items {
    s += x
}

// ✅ O(n)
var b strings.Builder
for _, x := range items {
    b.WriteString(x)
}
s := b.String()

6. exec 加 context 防卡住

// ❌ 可能永遠卡住
exec.Command("foo").Run()

// ✅ 有上限
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
defer cancel()
exec.CommandContext(ctx, "foo").Run()

7. 檢查 status code 與 error 分開

resp, err := client.Do(req)
if err != nil {
    // 網路層失敗
    return err
}
defer resp.Body.Close()

if resp.StatusCode >= 400 {
    // 應用層失敗(4xx/5xx)
    return fmt.Errorf("unexpected status: %d", resp.StatusCode)
}

常見問題

問題 1:http.Get 沒 timeout 卡死

症狀:HTTP 呼叫掛住整個 process

原因http.DefaultClient 的 Timeout = 0 = 永不超時

解決:自訂 client

client := &http.Client{Timeout: 10 * time.Second}

問題 2:JSON Unmarshal 把空字串當數字 fail

症狀:API 回傳 {"count": ""},但 struct 是 int

解決:用 *int 或自訂 UnmarshalJSON:

type Response struct {
    Count *int `json:"count"` // 可為 nil
}

問題 3:time.Parse 噴 cannot parse

原因:layout 字串寫錯,必須是 2006-01-02 15:04:05 那組魔法數字

// ❌ 直覺寫法
time.Parse("YYYY-MM-DD", "2026-05-16")

// ✅ 正確
time.Parse("2006-01-02", "2026-05-16")

問題 4:os.ReadFile 大檔吃光記憶體

解決:用 bufio.Scannerio.Copy 串流處理

f, _ := os.Open(path)
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
    line := scanner.Text()
    // ...
}

問題 5:exec.Output 拿不到 stderr

原因Output() 只拿 stdout

解決:用 CombinedOutput 或自訂 cmd.Stderr:

out, err := exec.Command("foo").CombinedOutput()
// 或
var stderr bytes.Buffer
cmd := exec.Command("foo")
cmd.Stderr = &stderr
cmd.Run()
fmt.Println("stderr:", stderr.String())

問題 6:goroutine leak 因為沒 close body

// ❌ 不 close → connection 不能 reuse → TCP 連線累積
resp, _ := client.Do(req)
// 沒 defer resp.Body.Close()
return

總結

核心要點

io.Reader/Writer 是 Go IO 抽象之根
json 用 streaming 處理大檔
http.Client 永遠設 timeout 並重用
time 用 UTC 內部、本地時區顯示
exec 永遠加 context

設計原則

  • ✅ http.Client 重用 + timeout
  • ✅ resp.Body 必須 close
  • ✅ time 內部 UTC、顯示本地時區
  • ✅ strings.Builder 取代 +=
  • ✅ exec / http 永遠加 context

速查表

// io
io.Copy(dst, src)
io.ReadAll(r)
io.LimitReader(r, n)

// json
json.Marshal(v); json.Unmarshal(b, &v)
json.NewEncoder(w).Encode(v)
json.NewDecoder(r).Decode(&v)

// http client
client := &http.Client{Timeout: 10 * time.Second}
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
resp, _ := client.Do(req)
defer resp.Body.Close()

// http server
mux := http.NewServeMux()
mux.HandleFunc("GET /path", handler)
srv := &http.Server{Addr: ":8080", Handler: mux}
srv.ListenAndServe()
srv.Shutdown(ctx)

// time
now := time.Now()
now.Format(time.RFC3339)
time.Parse("2006-01-02", "2026-05-16")
time.Sleep(time.Second)

// strings/strconv
strings.Split(s, ",")
strings.Join(arr, ",")
strconv.Atoi("123")
strconv.Itoa(123)

// os/exec
out, _ := exec.CommandContext(ctx, "ls").Output()

Time Layout 速查

想要的格式 Layout
2026-05-16 2006-01-02
2026-05-16 10:00:00 2006-01-02 15:04:05
2026-05-16T10:00:00Z time.RFC3339
10:00 AM 3:04 PM
Mon, 16 May 2026 Mon, 02 Jan 2006

相關閱讀


參考資源


建立日期:2026-05-16 最後更新:2026-05-16

🔗相關文章