func (s *Scheduler) scheduleNextRun(job *Job) {
	now := s.now()
	lastRun := job.LastRun()
	if !s.jobPresent(job) {
		return
	}
	if job.getStartsImmediately() {
		s.run(job)
		lastRun = now
		job.setStartsImmediately(false)
	}
	if job.neverRan() {
		if !job.startAtTime.IsZero() && job.startAtTime.Before(now) {
			duration := s.durationToNextRun(job.startAtTime, job).duration
			job.startAtTime = job.startAtTime.Add(duration)
			if job.startAtTime.Before(now) {
				diff := now.Sub(job.startAtTime)
				duration := s.durationToNextRun(job.startAtTime, job).duration
				count := diff / duration
				if diff%duration != 0 {
					count++
				}
				job.startAtTime = job.startAtTime.Add(duration * count)
			}
		}
		lastRun = now
	}
	if !job.shouldRun() {
		s.RemoveByReference(job)
		return
	}
	next := s.durationToNextRun(lastRun, job)
	if next.dateTime.IsZero() {
		job.setNextRun(lastRun.Add(next.duration))
	} else {
		job.setNextRun(next.dateTime)
	}
	job.setTimer(time.AfterFunc(next.duration, func() {
		if !next.dateTime.IsZero() {
			for {
				if time.Now().Unix() >= next.dateTime.Unix() {
					break
				}
			}
		}
		s.run(job)
		s.scheduleNextRun(job)
	}))
}
scheduleNextRun Compute the instant when this Job should run next
package gocron
import (
	"context"
	"fmt"
	"reflect"
	"sort"
	"sync"
	"time"
	"github.com/robfig/cron/v3"
	"golang.org/x/sync/semaphore"
)
type limitMode int8
type Scheduler struct {
	jobsMutex sync.RWMutex
	jobs      []*Job
	locationMutex sync.RWMutex
	location      *time.Location
	runningMutex  sync.RWMutex
	running       bool  
	time     timeWrapper  
	executor *executor    
	tags sync.Map  
	tagsUnique      bool  
	updateJob       bool  
	waitForInterval bool  
	jobCreated      bool  
}
const allWeekDays = 7
func NewScheduler(loc *time.Location) *Scheduler {
	executor := newExecutor()
	return &Scheduler{
		jobs:       make([]*Job, 0),
		location:   loc,
		running:    false,
		time:       &trueTime{},
		executor:   &executor,
		tagsUnique: false,
	}
}
func (s *Scheduler) SetMaxConcurrentJobs(n int, mode limitMode) {
	s.executor.maxRunningJobs = semaphore.NewWeighted(int64(n))
	s.executor.limitMode = mode
}
func (s *Scheduler) StartBlocking() {
	s.StartAsync()
	<-make(chan bool)
}
func (s *Scheduler) StartAsync() {
	if !s.IsRunning() {
		s.start()
	}
}
func (s *Scheduler) start() {
	go s.executor.start()
	s.setRunning(true)
	s.runJobs(s.Jobs())
}
func (s *Scheduler) runJobs(jobs []*Job) {
	for _, job := range jobs {
		s.scheduleNextRun(job)
	}
}
func (s *Scheduler) setRunning(b bool) {
	s.runningMutex.Lock()
	defer s.runningMutex.Unlock()
	s.running = b
}
func (s *Scheduler) IsRunning() bool {
	s.runningMutex.RLock()
	defer s.runningMutex.RUnlock()
	return s.running
}
func (s *Scheduler) Jobs() []*Job {
	s.jobsMutex.RLock()
	defer s.jobsMutex.RUnlock()
	return s.jobs
}
func (s *Scheduler) setJobs(jobs []*Job) {
	s.jobsMutex.Lock()
	defer s.jobsMutex.Unlock()
	s.jobs = jobs
}
func (s *Scheduler) Len() int {
	s.jobsMutex.RLock()
	defer s.jobsMutex.RUnlock()
	return len(s.jobs)
}
func (s *Scheduler) Swap(i, j int) {
	s.jobsMutex.Lock()
	defer s.jobsMutex.Unlock()
	s.jobs[i], s.jobs[j] = s.jobs[j], s.jobs[i]
}
func (s *Scheduler) Less(first, second int) bool {
	return s.Jobs()[second].NextRun().Unix() >= s.Jobs()[first].NextRun().Unix()
}
func (s *Scheduler) ChangeLocation(newLocation *time.Location) {
	s.locationMutex.Lock()
	defer s.locationMutex.Unlock()
	s.location = newLocation
}
func (s *Scheduler) Location() *time.Location {
	s.locationMutex.RLock()
	defer s.locationMutex.RUnlock()
	return s.location
}
type nextRun struct {
	duration time.Duration
	dateTime time.Time
}