diff --git a/lib/modules/datasource/endoflife-date/index.spec.ts b/lib/modules/datasource/endoflife-date/index.spec.ts
index 6e5bc4512324081424745c474409f448dadfb908..b564776ba189667cfb18d02a91f15212d813c240 100644
--- a/lib/modules/datasource/endoflife-date/index.spec.ts
+++ b/lib/modules/datasource/endoflife-date/index.spec.ts
@@ -1,3 +1,4 @@
+import { DateTime, Settings } from 'luxon';
 import { getPkgReleases } from '..';
 import { Fixtures } from '../../../../test/fixtures';
 import * as httpMock from '../../../../test/http-mock';
@@ -12,6 +13,11 @@ const packageName = 'amazon-eks';
 const eksMockPath = `/${packageName}.json`;
 
 describe('modules/datasource/endoflife-date/index', () => {
+  beforeAll(() => {
+    const now = DateTime.fromISO('2023-06-03');
+    Settings.now = () => now.valueOf();
+  });
+
   describe('getReleases', () => {
     it('processes real data', async () => {
       httpMock
diff --git a/lib/modules/datasource/endoflife-date/index.ts b/lib/modules/datasource/endoflife-date/index.ts
index 9eba4d50eb7965c43419b085ba00d82c48f23e3e..3e4e14d49311f2162fd7738c7e5f65085715b4a1 100644
--- a/lib/modules/datasource/endoflife-date/index.ts
+++ b/lib/modules/datasource/endoflife-date/index.ts
@@ -5,7 +5,7 @@ import { joinUrlParts } from '../../../util/url';
 import { Datasource } from '../datasource';
 import type { GetReleasesConfig, ReleaseResult } from '../types';
 import { datasource, registryUrl } from './common';
-import { EndoflifeHttpResponseScheme } from './schema';
+import { EndoflifeDateVersions } from './schema';
 
 export class EndoflifeDatePackagesource extends Datasource {
   static readonly id = datasource;
@@ -41,10 +41,7 @@ export class EndoflifeDatePackagesource extends Datasource {
     const url = joinUrlParts(registryUrl, `${packageName}.json`);
 
     try {
-      const response = await this.http.getJson(
-        url,
-        EndoflifeHttpResponseScheme
-      );
+      const response = await this.http.getJson(url, EndoflifeDateVersions);
 
       result.releases.push(...response.body);
 
diff --git a/lib/modules/datasource/endoflife-date/schema.ts b/lib/modules/datasource/endoflife-date/schema.ts
index 38b58c98e5e56a286a8b312c137a9058cd7fcb4b..ac4eabdfe85fc154c7bc417feeaee8b2163bc714 100644
--- a/lib/modules/datasource/endoflife-date/schema.ts
+++ b/lib/modules/datasource/endoflife-date/schema.ts
@@ -1,42 +1,35 @@
 import { DateTime } from 'luxon';
 import { z } from 'zod';
+import { UtcDate } from '../../../util/schema-utils';
 import type { Release } from '../types';
 
-const EndoflifeDateVersionScheme = z
+const ExpireableField = z.union([
+  UtcDate.transform((x) => {
+    const now = DateTime.now().toUTC();
+    return x <= now;
+  }),
+  z.boolean(),
+]);
+
+export const EndoflifeDateVersions = z
   .object({
     cycle: z.string(),
     latest: z.optional(z.string()),
     releaseDate: z.optional(z.string()),
-    eol: z.optional(z.union([z.string(), z.boolean()])),
-    discontinued: z.optional(z.union([z.string(), z.boolean()])),
+    eol: z.optional(ExpireableField),
+    discontinued: z.optional(ExpireableField),
   })
-  .transform(({ cycle, latest, releaseDate, eol, discontinued }): Release => {
-    let isDeprecated = false;
-
-    // If "eol" date or "discontinued" date has passed or any of the values is explicitly true, set to deprecated
-    // "support" is not checked because support periods sometimes end before the EOL.
-    if (
-      eol === true ||
-      discontinued === true ||
-      (typeof eol === 'string' &&
-        DateTime.fromISO(eol, { zone: 'utc' }) <= DateTime.now().toUTC()) ||
-      (typeof discontinued === 'string' &&
-        DateTime.fromISO(discontinued, { zone: 'utc' }) <=
-          DateTime.now().toUTC())
-    ) {
-      isDeprecated = true;
-    }
-
-    let version = cycle;
-    if (latest !== undefined) {
-      version = latest;
+  .transform(
+    ({
+      cycle,
+      latest,
+      releaseDate: releaseTimestamp,
+      eol,
+      discontinued,
+    }): Release => {
+      const version = latest ?? cycle;
+      const isDeprecated = eol === true || discontinued === true;
+      return { version, releaseTimestamp, isDeprecated };
     }
-
-    return {
-      version,
-      releaseTimestamp: releaseDate,
-      isDeprecated,
-    };
-  });
-
-export const EndoflifeHttpResponseScheme = z.array(EndoflifeDateVersionScheme);
+  )
+  .array();
diff --git a/lib/util/schema-utils.spec.ts b/lib/util/schema-utils.spec.ts
index 45ef6761229fe0ff7136c93abd44c9908d0fb02b..f62fcc4bbffe08a3898a5ae1f455f93d5f8d1f21 100644
--- a/lib/util/schema-utils.spec.ts
+++ b/lib/util/schema-utils.spec.ts
@@ -1,5 +1,5 @@
 import { z } from 'zod';
-import { Json, Json5, LooseArray, LooseRecord } from './schema-utils';
+import { Json, Json5, LooseArray, LooseRecord, UtcDate } from './schema-utils';
 
 describe('util/schema-utils', () => {
   describe('LooseArray', () => {
@@ -258,4 +258,16 @@ describe('util/schema-utils', () => {
       });
     });
   });
+
+  describe('UtcDate', () => {
+    it('parses date', () => {
+      expect(UtcDate.parse('2020-04-04').toString()).toBe(
+        '2020-04-04T00:00:00.000Z'
+      );
+    });
+
+    it('rejects invalid date', () => {
+      expect(() => UtcDate.parse('foobar')).toThrow();
+    });
+  });
 });
diff --git a/lib/util/schema-utils.ts b/lib/util/schema-utils.ts
index f7d7c0985104ee97fc69110a6d7a2eb720b2273e..f3dfe108ce7a508901156887e21ceaae6d61d11f 100644
--- a/lib/util/schema-utils.ts
+++ b/lib/util/schema-utils.ts
@@ -1,4 +1,5 @@
 import JSON5 from 'json5';
+import { DateTime } from 'luxon';
 import type { JsonValue } from 'type-fest';
 import { z } from 'zod';
 
@@ -211,3 +212,14 @@ export const Json5 = z.string().transform((str, ctx): JsonValue => {
     return z.NEVER;
   }
 });
+
+export const UtcDate = z
+  .string({ description: 'ISO 8601 string' })
+  .transform((str, ctx): DateTime => {
+    const date = DateTime.fromISO(str, { zone: 'utc' });
+    if (!date.isValid) {
+      ctx.addIssue({ code: 'custom', message: 'Invalid date' });
+      return z.NEVER;
+    }
+    return date;
+  });