rellocs wood
rellocs wood

Reputation: 1481

How to gracefully iterate a date range in Go

I need to iterate any specified dates range, say 2017-12-21 to 2018-01-05. What is the best way to implement that in Go?

output:
20171221
20171222
20171223
...
20180104
20180105

Upvotes: 13

Views: 18003

Answers (4)

Orkhan Alikhanov
Orkhan Alikhanov

Reputation: 10060

With go 1.23 we can use iterators to achieve it:

func iterateDays(startDate, endDate time.Time) iter.Seq[time.Time] {
    return func(yield func(time.Time) bool) {
        currentDate := startDate
        for !currentDate.After(endDate) {
            if !yield(currentDate) {
                return
            }

            currentDate = currentDate.AddDate(0, 0, 1)
        }
    }
}

Usage:

start := time.Now()
end := time.Now().AddDate(0, 0, 3)

for date := range iterateDays(start, end) {
    fmt.Println(date.Format(time.RFC3339))
}

Playground: https://go.dev/play/p/fSEpX0QSAL9

Upvotes: 0

Pavani siva dath
Pavani siva dath

Reputation: 495

we can implement generator pattern for such use case, for simplicity I've used buffered channel to avoid routine leak.

Ref: oreilly: concurrency-in-go for more understanding playground link

// dates generates dates between start and end time (both included).
func dates(start, end time.Time) (dates <-chan time.Time) {
y, m, d := start.Date()
start = time.Date(y, m, d, 0, 0, 0, 0, time.UTC)
y, m, d = end.Date()
end = time.Date(y, m, d, 0, 0, 0, 0, time.UTC)
days := int(end.Sub(start).Hours()/24) + 1 // +1 to include both start and end date
if days < 0 {
    days = 0
}
ch := make(chan time.Time, days)
go func() {
    defer close(ch)
    for ;!start.After(end); start = start.AddDate(0,0,1){
        ch <- start
    }
}()
return ch
}

Upvotes: 0

thenguyenit
thenguyenit

Reputation: 789

start := time.Now()
end := start.AddDate(0, 1, 0)
for d := start; d.After(end) == false; d = d.AddDate(0, 0, 1) {
    fmt.Println(d.Format("2006-01-02"))
}

Go play: https://play.golang.org/p/cFcaf-yfo0n

Upvotes: 59

peterSO
peterSO

Reputation: 166805

The Go Programming Language Specification

Function literals

A function literal represents an anonymous function.

FunctionLit = "func" Signature FunctionBody .

func(a, b int, z float64) bool { return a*b < int(z) }

A function literal can be assigned to a variable or invoked directly.

f := func(x, y int) int { return x + y }
func(ch chan int) { ch <- ACK }(replyChan)

Function literals are closures: they may refer to variables defined in a surrounding function. Those variables are then shared between the surrounding function and the function literal, and they survive as long as they are accessible.


In Go, encapsulate complexity in functions. Use a function literal as a closure.

For example,

package main

import (
    "fmt"
    "time"
)

// rangeDate returns a date range function over start date to end date inclusive.
// After the end of the range, the range function returns a zero date,
// date.IsZero() is true.
func rangeDate(start, end time.Time) func() time.Time {
    y, m, d := start.Date()
    start = time.Date(y, m, d, 0, 0, 0, 0, time.UTC)
    y, m, d = end.Date()
    end = time.Date(y, m, d, 0, 0, 0, 0, time.UTC)

    return func() time.Time {
        if start.After(end) {
            return time.Time{}
        }
        date := start
        start = start.AddDate(0, 0, 1)
        return date
    }
}

func main() {
    start := time.Now()
    end := start.AddDate(0, 0, 6)
    fmt.Println(start.Format("2006-01-02"), "-", end.Format("2006-01-02"))

    for rd := rangeDate(start, end); ; {
        date := rd()
        if date.IsZero() {
            break
        }
        fmt.Println(date.Format("2006-01-02"))
    }
}

Playground: https://play.golang.org/p/wmfQC9fEs1S

Output:

2018-06-22 - 2018-06-28
2018-06-22
2018-06-23
2018-06-24
2018-06-25
2018-06-26
2018-06-27
2018-06-28

Upvotes: 12

Related Questions