diff --git a/.loki/reference/chrome_laptop_static_viz_ComboChart_Line_Replace_Missing_Values_Zero.png b/.loki/reference/chrome_laptop_static_viz_ComboChart_Line_Replace_Missing_Values_Zero.png
new file mode 100644
index 0000000000000000000000000000000000000000..c6ac20f1b9fa7c8398b8c1f02baf1eb682c847a8
Binary files /dev/null and b/.loki/reference/chrome_laptop_static_viz_ComboChart_Line_Replace_Missing_Values_Zero.png differ
diff --git a/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Combo.png b/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Combo.png
index 4b2568d86451fd35fc648fd616a5a599366a544c..85cb90c2d43e23a8839e219509bdffebae17321e 100644
Binary files a/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Combo.png and b/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Combo.png differ
diff --git a/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Combo_Log.png b/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Combo_Log.png
index 69d8cbf2dac9c95e4ac376f4792827e29e05e1f3..e86878a5683d1abfab9f36cc930fa7256d8b970b 100644
Binary files a/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Combo_Log.png and b/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Combo_Log.png differ
diff --git a/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Combo_Power.png b/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Combo_Power.png
index 2e347935ba95e74d0081449ecfa0dd8855cc8a48..8055b4ce7888c9726003b51211f881ae9fb2beda 100644
Binary files a/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Combo_Power.png and b/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Combo_Power.png differ
diff --git a/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Multi_Series_Area.png b/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Multi_Series_Area.png
index 72676fb8bc0b828ae484a0dd6e7b21e5fddce111..2785dcd9bd70462860f9db0bae15fbd8d31bdbf0 100644
Binary files a/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Multi_Series_Area.png and b/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Multi_Series_Area.png differ
diff --git a/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Multi_Series_Line.png b/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Multi_Series_Line.png
index 25b7fa73a8692a69d605c6ed4660d8be1f4535f1..d88a85438cbe66b1360adaeb2fc0db43ae9db622 100644
Binary files a/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Multi_Series_Line.png and b/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Multi_Series_Line.png differ
diff --git a/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Multi_Series_Normalized_Stacked_Area.png b/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Multi_Series_Normalized_Stacked_Area.png
index fd1101ca108f211aa12b65abdbead2b02a120c59..ccc89a4bd3e6b9a2ded420c7bc546491d6ff55cf 100644
Binary files a/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Multi_Series_Normalized_Stacked_Area.png and b/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Multi_Series_Normalized_Stacked_Area.png differ
diff --git a/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Multi_Series_Normalized_Stacked_Bar.png b/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Multi_Series_Normalized_Stacked_Bar.png
index 63e03afedac215284b32670ff6615416649fa27c..ce4cba1653b0215be8b1514ed80fc256c8e6c979 100644
Binary files a/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Multi_Series_Normalized_Stacked_Bar.png and b/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Multi_Series_Normalized_Stacked_Bar.png differ
diff --git a/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Multi_Series_Stacked_Area.png b/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Multi_Series_Stacked_Area.png
index e530249218408c21ad03deb6d26e73b16ea86661..5ee3084d007da6a6645cc2ddd3c0df5d4dae7b3f 100644
Binary files a/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Multi_Series_Stacked_Area.png and b/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Multi_Series_Stacked_Area.png differ
diff --git a/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Single_Series_Area.png b/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Single_Series_Area.png
index 3f36750790d58ccee50c268d918108a1fa2198d7..f26d5b6f954188928173b64f6de39158d72c6bc6 100644
Binary files a/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Single_Series_Area.png and b/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Single_Series_Area.png differ
diff --git a/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Single_Series_Line.png b/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Single_Series_Line.png
index 4e283cad217cec61c303bfc172b7c7b94a71c1e9..dfd36aaadfcf5b126b5c38ba3836ee9f7ae49808 100644
Binary files a/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Single_Series_Line.png and b/.loki/reference/chrome_laptop_static_viz_ComboChart_Trend_Single_Series_Line.png differ
diff --git a/frontend/src/metabase/static-viz/components/ComboChart/ComboChart.stories.tsx b/frontend/src/metabase/static-viz/components/ComboChart/ComboChart.stories.tsx
index 0d8f2c7d29624eb379ba9112a08f2fe132464fa7..1f963ffaa6283e183efe8db1dc835adf41e70038 100644
--- a/frontend/src/metabase/static-viz/components/ComboChart/ComboChart.stories.tsx
+++ b/frontend/src/metabase/static-viz/components/ComboChart/ComboChart.stories.tsx
@@ -604,6 +604,7 @@ HistogramTicks45Degrees.args = {
   dashcardSettings: {},
   renderingContext,
 };
