strings and time
strings Package
Section titled “strings Package”The strings package provides functions for manipulating UTF-8 encoded strings. Strings in Go are immutable byte slices — all operations return new strings rather than modifying in place.
Searching
Section titled “Searching”strings.Contains("hello world", "world") // truestrings.HasPrefix("hello", "hel") // truestrings.HasSuffix("hello", "llo") // truestrings.Index("hello", "ll") // 2strings.LastIndex("hello", "l") // 3strings.Count("hello", "l") // 2Index returns -1 if the substring is not found. Count counts non-overlapping occurrences.
Splitting and Joining
Section titled “Splitting and Joining”parts := strings.Split("a,b,c", ",") // ["a", "b", "c"]parts := strings.SplitN("a,b,c", ",", 2) // ["a", "b,c"]result := strings.Join(parts, "-") // "a-b-c"fields := strings.Fields(" hello world ") // ["hello", "world"]SplitN limits the number of splits. Fields splits on whitespace and discards empty strings.
Trimming and Case
Section titled “Trimming and Case”strings.TrimSpace(" hello ") // "hello"strings.Trim("!!!hello!!!", "!") // "hello"strings.TrimPrefix("hello", "hel") // "lo"strings.TrimSuffix("hello", "llo") // "hel"strings.ToUpper("hello") // "HELLO"strings.ToLower("HELLO") // "hello"Replace and Repeat
Section titled “Replace and Repeat”strings.Replace("foo bar foo", "foo", "baz", 1) // "baz bar foo"strings.ReplaceAll("foo bar foo", "foo", "baz") // "baz bar baz"strings.Repeat("ab", 3) // "ababab"Replace takes a count argument — -1 replaces all occurrences (same as ReplaceAll).
strings.Builder
Section titled “strings.Builder”For efficient string concatenation in loops, use strings.Builder instead of repeated +=:
var b strings.Builderfor _, s := range items { b.WriteString(s) b.WriteByte(",')}result := b.String()Builder avoids allocating a new string on each concatenation. Grow(n) pre-allocates capacity:
var b strings.Builderb.Grow(1000)for _, s := range items { b.WriteString(s)}strconv Package
Section titled “strconv Package”The strconv package converts between strings and other types:
n, err := strconv.Atoi("42") // int 42s := strconv.Itoa(42) // "42"s := strconv.FormatBool(true) // "true"s := strconv.FormatInt(-42, 10) // "-42"s := strconv.FormatInt(255, 16) // "ff"s := strconv.FormatFloat(3.14, 'f', 2, 64) // "3.14"
f, err := strconv.ParseFloat("3.14", 64) // 3.14i, err := strconv.ParseInt("ff", 16, 64) // 255
s := strconv.Quote("hello \"world\"") // "hello \"world\""s, err := strconv.Unquote(`"hello"`) // "hello"Atoi is shorthand for ParseInt(s, 10, 0). Always check the returned error.
time Package
Section titled “time Package”Creating and Reading time.Time
Section titled “Creating and Reading time.Time”t := time.Now() // current local timet := time.Date(2026, 5, 30, 14, 30, 0, 0, time.UTC)
fmt.Println(t.Year()) // 2026fmt.Println(t.Month()) // Mayfmt.Println(t.Day()) // 30fmt.Println(t.Hour()) // 14fmt.Println(t.Minute()) // 30fmt.Println(t.Second()) // 0fmt.Println(t.Unix()) // seconds since epochfmt.Println(t.UnixNano()) // nanoseconds since epochfmt.Println(t.Weekday()) // Saturdayfmt.Println(t.IsZero()) // falseParsing
Section titled “Parsing”layout := "2006-01-02 15:04:05"t, err := time.Parse(layout, "2026-05-30 14:30:00")Go uses a reference date for layout strings. The reference date is Mon Jan 2 15:04:05 MST 2006 (which is 01/02 03:04:05 PM 06 -0700 in a more memorable form). Each component determines how the corresponding value is interpreted.
Common layouts:
time.RFC3339 // "2006-01-02T15:04:05Z07:00"time.RFC3339Nano // "2006-01-02T15:04:05.999999999Z07:00"time.RFC1123 // "Mon, 02 Jan 2006 15:04:05 MST"time.Kitchen // "3:04PM"time.RubyDate // "2006-01-02 15:04:05 -0700"time.UnixDate // "Mon Jan 2 15:04:05 MST 2006"Formatting
Section titled “Formatting”t := time.Now()fmt.Println(t.Format(time.RFC3339)) // "2026-05-30T14:30:00Z"fmt.Println(t.Format("2006-01-02")) // "2026-05-30"fmt.Println(t.Format("Jan _2, 2006")) // "May 30, 2026"fmt.Println(t.Format("15:04:05")) // "14:30:00"Duration
Section titled “Duration”time.Duration is an int64 representing nanoseconds. Constants help construct durations:
d := 2 * time.Hour + 30 * time.Minute // 2h30m0sd := time.Millisecond * 500 // 500msd := time.Duration(5) * time.Second // 5s
fmt.Println(d) // "2h30m0s"fmt.Println(d.Hours()) // 2.5fmt.Println(d.Minutes()) // 150fmt.Println(d.Seconds()) // 9000fmt.Println(d.Milliseconds()) // 9000000Time Arithmetic
Section titled “Time Arithmetic”t := time.Now()future := t.Add(24 * time.Hour) // 24 hours from nowpast := t.Add(-24 * time.Hour) // 24 hours ago
diff := future.Sub(t) // 24h0m0sfmt.Println(diff.Hours()) // 24
t1 := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)t2 := time.Date(2026, 12, 31, 0, 0, 0, 0, time.UTC)t1.Before(t2) // truet1.After(t2) // falset2.Equal(t1) // false
since := time.Since(t1) // duration since t1until := time.Until(t2) // duration until t2Timers and Tickers
Section titled “Timers and Tickers”time.Timer
Section titled “time.Timer”A Timer fires once after a specified duration:
timer := time.NewTimer(2 * time.Second)<-timer.C // blocks for 2 seconds, then receives the current timeStop a timer before it fires:
timer := time.NewTimer(5 * time.Second)stopped := timer.Stop()if stopped { fmt.Println("timer was stopped before firing")}If the timer has already fired or been stopped, Stop() returns false. Always drain the channel if Stop() returns false:
if !timer.Stop() { <-timer.C}time.Ticker
Section titled “time.Ticker”A Ticker fires repeatedly at a fixed interval:
ticker := time.NewTicker(1 * time.Second)defer ticker.Stop()
for i := 0; i < 5; i++ { <-ticker.C fmt.Println("tick", time.Now().Format("15:04:05"))}Always stop tickers with defer ticker.Stop() to prevent goroutine leaks.
time.After and time.AfterFunc
Section titled “time.After and time.AfterFunc”// After: fires once, returns a channel<-time.After(2 * time.Second) // blocks for 2 seconds
// AfterFunc: fires once, calls a functiontimer := time.AfterFunc(2*time.Second, func() { fmt.Println("2 seconds elapsed")})timer.Stop() // canceltime.After is convenient but note that it creates a timer that is not garbage-collected until it fires. Avoid time.After in tight loops — use time.NewTimer instead.
Time Zones
Section titled “Time Zones”loc, err := time.LoadLocation("America/New_York")if err != nil { log.Fatal(err)}
t := time.Date(2026, 5, 30, 14, 30, 0, 0, loc)fmt.Println(t) // 2026-05-30 14:30:00 -0400 EDT
utc := t.UTC() // convert to UTClocal := t.Local() // convert to localback := utc.In(loc) // convert to specific locationtime.UTC is a pre-defined location for UTC. time.Local is the system’s local time zone.
Comparisons: always compare times in the same zone. Equal compares the absolute instant regardless of location:
t1 := time.Date(2026, 5, 30, 14, 0, 0, 0, time.UTC)t2 := time.Date(2026, 5, 30, 10, 0, 0, 0, loc) // same instant, different zonet1.Equal(t2) // trueIntuition
Section titled “Intuition”Strings are immutable bricks, Builder is the mortar: In Go, strings are like sealed glass bottles — you can’t change the liquid inside, but you can glue bottles together (which creates a new bottle each time). strings.Builder is like a bucket where you pour liquids together, creating one final bottle at the end. The time package’s layout system is a fill-in-the-blank form: the reference date 01/02 03:04:05 PM '06 tells the parser which blank is month, which is day, which is hour.
Why it matters: String concatenation in loops with += creates O(n²) allocations. strings.Builder makes it O(n). Time parsing with the wrong layout silently produces wrong dates — the reference date convention prevents this once you internalize it.
The key insight: Go’s time layout uses a memorable reference moment (January 2, 3:04:05 PM 2006) so that every position in the format string corresponds to the value that belongs there.
Common Pitfalls
Section titled “Common Pitfalls”Using += for string concatenation in loops. Each
+=allocates a new string. Usestrings.Builderfor repeated concatenation.Not checking strconv errors.
AtoiandParseIntreturn errors for invalid input. Ignoring the error yields a zero value, which can silently corrupt data.Using the wrong layout string for time.Parse. The layout must use the reference date
2006-01-02 15:04:05. Using other values (e.g.,2015-01-02) causes incorrect parsing.Comparing times with ==. Use
t1.Equal(t2)instead. Twotime.Timevalues in different locations representing the same instant are not==, butEqualreturnstrue.Leaking timers and tickers.
time.NewTimerandtime.NewTickercreate resources that must be stopped. Failing to callStop()leaks a goroutine. Always usedefer.Using time.After in a loop. Each call creates a new timer that cannot be stopped. In a loop, this leaks timers. Use
time.NewTimerwithStop()instead.Ignoring time zone in parsing.
time.Parsewithout a time zone offset produces a time with no location (UTC +0). Usetime.ParseInLocationwhen the input has a known time zone but no offset:
loc, _ := time.LoadLocation("America/New_York")t, _ := time.ParseInLocation("2006-01-02 15:04", "2026-05-30 14:30", loc)Summary
Section titled “Summary”This topic covers the core concepts of strings and time in Go, including underlying theory, practical implementation, and key applications.
Key concepts include:
- string manipulation and efficient concatenation
- type conversions with strconv
- time parsing, formatting, and arithmetic
- timers, tickers, and time zones
- common pitfalls and best practices
Understanding these concepts thoroughly is essential for both examinations and practical programming, and requires both theoretical knowledge and hands-on practice.
Worked Examples
Section titled “Worked Examples”Worked examples demonstrating the application of key concepts are covered in the detailed sub-pages linked above.
Cross-References
Section titled “Cross-References”- I/O: Buffered I/O and stream processing for string manipulation.
- net/http: HTTP header parsing and time-based cache headers.
- Types and Variables: String and time type fundamentals.