Repository: gobuffalo/pop (go)

Issue:
Enable customizing CreatedAt and UpdatedAt touching
## Description

To enforce consistent timestamps between databases and service instances, we want to store all timestamps in UTC. However it is not possible to enforce UTC for `UpdatedAt` because pop will overwrite the value with `time.Now()` which includes the local time zone. In contrast, `CreatedAt` is only set to `time.Now()` when it is the zero value.

### Proposals

There are a view ways I can think of we can allow this kind of customization of time stamps.

1. Allow setting the `nowFunc`. This will enable globally defining the behavior.

1. Customizing model based. Add an interface a model can implement to `touchUpdatedAt` and `touchCreatedAt`, something like
  ```go
  type UpdatedAtSetter interface{
    TouchUpdatedAt()
  }
  ```

1. Both of above options.

I'm happy to implement this if you tell me the approach you like. I personally would prefer setting the `nowFunc` globally.

Pull request description:
feat: Allow customizing the time used for CreatedAt/UpdatedAt
Provide a way to override the time used for CreatedAt/UpdatedAt. This can be really useful to make tests deterministic, but also for overriding things like the time zone for UpdatedAt

Resolves #591 



Patch:
diff --git a/model.go b/model.go
index dddbec6..7b03d78 100644
--- a/model.go
+++ b/model.go
@@ -15,6 +15,11 @@ import (
 
 var nowFunc = time.Now
 
+// SetNowFunc allows an override of time.Now for customizing CreatedAt/UpdatedAt
+func SetNowFunc(f func() time.Time) {
+	nowFunc = f
+}
+
 // Value is the contents of a `Model`.
 type Value interface{}
 


Test patch:
diff --git a/executors_test.go b/executors_test.go
index d515483..ddafdb5 100644
--- a/executors_test.go
+++ b/executors_test.go
@@ -1964,3 +1964,28 @@ func Test_Delete(t *testing.T) {
 		r.Equal(count, ctx)
 	})
 }
+
+func Test_Create_Timestamps_With_NowFunc(t *testing.T) {
+	if PDB == nil {
+		t.Skip("skipping integration tests")
+	}
+	transaction(func(tx *Connection) {
+		r := require.New(t)
+
+		originalNowFunc := nowFunc
+		// ensure the original function is restored
+		defer func() {
+			nowFunc = originalNowFunc
+		}()
+
+		fakeNow, _ := time.Parse(time.RFC3339, "2019-07-14T00:00:00Z")
+		SetNowFunc(func() time.Time { return fakeNow })
+
+		friend:= Friend{FirstName: "Yester", LastName: "Day"}
+		err := tx.Create(&friend)
+		r.NoError(err)
+
+		r.Equal(fakeNow, friend.CreatedAt)
+		r.Equal(fakeNow, friend.UpdatedAt)
+	})
+}