+
 export const HistogramTicks90Degrees = Template.bind({});
 HistogramTicks90Degrees.args = {
   rawSeries: data.histogramTicks90Degrees as any,
@@ -618,6 +619,13 @@ LineUnpinFromZero.args = {
   renderingContext,
 };
 
+export const LineReplaceMissingValuesZero = Template.bind({});
+LineReplaceMissingValuesZero.args = {
+  rawSeries: data.lineReplaceMissingValuesZero as any,
+  dashcardSettings: {},
+  renderingContext,
+};
+
 export const Default = Template.bind({});
 Default.args = {
   rawSeries: data.messedUpAxis as any,
diff --git a/frontend/src/metabase/static-viz/components/ComboChart/stories-data/index.ts b/frontend/src/metabase/static-viz/components/ComboChart/stories-data/index.ts
index 13360c413038e514d83d05c591892d519d2e19d7..563168b4c7d53a775d181d1f47324ad14df8c6b6 100644
--- a/frontend/src/metabase/static-viz/components/ComboChart/stories-data/index.ts
+++ b/frontend/src/metabase/static-viz/components/ComboChart/stories-data/index.ts
@@ -51,6 +51,7 @@ import lineLinearXScaleUnsorted from "./line-linear-x-scale-unsorted.json";
 import lineLinearXScale from "./line-linear-x-scale.json";
 import lineLogYScaleNegative from "./line-log-y-scale-negative.json";
 import lineLogYScale from "./line-log-y-scale.json";
+import lineReplaceMissingValuesZero from "./line-replace-missing-values-zero.json";
 import lineShowDotsAuto from "./line-show-dots-auto.json";
 import lineShowDotsOff from "./line-show-dots-off.json";
 import lineShowDotsOn from "./line-show-dots-on.json";
@@ -170,4 +171,5 @@ export const data = {
   histogramTicks45Degrees,
   histogramTicks90Degrees,
   lineUnpinFromZero,
+  lineReplaceMissingValuesZero,
 };
diff --git a/frontend/src/metabase/static-viz/components/ComboChart/stories-data/line-replace-missing-values-zero.json b/frontend/src/metabase/static-viz/components/ComboChart/stories-data/line-replace-missing-values-zero.json
new file mode 100644
index 0000000000000000000000000000000000000000..5faf44b78be236b9600273c5347927879549213c
--- /dev/null
+++ b/frontend/src/metabase/static-viz/components/ComboChart/stories-data/line-replace-missing-values-zero.json
@@ -0,0 +1,175 @@
+[
+  {
+    "card": {
+      "public_uuid": null,
+      "parameter_usage_count": 0,
+      "created_at": "2024-05-01T23:25:14.654039Z",
+      "parameters": [],
+      "metabase_version": "v0.48.1-SNAPSHOT (8053de5)",
+      "collection": null,
+      "visualization_settings": {
+        "series_settings": {
+          "Y": {
+            "line.missing": "zero"
+          }
+        },
+        "graph.dimensions": ["X"],
+        "graph.metrics": ["Y"]
+      },
+      "collection_preview": true,
+      "entity_id": "hr7YTZePecARmmOlJMmYM",
+      "display": "line",
+      "parameter_mappings": [],
+      "id": 197,
+      "dataset_query": {
+        "database": 1,
+        "type": "native",
+        "native": {
+          "query": "SELECT DATE '2020-01-01' AS x, 10 AS y\nUNION ALL\nSELECT DATE '2023-01-01', 10\nUNION ALL\nSELECT DATE '2024-01-01', 10",
+          "template-tags": {}
+        }
+      },
+      "cache_ttl": null,
+      "embedding_params": null,
+      "made_public_by_id": null,
+      "updated_at": "2024-05-01T23:25:14.654039Z",
+      "moderation_reviews": [],
+      "creator_id": 1,
+      "average_query_time": null,
+      "type": "question",
+      "dashboard_count": 0,
+      "last_query_start": null,
+      "name": "replace missing values with zero",
+      "query_type": "native",
+      "collection_id": null,
+      "enable_embedding": false,
+      "database_id": 1,
+      "can_write": true,
+      "initially_published_at": null,
+      "result_metadata": null,
+      "table_id": null,
+      "collection_position": null,
+      "view_count": 0,
+      "archived": false,
+      "description": null,
+      "cache_invalidated_at": null,
+      "displayIsLocked": true
+    },
+    "data": {
+      "rows": [
+        ["2020-01-01T00:00:00-03:00", 10],
+        ["2023-01-01T00:00:00-03:00", 10],
+        ["2024-01-01T00:00:00-03:00", 10]
+      ],
+      "cols": [
+        {
+          "display_name": "X",
+          "source": "native",
+          "field_ref": [
+            "field",
+            "X",
+            {
+              "base-type": "type/Date"
+            }
+          ],
+          "name": "X",
+          "base_type": "type/Date",
+          "effective_type": "type/Date"
+        },
+        {
+          "display_name": "Y",
+          "source": "native",
+          "field_ref": [
+            "field",
+            "Y",
+            {
+              "base-type": "type/Integer"
+            }
+          ],
+          "name": "Y",
+          "base_type": "type/Integer",
+          "effective_type": "type/Integer"
+        }
+      ],
+      "native_form": {
+        "params": null,
+        "query": "SELECT DATE '2020-01-01' AS x, 10 AS y\nUNION ALL\nSELECT DATE '2023-01-01', 10\nUNION ALL\nSELECT DATE '2024-01-01', 10"
+      },
+      "format-rows?": true,
+      "results_timezone": "America/Montevideo",
+      "requested_timezone": "Canada/Eastern",
+      "results_metadata": {
+        "columns": [
+          {
+            "display_name": "X",
+            "field_ref": [
+              "field",
+              "X",
+              {
+                "base-type": "type/Date"
+              }
+            ],
+            "name": "X",
+            "base_type": "type/Date",
+            "effective_type": "type/Date",
+            "semantic_type": null,
+            "fingerprint": {
+              "global": {
+                "distinct-count": 3,
+                "nil%": 0
+              },
+              "type": {
+                "type/DateTime": {
+                  "earliest": "2020-01-01T00:00:00-03:00",
+                  "latest": "2024-01-01T00:00:00-03:00"
+                }
+              }
+            }
+          },
+          {
+            "display_name": "Y",
+            "field_ref": [
+              "field",
+              "Y",
+              {
+                "base-type": "type/Integer"
+              }
+            ],
+            "name": "Y",
+            "base_type": "type/Integer",
+            "effective_type": "type/Integer",
+            "semantic_type": null,
+            "fingerprint": {
+              "global": {
+                "distinct-count": 1,
+                "nil%": 0
+              },
+              "type": {
+                "type/Number": {
+                  "min": 10,
+                  "q1": 10,
+                  "q3": 10,
+                  "max": 10,
+                  "sd": 0,
+                  "avg": 10
+                }
+              }
+            }
+          }
+        ]
+      },
+      "insights": [
+        {
+          "previous-value": 10,
+          "unit": "year",
+          "offset": 10,
+          "last-change": 0,
+          "col": "Y",
+          "slope": 0,
+          "last-value": 10,
+          "best-fit": ["+", 10, ["*", 0, ["log", "x"]]]
+        }
+      ]
+    }
+  }
+]
diff --git a/frontend/src/metabase/visualizations/echarts/cartesian/model/dataset.ts b/frontend/src/metabase/visualizations/echarts/cartesian/model/dataset.ts
index aed25283ff65f71e810464e2638e60cd3ab8996b..578d7ff83a93f7ecee7496976daa70352afa7987 100644
--- a/frontend/src/metabase/visualizations/echarts/cartesian/model/dataset.ts
+++ b/frontend/src/metabase/visualizations/echarts/cartesian/model/dataset.ts
@@ -1,6 +1,7 @@
 import { t } from "ttag";
 
 import { getObjectKeys, getObjectValues } from "metabase/lib/objects";
+import { parseTimestamp } from "metabase/lib/time-dayjs";
 import { checkNumber, isNotNull } from "metabase/lib/types";
 import { isEmpty } from "metabase/lib/validate";
 import {
@@ -21,6 +22,7 @@ import type {
   XAxisModel,
   NumericAxisScaleTransforms,
   ShowWarning,
+  TimeSeriesXAxisModel,
 } from "metabase/visualizations/echarts/cartesian/model/types";
 import type { CartesianChartColumns } from "metabase/visualizations/lib/graph/columns";
 import { getNumberOr } from "metabase/visualizations/lib/settings/row-values";
@@ -282,12 +284,13 @@ export const getNullReplacerTransform = (
     )
     .map(seriesModel => seriesModel.dataKey);
 
-  return getKeyBasedDatasetTransform(
-    replaceNullsWithZeroDataKeys,
-    (value: RowValue) => {
-      return value === null ? 0 : value;
-    },
-  );
+  return datum => {
+    const transformedDatum = { ...datum };
+    for (const key of replaceNullsWithZeroDataKeys) {
+      transformedDatum[key] = datum[key] != null ? datum[key] : 0;
+    }
+    return transformedDatum;
+  };
 };
 
 const hasInterpolatedSeries = (
@@ -518,6 +521,43 @@ function getHistogramDataset(
   return dataset;
 }
 
+const MAX_FILL_COUNT = 10000;
+
+const interpolateTimeSeriesData = (
+  dataset: ChartDataset,
+  axisModel: TimeSeriesXAxisModel,
+): ChartDataset => {
+  if (axisModel.intervalsCount > MAX_FILL_COUNT) {
+    return dataset;
+  }
+
+  const { count, unit } = axisModel.interval;
+  const result = [];
+
+  for (let i = 0; i < dataset.length; i++) {
+    const datum = dataset[i];
+    result.push(datum);
+
+    if (i === dataset.length - 1) {
+      break;
+    }
+
+    const end = parseTimestamp(dataset[i + 1][X_AXIS_DATA_KEY]);
+
+    let start = parseTimestamp(datum[X_AXIS_DATA_KEY]);
+    while (start.add(count, unit).isBefore(end)) {
+      const interpolatedValue = start.add(count, unit);
+      result.push({
+        [X_AXIS_DATA_KEY]: interpolatedValue.toISOString(),
+      });
+
+      start = interpolatedValue;
+    }
+  }
+
+  return result;
+};
+
 /**
  * Modifies the dataset for visualization according to the specified visualization settings.
  *
@@ -539,8 +579,8 @@ export const applyVisualizationSettingsDataTransformations = (
   const seriesDataKeys = seriesModels.map(seriesModel => seriesModel.dataKey);
 
   if (
-    xAxisModel.axisType === "value" ||
-    xAxisModel.axisType === "time" ||
+    isNumericAxis(xAxisModel) ||
+    isTimeSeriesAxis(xAxisModel) ||
     xAxisModel.isHistogram
   ) {
     dataset = filterNullDimensionValues(dataset, showWarning);
@@ -550,10 +590,14 @@ export const applyVisualizationSettingsDataTransformations = (
     dataset = replaceZeroesForLogScale(dataset, seriesDataKeys);
   }
 
-  if (xAxisModel.axisType === "category" && xAxisModel.isHistogram) {
+  if (isCategoryAxis(xAxisModel) && xAxisModel.isHistogram) {
     dataset = getHistogramDataset(dataset, xAxisModel.histogramInterval);
   }
 
+  if (isTimeSeriesAxis(xAxisModel)) {
+    dataset = interpolateTimeSeriesData(dataset, xAxisModel);
+  }
+
   return transformDataset(dataset, [
     getNullReplacerTransform(settings, seriesModels),
     {
diff --git a/frontend/src/metabase/visualizations/echarts/cartesian/model/dataset.unit.spec.ts b/frontend/src/metabase/visualizations/echarts/cartesian/model/dataset.unit.spec.ts
index cefc17cc45c3adaa72720cf3fcf0d289672a8d7c..8e2b42768829a5e04af4148e4a6cfb3469e6cdd4 100644
--- a/frontend/src/metabase/visualizations/echarts/cartesian/model/dataset.unit.spec.ts
+++ b/frontend/src/metabase/visualizations/echarts/cartesian/model/dataset.unit.spec.ts
@@ -1,6 +1,9 @@
+import dayjs from "dayjs";
+
 import { createMockSeriesModel } from "__support__/echarts";
 import { checkNumber } from "metabase/lib/types";
 import {
+  ORIGINAL_INDEX_DATA_KEY,
   POSITIVE_STACK_TOTAL_DATA_KEY,
   X_AXIS_DATA_KEY,
 } from "metabase/visualizations/echarts/cartesian/constants/dataset";
@@ -34,6 +37,7 @@ import type {
   ChartDataset,
   LegacySeriesSettingsObjectKey,
   NumericAxisScaleTransforms,
+  TimeSeriesXAxisModel,
   XAxisModel,
 } from "./types";
 
@@ -400,6 +404,82 @@ describe("dataset transform functions", () => {
       ]);
     });
 
+    describe("time series", () => {
+      const dataset = [
+        {
+          [X_AXIS_DATA_KEY]: "2020-01-01T00:00:00.000Z",
+          dimensionKey: "A",
+          series1: 10,
+        },
+        // Missing February
+        {
+          [X_AXIS_DATA_KEY]: "2020-03-01T00:00:00.000Z",
+          dimensionKey: "A",
+          series1: 20,
+        },
+      ];
+
+      const xAxisModel: TimeSeriesXAxisModel = {
+        axisType: "time",
+        intervalsCount: 2,
+        interval: {
+          count: 1,
+          unit: "month",
+        },
+        timezone: "UTC",
+        range: [dayjs(), dayjs()],
+        formatter: value => String(value),
+        fromEChartsAxisValue: () => dayjs(),
+        toEChartsAxisValue: val => String(val),
+      };
+
+      it("should replace missing values with zeros based on the x-axis interval", () => {
+        const result = applyVisualizationSettingsDataTransformations(
+          dataset,
+          xAxisModel,
+          [createMockSeriesModel({ dataKey: "series1" })],
+          yAxisScaleTransforms,
+          createMockComputedVisualizationSettings({
+            series: () => ({
+              "line.missing": "zero",
+            }),
+          }),
+        );
+
+        expect(result).toEqual([
+          {
+            [ORIGINAL_INDEX_DATA_KEY]: 0,
+            [X_AXIS_DATA_KEY]: "2020-01-01T00:00:00.000Z",
+            dimensionKey: "A",
+            series1: 10,
+          },
+          { [X_AXIS_DATA_KEY]: "2020-02-01T00:00:00.000Z", series1: 0 },
+          {
+            [ORIGINAL_INDEX_DATA_KEY]: 1,
+            [X_AXIS_DATA_KEY]: "2020-03-01T00:00:00.000Z",
+            dimensionKey: "A",
+            series1: 20,
+          },
+        ]);
+      });
+
+      it("should not replace missing values with zeros when x-axis interval is too big", () => {
+        const result = applyVisualizationSettingsDataTransformations(
+          dataset,
+          { ...xAxisModel, intervalsCount: 10001 },
+          [createMockSeriesModel({ dataKey: "series1" })],
+          yAxisScaleTransforms,
+          createMockComputedVisualizationSettings({
+            series: () => ({
+              "line.missing": "zero",
+            }),
+          }),
+        );
+
+        expect(result).toHaveLength(dataset.length);
+      });
+    });
+
     it("should work on empty datasets", () => {
       const result = applyVisualizationSettingsDataTransformations(
         [],