Repository: PrefectHQ/prefect

Issue:
Default option for DateTimeParameter
## Current behavior
You can set a default value for a `Parameter`, ie:
```
my_param = Parameter('my_param', default=None)
my_other_param = Parameter('my_other_param', default='hi')
```
but not for a `DateTimeParameter`. If nothing is supplied by the user, it will default to `None` if it's optional (`required = False`), and will raise an error if `required = True`.

When a DateTimeParameter is in a flow, it's likely that there's code later in the flow which operates on this date (eg. adding days) and which expect `DateTime` objects. Some of these operations raise errors when they're provided with a `NoneType` object. This means that if a `DateTimeParameter` is optional, it is likely to result in errors downstream. This means that we need to supply a value for all DateTimeParameters each time we run the flow (this gets cumbersome if we are testing and triggering repeated runs through the UI with the same DateTimeParameters each time).

## Proposed behavior
It would be awesome if we can set default values for `DateTimeParameter`s too which mirrors the behaviour of the defaults on `Parameter`. That is, if a `DateTimeParameter` is optional but we have provided a default, the flow can still run using this default value rather than raising an error or needing to supply values each time we run a flow. Currently, we would have to handle the `None` case when it's required (eg. `if date is None then date=...`), but this is effectively the same as setting a default. It would be better and more readable if the default was attached to the `DateTimeParameter` argument upfront.

To stay consistent with the behaviour of `Parameter`, if a `DateTimeParameter` is required and a default is provided, it will still raise a 'no parameter received' error if nothing is supplied by the user.

Ideally we could pass through a `Date` or `DateTime` object, but a string would suffice too, if JSON-serializable requirements get in the way.

## Example
```
# Ideal
from pendulum import DateTime
my_date = DateTimeParameter('my_date', default=DateTime(2020, 9, 1))
# Admitting a string would be ok too
my_other_date = DateTimeParameter('my_other_date', default='2021-01-01T00:00:00+00:00')
```



Discussion:

Resolving patch:
diff --git a/src/prefect/core/parameter.py b/src/prefect/core/parameter.py
--- a/src/prefect/core/parameter.py
+++ b/src/prefect/core/parameter.py
@@ -122,6 +122,8 @@ class DateTimeParameter(Parameter):
 
     Args:
         - name (str): the Parameter name.
+        - default (any, optional): A default value for the parameter. Must be a JSON-Serializable type,
+            and must be parseable by pendulum.
         - required (bool, optional): If True, the Parameter is required. Otherwise, it
             is optional and will return `None` if no value is provided.
         - tags ([str], optional): A list of tags for this parameter
@@ -130,10 +132,10 @@ class DateTimeParameter(Parameter):
     def __init__(
         self,
         name: str,
+        default: JSONSerializableParameterValue = no_default,
         required: bool = True,
         tags: Iterable[str] = None,
     ) -> None:
-        default = no_default if required else None
         super().__init__(name=name, default=default, required=required, tags=tags)
         self.result = PrefectResult(serializer=DateTimeSerializer())
 


Test patch:
diff --git a/tests/core/test_parameter.py b/tests/core/test_parameter.py
--- a/tests/core/test_parameter.py
+++ b/tests/core/test_parameter.py
@@ -131,3 +131,24 @@ def test_datetime_parameter_with_none_default(self):
         state = self.dt_flow.run()
         assert state.result[self.dt].result is None
         assert state.result[self.x].result is None
+
+    def test_datetime_parameter_returns_datetimes_with_default_string(self):
+        now = pendulum.now()
+        with Flow("test") as dt_flow:
+            dt = DateTimeParameter("dt", default=str(now), required=False)
+            x = self.return_value(dt)
+
+        state = dt_flow.run()
+        assert state.result[dt].result == now
+        assert state.result[x].result == now
+
+    def test_datetime_parameter_must_have_value_or_default_when_required_is_true(self):
+        with Flow("test") as dt_flow:
+            dt = DateTimeParameter("dt", required=True)
+            x = self.return_value(dt)
+
+        with pytest.raises(
+            ValueError,
+            match="Flow.run did not receive the following required parameters: dt",
+        ):
+            _ = dt_flow.run()