diff --git a/homeassistant/components/calendar/__init__.py b/homeassistant/components/calendar/__init__.py
index 0b1c37cea5f..594964c129c 100644
--- a/homeassistant/components/calendar/__init__.py
+++ b/homeassistant/components/calendar/__init__.py
@@ -67,6 +67,13 @@ SCAN_INTERVAL = datetime.timedelta(seconds=60)
 # Don't support rrules more often than daily
 VALID_FREQS = {"DAILY", "WEEKLY", "MONTHLY", "YEARLY"}
 
+# Ensure events created in Home Assistant have a positive duration
+MIN_NEW_EVENT_DURATION = datetime.timedelta(seconds=1)
+
+# Events must have a non-negative duration e.g. Google Calendar can create zero
+# duration events in the UI.
+MIN_EVENT_DURATION = datetime.timedelta(seconds=0)
+
 
 def _has_timezone(*keys: Any) -> Callable[[dict[str, Any]], dict[str, Any]]:
     """Assert that all datetime values have a timezone."""
@@ -116,17 +123,38 @@ def _as_local_timezone(*keys: Any) -> Callable[[dict[str, Any]], dict[str, Any]]
     return validate
 
 
-def _has_duration(
-    start_key: str, end_key: str
+def _has_min_duration(
+    start_key: str, end_key: str, min_duration: datetime.timedelta
 ) -> Callable[[dict[str, Any]], dict[str, Any]]:
-    """Verify that the time span between start and end is positive."""
+    """Verify that the time span between start and end has a minimum duration."""
 
     def validate(obj: dict[str, Any]) -> dict[str, Any]:
-        """Test that all keys in the dict are in order."""
         if (start := obj.get(start_key)) and (end := obj.get(end_key)):
             duration = end - start
-            if duration.total_seconds() <= 0:
-                raise vol.Invalid(f"Expected positive event duration ({start}, {end})")
+            if duration < min_duration:
+                raise vol.Invalid(
+                    f"Expected minimum event duration of {min_duration} ({start}, {end})"
+                )
+        return obj
+
+    return validate
+
+
+def _has_all_day_event_duration(
+    start_key: str,
+    end_key: str,
+) -> Callable[[dict[str, Any]], dict[str, Any]]:
+    """Modify all day events to have a duration of one day."""
+
+    def validate(obj: dict[str, Any]) -> dict[str, Any]:
+        if (
+            (start := obj.get(start_key))
+            and (end := obj.get(end_key))
+            and not isinstance(start, datetime.datetime)
+            and not isinstance(end, datetime.datetime)
+            and start == end
+        ):
+            obj[end_key] = start + datetime.timedelta(days=1)
         return obj
 
     return validate
@@ -204,8 +232,8 @@ CREATE_EVENT_SCHEMA = vol.All(
     ),
     _has_consistent_timezone(EVENT_START_DATETIME, EVENT_END_DATETIME),
     _as_local_timezone(EVENT_START_DATETIME, EVENT_END_DATETIME),
-    _has_duration(EVENT_START_DATE, EVENT_END_DATE),
-    _has_duration(EVENT_START_DATETIME, EVENT_END_DATETIME),
+    _has_min_duration(EVENT_START_DATE, EVENT_END_DATE, MIN_NEW_EVENT_DURATION),
+    _has_min_duration(EVENT_START_DATETIME, EVENT_END_DATETIME, MIN_NEW_EVENT_DURATION),
 )
 
 WEBSOCKET_EVENT_SCHEMA = vol.Schema(
@@ -221,7 +249,7 @@ WEBSOCKET_EVENT_SCHEMA = vol.Schema(
         _has_same_type(EVENT_START, EVENT_END),
         _has_consistent_timezone(EVENT_START, EVENT_END),
         _as_local_timezone(EVENT_START, EVENT_END),
-        _has_duration(EVENT_START, EVENT_END),
+        _has_min_duration(EVENT_START, EVENT_END, MIN_NEW_EVENT_DURATION),
     )
 )
 
@@ -238,7 +266,8 @@ CALENDAR_EVENT_SCHEMA = vol.Schema(
         _has_timezone("start", "end"),
         _has_consistent_timezone("start", "end"),
         _as_local_timezone("start", "end"),
-        _has_duration("start", "end"),
+        _has_min_duration("start", "end", MIN_EVENT_DURATION),
+        _has_all_day_event_duration("start", "end"),
     ),
     extra=vol.ALLOW_EXTRA,
 )
diff --git a/tests/components/caldav/test_calendar.py b/tests/components/caldav/test_calendar.py
index fc224d20685..87aec3a6f5d 100644
--- a/tests/components/caldav/test_calendar.py
+++ b/tests/components/caldav/test_calendar.py
@@ -254,6 +254,32 @@ DTEND;TZID=Europe/London:20221127T003000
 SUMMARY:Event with a provided Timezone
 END:VEVENT
 END:VCALENDAR
