BAB 35: Time, Parsing, dan Formatting

Pelajari cara bekerja dengan tanggal dan waktu di Go menggunakan package time: membuat, mem-parsing, memformat, dan membandingkan nilai time.Time.

Di bab sebelumnya, seed berbasis time.Now().UnixNano() digunakan untuk menghasilkan angka acak yang berbeda setiap eksekusi. Itu adalah penggunaan time yang paling sederhana — hanya mengambil nilai numerik dari waktu saat ini. Tapi time di Go jauh lebih kaya dari itu. Program nyata sering perlu menyimpan, membandingkan, memformat, dan mem-parsing tanggal: kapan sebuah transaksi dicatat, berapa hari lagi tenggat waktu, atau bagaimana mengubah string "2024-03-15" dari input pengguna menjadi nilai yang bisa diolah. Semua itu ditangani oleh package time.

Membuat Nilai Waktu

Ada dua cara utama untuk mendapatkan nilai time.Time di Go: mengambil waktu saat ini, atau membangunnya dari komponen tertentu.

// waktu.go
package main

import (
    "fmt"
    "time"
)

func main() {
    // waktu saat ini
    sekarang := time.Now()
    fmt.Println("sekarang:", sekarang)

    // membangun waktu dari komponen
    tenggat := time.Date(2024, time.December, 31, 23, 59, 0, 0, time.UTC)
    fmt.Println("tenggat :", tenggat)
}
sekarang: 2024-03-15 09:42:11.382741 +0700 WIB m=+0.000123456
tenggat : 2024-12-31 23:59:00 +0000 UTC

time.Date menerima parameter berurutan: tahun, bulan (sebagai konstanta time.Month), hari, jam, menit, detik, nanosecond, dan lokasi timezone. time.UTC adalah lokasi bawaan; untuk timezone lokal sistem, gunakan time.Local.

Reference Time: Konvensi Unik Go

Go menggunakan pendekatan yang berbeda dari bahasa lain untuk mendefinisikan format waktu. Alih-alih placeholder seperti YYYY atau MM, Go menggunakan satu reference time yang sudah ditentukan:

Mon Jan 2 15:04:05 MST 2006

Ini bukan waktu sembarang. Setiap komponennya punya nilai yang unik dan tidak bisa tertukar:

KomponenNilaiKeterangan
Tahun2006Satu-satunya angka empat digit yang mengandung 6
Bulan (angka)01Bulan pertama dengan nol di depan
Bulan (teks)Jan / JanuaryNama bulan ke-1
Hari02Hari ke-2
Jam (24h)15Jam 3 sore dalam format 24 jam
Jam (12h)3 / 03Format 12 jam
Menit04Menit ke-4
Detik05Detik ke-5
Hari dalam semingguMon / MondayHari ke-2 dalam seminggu

Artinya, jika kamu ingin format YYYY-MM-DD, kamu tulis 2006-01-02 — bukan YYYY-MM-DD. Ini membingungkan pertama kali, tapi begitu dipahami, ia tidak pernah ambigu.

Hafalkan reference time sebagai kalimat: “Pada tanggal 2 Januari 2006, pukul 15:04

. Angka-angka itu adalah 1, 2, 3, 4, 5, 6, 7 berurutan jika ditulis 01/02 03:04:05 2006 -07.

Formatting: Waktu ke String

Method Format mengubah nilai time.Time menjadi string sesuai layout yang kamu definisikan:

// waktu.go
package main

import (
    "fmt"
    "time"
)

func main() {
    pencatatan := time.Date(2024, time.August, 17, 8, 30, 0, 0, time.UTC)

    // format tanggal saja
    fmt.Println(pencatatan.Format("2006-01-02"))

    // format dengan nama hari dan bulan
    fmt.Println(pencatatan.Format("Monday, 02 January 2006"))

    // format dengan waktu lengkap
    fmt.Println(pencatatan.Format("02 Jan 2006 — 15:04 WIB"))

    // menggunakan konstanta bawaan
    fmt.Println(pencatatan.Format(time.RFC3339))
}
2024-08-17
Saturday, 17 August 2024
17 Aug 2024 — 08:30 WIB
2024-08-17T08:30:00Z

