Repository: go-co-op/gocron
Pull request #498 (2023-05-24 11:35:21)

add Job.PreivousRun() to expose the job run time prior to LastRun()
### What does this do?

adds a new method to Job: `PreivousRun()` to expose the job run time prior to `LastRun()`

### Which issue(s) does this PR fix/relate to?
<!--- Put `Resolves #XXX` here to auto-close the issue that your PR fixes (if such) --->


### List any changes that modify/break current functionality


### Have you included tests for your changes?


### Did you document any new/modified functionality?

- [ ] Updated `example_test.go`
- [ ] Updated `README.md`

### Notes



Patch:
diff --git a/job.go b/job.go
index 87918fa..fdada82 100644
--- a/job.go
+++ b/job.go
@@ -24,6 +24,7 @@ type Job struct {
 	atTimes           []time.Duration // optional time(s) at which this Job runs when interval is day
 	startAtTime       time.Time       // optional time at which the Job starts
 	error             error           // error related to Job
+	previousRun       time.Time       // datetime of the run before last run
 	lastRun           time.Time       // datetime of last run
 	nextRun           time.Time       // datetime of next run
 	scheduledWeekdays []time.Weekday  // Specific days of the week to start on
@@ -435,6 +436,7 @@ func (j *Job) LastRun() time.Time {
 }
 
 func (j *Job) setLastRun(t time.Time) {
+	j.previousRun = j.lastRun
 	j.lastRun = t
 }
 
@@ -452,6 +454,13 @@ func (j *Job) setNextRun(t time.Time) {
 	j.jobFunction.jobFuncNextRun = t
 }
 
+// PreviousRun returns the job run time previous to LastRun
+func (j *Job) PreviousRun() time.Time {
+	j.mu.RLock()
+	defer j.mu.RUnlock()
+	return j.previousRun
+}
+
 // RunCount returns the number of times the job has been started
 func (j *Job) RunCount() int {
 	j.mu.Lock()
@@ -498,6 +507,7 @@ func (j *Job) copy() Job {
 		error:             j.error,
 		lastRun:           j.lastRun,
 		nextRun:           j.nextRun,
+		previousRun:       j.previousRun,
 		scheduledWeekdays: j.scheduledWeekdays,
 		daysOfTheMonth:    j.daysOfTheMonth,
 		tags:              j.tags,
diff --git a/scheduler.go b/scheduler.go
index 583951e..50fa7d1 100644
--- a/scheduler.go
+++ b/scheduler.go
@@ -600,6 +600,9 @@ func (s *Scheduler) runContinuous(job *Job) {
 	if !job.getStartsImmediately() {
 		job.setStartsImmediately(true)
 	} else {
+		//if job.neverRan() {
+		//	job.setLastRun(s.now())
+		//}
 		s.run(job)
 	}
 	nr := next.dateTime.Sub(s.now())


Test patch:
diff --git a/example_test.go b/example_test.go
index e7b8cd0..229c64d 100644
--- a/example_test.go
+++ b/example_test.go
@@ -84,6 +84,14 @@ func ExampleJob_NextRun() {
 	s.StartAsync()
 }
 
+func ExampleJob_PreviousRun() {
+	s := gocron.NewScheduler(time.UTC)
+	job, _ := s.Every(1).Second().Do(task)
+	s.StartAsync()
+
+	fmt.Println("Previous run:", job.PreviousRun())
+}
+
 func ExampleJob_RunCount() {
 	s := gocron.NewScheduler(time.UTC)
 	job, _ := s.Every(1).Second().Do(task)
diff --git a/job_test.go b/job_test.go
index 5afacbb..6483a85 100644
--- a/job_test.go
+++ b/job_test.go
@@ -285,3 +285,40 @@ func TestJob_Stop(t *testing.T) {
 		assert.True(t, errors.Is(jobCtx.Err(), context.Canceled))
 	})
 }
+
+func TestJob_PreviousRun(t *testing.T) {
+	t.Run("at time", func(t *testing.T) {
+		s := NewScheduler(time.UTC)
+		atTime := time.Now().UTC().Add(time.Millisecond * 150)
+		_, err := s.Every(1).Day().At(atTime).DoWithJobDetails(func(job Job) {
+			assert.Zero(t, job.PreviousRun())
+			assert.Equal(t, atTime.Truncate(time.Second), job.LastRun())
+		})
+		require.NoError(t, err)
+		s.StartAsync()
+		time.Sleep(500 * time.Millisecond)
+		s.Stop()
+	})
+
+	t.Run("duration", func(t *testing.T) {
+		s := NewScheduler(time.UTC)
+		var counter int
+		var lastRunTime time.Time
+		var mu sync.Mutex
+		_, err := s.Every("250ms").DoWithJobDetails(func(job Job) {
+			mu.Lock()
+			defer mu.Unlock()
+			if counter == 0 {
+				assert.Zero(t, job.PreviousRun())
+				lastRunTime = job.LastRun()
+			} else {
+				assert.Equalf(t, lastRunTime, job.PreviousRun(), "last lastRun and previous")
+			}
+			counter++
+		})
+		require.NoError(t, err)
+		s.StartAsync()
+		time.Sleep(450 * time.Millisecond)
+		s.Stop()
+	})
+}