Reputation: 1481
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
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
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
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
Reputation: 166805
The Go Programming Language Specification
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