diff --git a/conductor-client/src/main/java/com/netflix/conductor/common/run/tasks/WaitTask.java b/conductor-client/src/main/java/com/netflix/conductor/common/run/tasks/WaitTask.java
index ed134ea74..86586560b 100644
--- a/conductor-client/src/main/java/com/netflix/conductor/common/run/tasks/WaitTask.java
+++ b/conductor-client/src/main/java/com/netflix/conductor/common/run/tasks/WaitTask.java
@@ -13,9 +13,14 @@
 package com.netflix.conductor.common.run.tasks;
 
 import java.time.Duration;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
 import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeParseException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import com.netflix.conductor.common.metadata.tasks.Task;
 import com.netflix.conductor.common.metadata.tasks.TaskType;
@@ -53,8 +58,19 @@ public class WaitTask extends TypedTask {
     public static final String DURATION_INPUT = "duration";
     public static final String UNTIL_INPUT = "until";
 
-    public static final DateTimeFormatter DATE_TIME_FORMATTER =
-            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm z");
+    private static final Pattern DURATION_PATTERN = Pattern.compile(
+            "\\s*(?:(\\d+)\\s*(?:days?|d))?"
+                    + "\\s*(?:(\\d+)\\s*(?:hours?|hrs?|h))?"
+                    + "\\s*(?:(\\d+)\\s*(?:minutes?|mins?|m))?"
+                    + "\\s*(?:(\\d+)\\s*(?:seconds?|secs?|s))?"
+                    + "\\s*",
+            Pattern.CASE_INSENSITIVE);
+
+    private static final DateTimeFormatter[] DATE_TIME_FORMATTERS = {
+            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"),
+            DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm z"),
+            DateTimeFormatter.ofPattern("yyyy-MM-dd")
+    };
 
     /**
      * Represents the type of wait condition.
@@ -153,56 +169,61 @@ private Duration parseDuration(String durationStr) {
         if (durationStr == null || durationStr.isEmpty()) {
             return null;
         }
-        durationStr = durationStr.trim().toLowerCase();
-
-        if (durationStr.length() < 2) {
-            // Assume seconds if just a number
-            try {
-                return Duration.ofSeconds(Long.parseLong(durationStr));
-            } catch (NumberFormatException e) {
-                return null;
-            }
+
+        Matcher m = DURATION_PATTERN.matcher(durationStr);
+        if (!m.matches()) {
+            return null;
         }
 
-        char unit = durationStr.charAt(durationStr.length() - 1);
-        String valueStr = durationStr.substring(0, durationStr.length() - 1);
+        int days = (m.start(1) == -1 ? 0 : Integer.parseInt(m.group(1)));
+        int hours = (m.start(2) == -1 ? 0 : Integer.parseInt(m.group(2)));
+        int mins = (m.start(3) == -1 ? 0 : Integer.parseInt(m.group(3)));
+        int secs = (m.start(4) == -1 ? 0 : Integer.parseInt(m.group(4)));
 
-        try {
-            long value = Long.parseLong(valueStr);
-            switch (unit) {
-                case 's':
-                    return Duration.ofSeconds(value);
-                case 'm':
-                    return Duration.ofMinutes(value);
-                case 'h':
-                    return Duration.ofHours(value);
-                case 'd':
-                    return Duration.ofDays(value);
-                default:
-                    // If the last char is a digit, assume the whole string is seconds
-                    if (Character.isDigit(unit)) {
-                        return Duration.ofSeconds(Long.parseLong(durationStr));
-                    }
-                    return null;
-            }
-        } catch (NumberFormatException e) {
+        // If all components are zero and string is not blank, it's invalid
+        if (days == 0 && hours == 0 && mins == 0 && secs == 0 && !durationStr.trim().isEmpty()) {
             return null;
         }
+
+        return Duration.ofSeconds((days * 86400L) + (hours * 60L + mins) * 60L + secs);
     }
 
     private ZonedDateTime parseDateTime(String dateTimeStr) {
         if (dateTimeStr == null || dateTimeStr.isEmpty()) {
             return null;
         }
+
+        // Try each pattern in order (matching backend DateTimeUtils behavior)
+        // Pattern 0: "yyyy-MM-dd HH:mm" - no timezone, use system default
+        // Pattern 1: "yyyy-MM-dd HH:mm z" - with timezone
+        // Pattern 2: "yyyy-MM-dd" - date only, use system default timezone
+
+        // Try pattern with timezone first
         try {
-            return ZonedDateTime.parse(dateTimeStr, DATE_TIME_FORMATTER);
-        } catch (DateTimeParseException e) {
-            // Try ISO format as fallback
-            try {
-                return ZonedDateTime.parse(dateTimeStr);
-            } catch (DateTimeParseException e2) {
-                return null;
-            }
+            return ZonedDateTime.parse(dateTimeStr, DATE_TIME_FORMATTERS[1]);
+        } catch (DateTimeParseException ignored) {
         }
+
+        // Try datetime without timezone
+        try {
+            LocalDateTime localDateTime = LocalDateTime.parse(dateTimeStr, DATE_TIME_FORMATTERS[0]);
+            return localDateTime.atZone(ZoneId.systemDefault());
+        } catch (DateTimeParseException ignored) {
+        }
+
+        // Try date only
+        try {
+            LocalDate localDate = LocalDate.parse(dateTimeStr, DATE_TIME_FORMATTERS[2]);
+            return localDate.atStartOfDay(ZoneId.systemDefault());
+        } catch (DateTimeParseException ignored) {
+        }
+
+        // Try ISO format as fallback
+        try {
+            return ZonedDateTime.parse(dateTimeStr);
+        } catch (DateTimeParseException ignored) {
+        }
+
+        return null;
     }
 }
diff --git a/conductor-client/src/test/java/com/netflix/conductor/common/run/tasks/TypedTaskTest.java b/conductor-client/src/test/java/com/netflix/conductor/common/run/tasks/TypedTaskTest.java
index 0026ea9e7..8a8eb504d 100644
--- a/conductor-client/src/test/java/com/netflix/conductor/common/run/tasks/TypedTaskTest.java
+++ b/conductor-client/src/test/java/com/netflix/conductor/common/run/tasks/TypedTaskTest.java
@@ -153,6 +153,68 @@ void testIsWaitTask() throws IOException {
             assertFalse(WaitTask.isWaitTask(httpTask));
             assertFalse(WaitTask.isWaitTask(null));
         }
+
+        @Test
+        void testWaitDurationComplex() throws IOException {
+            Task task = loadTask("wait_duration_complex.json");
+
+            WaitTask wait = new WaitTask(task);
+
+            assertTrue(wait.isDurationBased());
+            assertEquals("1d 2h 3m 4s", wait.getDurationString());
+
+            Duration expected = Duration.ofDays(1).plusHours(2).plusMinutes(3).plusSeconds(4);
+            assertEquals(expected, wait.getDuration());
+        }
+
+        @Test
+        void testWaitDurationWithWords() throws IOException {
+            Task task = loadTask("wait_duration_words.json");
+
+            WaitTask wait = new WaitTask(task);
+
+            assertTrue(wait.isDurationBased());
+            assertEquals("2 days 5 hours 30 minutes", wait.getDurationString());
+
+            Duration expected = Duration.ofDays(2).plusHours(5).plusMinutes(30);
+            assertEquals(expected, wait.getDuration());
+        }
+
+        @Test
+        void testWaitUntilNoTimezone() throws IOException {
+            Task task = loadTask("wait_until_no_timezone.json");
+
+            WaitTask wait = new WaitTask(task);
+
+            assertTrue(wait.isUntilBased());
+            assertEquals("2025-12-31 23:59", wait.getUntilString());
+
+            ZonedDateTime until = wait.getUntil();
+            assertNotNull(until);
+            assertEquals(2025, until.getYear());
+            assertEquals(12, until.getMonthValue());
+            assertEquals(31, until.getDayOfMonth());
+            assertEquals(23, until.getHour());
+            assertEquals(59, until.getMinute());
+        }
+
+        @Test
+        void testWaitUntilDateOnly() throws IOException {
+            Task task = loadTask("wait_until_date_only.json");
+
+            WaitTask wait = new WaitTask(task);
+
+            assertTrue(wait.isUntilBased());
+            assertEquals("2025-12-31", wait.getUntilString());
+
+            ZonedDateTime until = wait.getUntil();
+            assertNotNull(until);
+            assertEquals(2025, until.getYear());
+            assertEquals(12, until.getMonthValue());
+            assertEquals(31, until.getDayOfMonth());
+            assertEquals(0, until.getHour());
+            assertEquals(0, until.getMinute());
+        }
     }
 
     @Nested
diff --git a/conductor-client/src/test/resources/tasks/wait_duration_complex.json b/conductor-client/src/test/resources/tasks/wait_duration_complex.json
new file mode 100644
index 000000000..6658ec529
--- /dev/null
+++ b/conductor-client/src/test/resources/tasks/wait_duration_complex.json
@@ -0,0 +1,11 @@
+{
+  "taskType": "WAIT",
+  "status": "IN_PROGRESS",
+  "taskId": "wait-task-002",
+  "referenceTaskName": "wait_for_complex_duration",
+  "workflowInstanceId": "workflow-002",
+  "inputData": {
+    "duration": "1d 2h 3m 4s"
+  },
+  "outputData": {}
+}
diff --git a/conductor-client/src/test/resources/tasks/wait_duration_words.json b/conductor-client/src/test/resources/tasks/wait_duration_words.json
new file mode 100644
index 000000000..ef96a8b13
--- /dev/null
+++ b/conductor-client/src/test/resources/tasks/wait_duration_words.json
@@ -0,0 +1,11 @@
+{
+  "taskType": "WAIT",
+  "status": "IN_PROGRESS",
+  "taskId": "wait-task-003",
+  "referenceTaskName": "wait_for_duration_words",
+  "workflowInstanceId": "workflow-003",
+  "inputData": {
+    "duration": "2 days 5 hours 30 minutes"
+  },
+  "outputData": {}
+}
diff --git a/conductor-client/src/test/resources/tasks/wait_until_date_only.json b/conductor-client/src/test/resources/tasks/wait_until_date_only.json
new file mode 100644
index 000000000..42bf3fbcc
--- /dev/null
+++ b/conductor-client/src/test/resources/tasks/wait_until_date_only.json
@@ -0,0 +1,11 @@
+{
+  "taskType": "WAIT",
+  "status": "IN_PROGRESS",
+  "taskId": "wait-task-005",
+  "referenceTaskName": "wait_until_date_only",
+  "workflowInstanceId": "workflow-005",
+  "inputData": {
+    "until": "2025-12-31"
+  },
+  "outputData": {}
+}
diff --git a/conductor-client/src/test/resources/tasks/wait_until_no_timezone.json b/conductor-client/src/test/resources/tasks/wait_until_no_timezone.json
new file mode 100644
index 000000000..81e1b64ab
--- /dev/null
+++ b/conductor-client/src/test/resources/tasks/wait_until_no_timezone.json
@@ -0,0 +1,11 @@
+{
+  "taskType": "WAIT",
+  "status": "IN_PROGRESS",
+  "taskId": "wait-task-004",
+  "referenceTaskName": "wait_until_no_tz",
+  "workflowInstanceId": "workflow-004",
+  "inputData": {
+    "until": "2025-12-31 23:59"
+  },
+  "outputData": {}
+}
