Running background task every head of hour

I aware we could run a task in the background with one of 2 common time schedules:

  1. After specific period of time, like after 5 minutes.

  2. Every specific interval, like every 5 minutes.

here is a simple example:

package main

import (
	"fmt"
	"time"
)

func bgTask() {
	ticker := time.NewTicker(1 * time.Second)
	for _ = range ticker.C {
		fmt.Println("Tock")
	}
}

func main() {
	fmt.Println("Go Tickers Tutorial")

	go bgTask()

	// This print statement will be executed before
	// the first `tock` prints in the console
	fmt.Println("The rest of my application can continue")

	// here we use an empty select{} in order to keep
	// our main function alive indefinitely as it would
	// complete before our backgroundTask has a chance
	// to execute if we didn't.
	select {}

}

My request is different, I want to run an activity every specific time, as every hour (12:00, 1:00, 2:00, 3:00, …), or every midnight (i.e. exactly at midnight, 00:00)

I guess you have to use github.com/robfig/cron
Code behind Cron

Thanks, I found the below code that works without 3rd party library:

package main

import (
	"context"
	"fmt"
	"time"
)

// Schedule calls function `f` with a period `p` offsetted by `o`.
func Schedule(ctx context.Context, p time.Duration, o time.Duration, f func(time.Time)) {
	// Position the first execution
	first := time.Now().Truncate(p).Add(o)
	if first.Before(time.Now()) {
		first = first.Add(p)
	}
	firstC := time.After(first.Sub(time.Now()))

	// Receiving from a nil channel blocks forever
	t := &time.Ticker{C: nil}

	for {
		select {
		case v := <-firstC:
			// The ticker has to be started before f as it can take some time to finish
			t = time.NewTicker(p)
			f(v)
		case v := <-t.C:
			f(v)
		case <-ctx.Done():
			t.Stop()
			return
		}
	}

}

func main() {
	ctx := context.Background() //  .TODO()
	fmt.Println("Let's start:", time.Now())
	current := time.Now()
	hr := current.Hour()
	fmt.Printf("The stated hour "+
		"within the day is: %v\n", hr)
	// Schedule(ctx, time.Minute*2, time.Minute, fn()) Run every 2 minutes, starting 1 minute after the first run of this code
	// if the code started 52:41.26, then first run will be at 53:00 followed by another run at 55:00, and so on

    // Schedule(ctx, time.Hour, time.Hour, func(t time.Time){}) Run every head of each hour, like 20:00:00, 21:00:00, ...
	Schedule(ctx, time.Hour, time.Hour, func(t time.Time) {

		fmt.Println("Hi, it is:", time.Now())
	})
}

Output so far:

PS D:\Desktop> go run scheduler.go
Let's start: 2022-05-18 19:04:25.9967368 +0300 +03 m=+0.001233101
The stated hour within the day is: 19
Hi, it is: 2022-05-18 20:00:00.0322164 +0300 +03 m=+3334.010983001
Hi, it is: 2022-05-18 21:00:00.0447332 +0300 +03 m=+6934.015473401
Hi, it is: 2022-05-18 22:00:00.053622 +0300 +03 m=+10534.022922201
Hi, it is: 2022-05-18 23:00:00.0611189 +0300 +03 m=+14134.020641301
Hi, it is: 2022-05-19 00:00:00.0722724 +0300 +03 m=+17734.012720101
Hi, it is: 2022-05-19 01:00:00.0873397 +0300 +03 m=+21334.023790201
Hi, it is: 2022-05-19 02:00:00.1060637 +0300 +03 m=+24934.025385401
Hi, it is: 2022-05-19 03:00:00.1107291 +0300 +03 m=+28534.025584401
Hi, it is: 2022-05-19 04:00:00.1168757 +0300 +03 m=+32134.018658401
Hi, it is: 2022-05-19 05:00:00.1334013 +0300 +03 m=+35734.024843001
Hi, it is: 2022-05-19 06:00:00.1393515 +0300 +03 m=+39334.024025801
Hi, it is: 2022-05-19 07:00:00.1362642 +0300 +03 m=+42934.015270601
Hi, it is: 2022-05-19 08:00:00.1512836 +0300 +03 m=+46534.025222701
Hi, it is: 2022-05-19 09:00:00.1654086 +0300 +03 m=+50134.022125001
Hi, it is: 2022-05-19 10:00:00.1721446 +0300 +03 m=+53734.020983001