Go menyediakan konstanta layout yang sudah jadi untuk format standar. Yang paling sering dipakai dalam API dan sistem:

  • time.RFC3339"2006-01-02T15:04:05Z07:00" — standar ISO 8601, umum di JSON API
  • time.RFC1123"Mon, 02 Jan 2006 15:04:05 MST" — standar HTTP header
  • time.DateOnly"2006-01-02" — tanggal saja (tersedia sejak Go 1.20)
  • time.TimeOnly"15:04:05" — waktu saja (tersedia sejak Go 1.20)

Parsing: String ke Waktu

time.Parse melakukan kebalikan dari Format — mengubah string menjadi time.Time. Layout yang diberikan harus sesuai dengan format string yang di-parse:

// waktu.go
package main

import (
    "fmt"
    "time"
)

func main() {
    // parsing format ISO
    tgl1, err := time.Parse("2006-01-02", "2024-06-15")
    if err != nil {
        fmt.Println("error:", err)
        return
    }
    fmt.Println("parsed:", tgl1)
    fmt.Println("hari  :", tgl1.Format("Monday"))

    // parsing dengan waktu
    tgl2, err := time.Parse("02/01/2006 15:04", "17/08/2024 08:30")
    if err != nil {
        fmt.Println("error:", err)
        return
    }
    fmt.Println("parsed:", tgl2.Format(time.RFC3339))
}
parsed: 2024-06-15 00:00:00 +0000 UTC
hari  : Saturday
parsed: 2024-08-17T08:30:00Z

time.Parse selalu mengembalikan dua nilai: time.Time dan error. Jangan abaikan errornya — jika string tidak sesuai layout, nilainya adalah zero time (0001-01-01 00:00:00) dan error akan menjelaskan ketidaksesuaiannya.

time.Parse mengasumsikan timezone UTC jika layout tidak menyertakan informasi timezone. Jika kamu mem-parse string yang mengandung timezone tertentu, sertakan komponen timezone di layout — misalnya "2006-01-02T15:04:05Z07:00" untuk RFC3339.

Operasi Perbandingan dan Selisih

Setelah memiliki nilai time.Time, kamu bisa membandingkan dan menghitung selisih antar waktu:

// waktu.go
package main

import (
    "fmt"
    "time"
)

func main() {
    mulai, _ := time.Parse("2006-01-02", "2024-01-01")
    selesai, _ := time.Parse("2006-01-02", "2024-08-17")

    durasi := selesai.Sub(mulai)

    fmt.Printf("mulai  : %s\n", mulai.Format("02 January 2006"))
    fmt.Printf("selesai: %s\n", selesai.Format("02 January 2006"))
    fmt.Printf("durasi : %.0f hari\n", durasi.Hours()/24)

    // perbandingan
    if selesai.After(mulai) {
        fmt.Println("selesai lebih baru dari mulai")
    }

    // selisih dari sekarang
    sekarang := time.Now()
    sejak := time.Since(mulai)
    fmt.Printf("sejak 1 Jan 2024: %.0f hari\n", sejak.Hours()/24)
    _ = sekarang
}
mulai  : 01 January 2024
selesai: 17 August 2024
durasi : 229 hari
selesai lebih baru dari mulai
sejak 1 Jan 2024: 74 hari

Sub mengembalikan time.Duration — tipe yang merepresentasikan selang waktu dalam nanosecond. Method .Hours(), .Minutes(), .Seconds(), dan .Milliseconds() mengonversinya ke satuan yang lebih mudah dibaca. time.Since(t) adalah shorthand untuk time.Now().Sub(t).

time.Duration: Merepresentasikan Selang Waktu

Setiap kali kamu memanggil Sub, time.Since, atau time.Until, hasilnya bertipe time.Duration. Tipe ini bukan sekadar angka — ia adalah int64 yang merepresentasikan durasi dalam satuan nanosecond.

Go mendefinisikan konstanta hierarkis untuk satuan yang lebih manusiawi:

KonstantaNilai
time.Nanosecond1
time.Microsecond1.000 nanosecond
time.Millisecond1.000 microsecond
time.Second1.000 millisecond
time.Minute60 detik
time.Hour60 menit

Konstanta-konstanta ini bisa dikombinasikan secara langsung dengan operator aritmatika:

// waktu.go
package main

import (
    "fmt"
    "time"
)

