func (j *Job) LastRun() time.Time {
	return j.lastRun
}
LastRun returns the time the job was run last
package gocron
import (
	"context"
	"fmt"
	"sync"
	"sync/atomic"
	"time"
	"github.com/robfig/cron/v3"
	"golang.org/x/sync/singleflight"
)
type Job struct {
	mu sync.RWMutex
	jobFunction
	interval          int             
	duration          time.Duration   
	unit              schedulingUnit  
	startsImmediately bool            
	atTime            time.Duration   
	startAtTime       time.Time       
	error             error           
	lastRun           time.Time       
	nextRun           time.Time       
	scheduledWeekdays []time.Weekday  
	daysOfTheMonth    []int           
	tags              []string        
	runCount          int             
	timer             *time.Timer     
	cronSchedule      cron.Schedule   
}
type jobFunction struct {
	function   interface{}          
	parameters []interface{}        
	name       string               
	runConfig  runConfig            
	limiter    *singleflight.Group  
	ctx        context.Context      
	cancel     context.CancelFunc   
	runState   *int64               
}
type runConfig struct {
	finiteRuns bool
	maxRuns    int
	mode       mode
}
type mode int8
const (
	defaultMode mode = iota
	singletonMode
)
func newJob(interval int, startImmediately bool) *Job {
	ctx, cancel := context.WithCancel(context.Background())
	var zero int64
	return &Job{
		interval: interval,
		unit:     seconds,
		lastRun:  time.Time{},
		nextRun:  time.Time{},
		jobFunction: jobFunction{
			ctx:      ctx,
			cancel:   cancel,
			runState: &zero,
		},
		tags:              []string{},
		startsImmediately: startImmediately,
	}
}
func (j *Job) neverRan() bool {
	return j.lastRun.IsZero()
}
func (j *Job) getStartsImmediately() bool {
	return j.startsImmediately
}
func (j *Job) setStartsImmediately(b bool) {
	j.startsImmediately = b
}
func (j *Job) setTimer(t *time.Timer) {
	j.mu.Lock()
	defer j.mu.Unlock()
	j.timer = t
}
func (j *Job) getAtTime() time.Duration {
	return j.atTime
}
func (j *Job) setAtTime(t time.Duration) {
	j.atTime = t
}
func (j *Job) getStartAtTime() time.Time {
	return j.startAtTime
}
func (j *Job) setStartAtTime(t time.Time) {
	j.startAtTime = t
}
func (j *Job) getUnit() schedulingUnit {
	j.mu.RLock()
	defer j.mu.RUnlock()
	return j.unit
}
func (j *Job) setUnit(t schedulingUnit) {
	j.mu.Lock()
	defer j.mu.Unlock()
	j.unit = t
}
func (j *Job) getDuration() time.Duration {
	j.mu.RLock()
	defer j.mu.RUnlock()
	return j.duration
}
func (j *Job) setDuration(t time.Duration) {
	j.mu.Lock()
	defer j.mu.Unlock()
	j.duration = t
}
func (j *Job) Error() error {
	return j.error
}
func (j *Job) Tag(tags ...string) {
	j.tags = append(j.tags, tags...)
}
func (j *Job) Untag(t string) {
	var newTags []string
	for _, tag := range j.tags {
		if t != tag {
			newTags = append(newTags, tag)
		}
	}
	j.tags = newTags
}
func (j *Job) Tags() []string {
	return j.tags
}
func (j *Job) ScheduledTime() time.Time {
	j.mu.RLock()
	defer j.mu.RUnlock()
	return j.nextRun
}
func (j *Job) ScheduledAtTime() string {
	return fmt.Sprintf("%d:%d", j.atTime/time.Hour, (j.atTime%time.Hour)/time.Minute)
}
func (j *Job) Weekday() (time.Weekday, error) {
	if len(j.scheduledWeekdays) == 0 {
		return time.Sunday, ErrNotScheduledWeekday
	}
	return j.scheduledWeekdays[0], nil
}
func (j *Job) Weekdays() []time.Weekday {
	if len(j.scheduledWeekdays) == 0 {
		return []time.Weekday{time.Sunday}
	}
	return j.scheduledWeekdays
}
func (j *Job) LimitRunsTo(n int) {
	j.mu.Lock()
	defer j.mu.Unlock()
	j.runConfig.finiteRuns = true
	j.runConfig.maxRuns = n
}
func (j *Job) SingletonMode() {
	j.mu.Lock()
	defer j.mu.Unlock()
	j.runConfig.mode = singletonMode
	j.jobFunction.limiter = &singleflight.Group{}
}
func (j *Job) shouldRun() bool {
	j.mu.RLock()
	defer j.mu.RUnlock()
	return !j.runConfig.finiteRuns || j.runCount < j.runConfig.maxRuns
}