+""",
+    """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Global Corp.//CalDAV Client//EN
+BEGIN:VEVENT
+UID:16
+DTSTAMP:20171125T000000Z
+DTSTART:20171127
+DTEND:20171128
+SUMMARY:All day event with same start and end
+LOCATION:Hamburg
+END:VEVENT
+END:VCALENDAR
+""",
+    """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Global Corp.//CalDAV Client//EN
+BEGIN:VEVENT
+UID:17
+DTSTAMP:20171125T000000Z
+DTSTART:20171127T010000
+DTEND:20171127T010000
+SUMMARY:Event with no duration
+LOCATION:Hamburg
+END:VEVENT
+END:VCALENDAR
 """,
 ]
 
@@ -1001,7 +1027,7 @@ async def test_get_events(hass: HomeAssistant, calendar, get_api_events) -> None
     await hass.async_block_till_done()
 
     events = await get_api_events("calendar.private")
-    assert len(events) == 16
+    assert len(events) == 18
     assert calendar.call
 
 
diff --git a/tests/components/calendar/test_init.py b/tests/components/calendar/test_init.py
index 875d5bf8c13..d58932ce898 100644
--- a/tests/components/calendar/test_init.py
+++ b/tests/components/calendar/test_init.py
@@ -324,7 +324,7 @@ async def test_unsupported_create_event_service(hass: HomeAssistant) -> None:
                 "end_date_time": "2022-04-01T06:00:00",
             },
             vol.error.MultipleInvalid,
-            "Expected positive event duration",
+            "Expected minimum event duration",
         ),
         (
             {
@@ -332,7 +332,7 @@ async def test_unsupported_create_event_service(hass: HomeAssistant) -> None:
                 "end_date": "2022-04-01",
             },
             vol.error.MultipleInvalid,
-            "Expected positive event duration",
+            "Expected minimum event duration",
         ),
         (
             {
@@ -340,7 +340,7 @@ async def test_unsupported_create_event_service(hass: HomeAssistant) -> None:
                 "end_date": "2022-04-01",
             },
             vol.error.MultipleInvalid,
-            "Expected positive event duration",
+            "Expected minimum event duration",
         ),
     ],
     ids=[
diff --git a/tests/components/google/test_calendar.py b/tests/components/google/test_calendar.py
index 8b544a828e9..7d59d80687e 100644
--- a/tests/components/google/test_calendar.py
+++ b/tests/components/google/test_calendar.py
@@ -1238,3 +1238,60 @@ async def test_reader_in_progress_event(
         "location": event["location"],
         "description": event["description"],
     }
+
+
+async def test_all_day_event_without_duration(
+    hass: HomeAssistant, mock_events_list_items, component_setup
+) -> None:
+    """Test that an all day event without a duration is adjusted to have a duration of one day."""
+    week_from_today = dt_util.now().date() + datetime.timedelta(days=7)
+    event = {
+        **TEST_EVENT,
+        "start": {"date": week_from_today.isoformat()},
+        "end": {"date": week_from_today.isoformat()},
+    }
+    mock_events_list_items([event])
+
+    assert await component_setup()
+
+    expected_end_event = week_from_today + datetime.timedelta(days=1)
+
+    state = hass.states.get(TEST_ENTITY)
+    assert state.name == TEST_ENTITY_NAME
+    assert state.state == STATE_OFF
+    assert dict(state.attributes) == {
+        "friendly_name": TEST_ENTITY_NAME,
+        "message": event["summary"],
+        "all_day": True,
+        "offset_reached": False,
+        "start_time": week_from_today.strftime(DATE_STR_FORMAT),
+        "end_time": expected_end_event.strftime(DATE_STR_FORMAT),
+        "location": event["location"],
+        "description": event["description"],
+        "supported_features": 3,
+    }
+
+
+async def test_event_without_duration(
+    hass: HomeAssistant, mock_events_list_items, component_setup
+) -> None:
+    """Google calendar UI allows creating events without a duration."""
+    one_hour_from_now = dt_util.now() + datetime.timedelta(minutes=30)
+    event = {
+        **TEST_EVENT,
+        "start": {"dateTime": one_hour_from_now.isoformat()},
+        "end": {"dateTime": one_hour_from_now.isoformat()},
+    }
+    mock_events_list_items([event])
+
+    assert await component_setup()
+
+    state = hass.states.get(TEST_ENTITY)
+    assert state.name == TEST_ENTITY_NAME
+    assert state.state == STATE_OFF
+    # Confirm the event is parsed successfully, but we don't assert on the
+    # specific end date as the client library may adjust it
+    assert state.attributes.get("message") == event["summary"]
+    assert state.attributes.get("start_time") == one_hour_from_now.strftime(
+        DATE_STR_FORMAT
+    )