func main() {
    // membuat durasi dari kombinasi konstanta
    tenggat := 2*time.Hour + 30*time.Minute + 15*time.Second
    fmt.Println("tenggat:", tenggat)

    // konversi ke satuan berbeda
    fmt.Printf("dalam detik: %.0f\n", tenggat.Seconds())
    fmt.Printf("dalam menit: %.1f\n", tenggat.Minutes())
    fmt.Printf("dalam jam  : %.2f\n", tenggat.Hours())
}
tenggat: 2h30m15s
dalam detik: 9015
dalam menit: 150.2
dalam jam  : 2.50

Perhatikan output 2h30m15s — Go secara otomatis memformat Duration ke representasi yang ringkas dan mudah dibaca.

Aritmatika Durasi

time.Duration bisa ditambah, dikurangi, dan dibandingkan seperti angka biasa. Ini membuatnya berguna untuk menghitung selang waktu secara tepat:

// waktu.go
package main

import (
    "fmt"
    "time"
)

func main() {
    mulai := time.Now()

    // simulasi pekerjaan yang berlangsung beberapa saat
    proses1 := 450 * time.Millisecond
    proses2 := 1*time.Second + 200*time.Millisecond
    total := proses1 + proses2

    fmt.Printf("proses 1: %v\n", proses1)
    fmt.Printf("proses 2: %v\n", proses2)
    fmt.Printf("total   : %v\n", total)

    // membandingkan durasi
    batas := 2 * time.Second
    if total > batas {
        fmt.Println("peringatan: total durasi melebihi batas")
    } else {
        fmt.Printf("masih dalam batas (sisa: %v)\n", batas-total)
    }

    fmt.Printf("waktu berlalu sejak mulai: %v\n", time.Since(mulai).Round(time.Millisecond))
}
proses 1: 450ms
proses 2: 1.2s
total   : 1.65s
masih dalam batas (sisa: 350ms)
waktu berlalu sejak mulai: 0ms

Method .Round(d) membulatkan durasi ke kelipatan terdekat dari d — berguna saat mencetak durasi agar tidak menampilkan presisi nanosecond yang berlebihan.

Gunakan time.Duration untuk semua kalkulasi selang waktu, bukan operasi manual dengan angka mentah. Menulis 5 * time.Second jauh lebih aman dan terbaca dibanding menyimpan 5000 sebagai angka biasa.

Latihan

Latihan 1 — Format kustom: Buat variabel time.Time yang merepresentasikan hari kemerdekaan Indonesia (17 Agustus 1945, pukul 10

pagi). Cetak tanggal tersebut dalam tiga format berbeda: "02 January 2006", "Monday, 02/01/2006", dan time.RFC3339.

Latihan 2 — Hitung usia: Tulis fungsi hitungUsia(lahir time.Time) int yang mengembalikan usia dalam tahun penuh berdasarkan tanggal lahir yang diberikan. Pastikan fungsi ini benar untuk kasus tanggal ulang tahun yang belum terlewati di tahun berjalan.

Latihan 3 — Validasi input tanggal: Buat fungsi parseTanggal(input string) (time.Time, error) yang mencoba mem-parse string input dalam tiga format berbeda secara berurutan: "2006-01-02", "02/01/2006", dan "2 January 2006". Kembalikan waktu yang berhasil di-parse, atau error jika tidak ada format yang cocok.

Latihan 4 — Stopwatch sederhana: Buat fungsi ukurWaktu(fn func()) time.Duration yang mengukur berapa lama fungsi fn dieksekusi. Gunakan fungsi ini untuk mengukur durasi sebuah loop for i := 0; i < 1_000_000; i++ dan cetak hasilnya dalam milidetik.

Kemampuan bekerja dengan waktu sebagai nilai — menyimpan, memformat, mem-parsing, membandingkan, dan menghitung selang waktu — sudah lengkap. Tapi package time menyimpan satu dimensi lagi yang belum kita sentuh: mengeksekusi kode berdasarkan berjalannya waktu. Bukan hanya tahu jam berapa sekarang, tapi membuat program bereaksi setelah 5 detik berlalu, atau menjalankan sesuatu setiap menit. Itu yang akan kita jelajahi di bab berikutnya dengan time.Timer dan time.Ticker.

Referensi

  1. 1Package time — Go Standard Library Documentation
  2. 2time.Duration — Go Standard Library Documentation
  3. 3Time Formatting / Parsing — Go by Example