Repository: microsoft/fluentui
Pull request #34422: feat(react-charting): overhaul default date label formatting
Author: login: AtishayMsft
Created: 2025-05-12T10:00:35Z | Merged: 2025-05-12T15:58:11Z

<!--
Thank you for submitting a pull request!

Please verify that:
* [ ] Code is up-to-date with the `master` branch
* [ ] Your changes are covered by tests (if possible)
* [ ] You've run `yarn change` locally


PR flow tips:
* [ ] Try to start with a Draft PR
* [ ] Once you're ready (ideally the pipeline is passing) promote your PR to Ready for Review. This step will auto-assign reviewers for your PR.
-->

## Previous Behavior
We were formatting date per tick label. So depending on the kind of datetime details, different ticks show different format.
This was making the x axis look inconsistent and sometimes leading to a degraded experience and reduced readablilty.

1. ![image](https://github.com/user-attachments/assets/78ac5e54-652a-4caf-bc46-49e90ae499f0)

2. ![image](https://github.com/user-attachments/assets/66060eab-1344-4e08-bf75-56e553d699f8)

3. ![image](https://github.com/user-attachments/assets/59754039-529e-4fa2-820d-b44103ebc2f5)


<!-- This is the behavior we have today -->

## New Behavior
Created a multilevel formatter for a given date range.
It determines the appropriate date format to accommodate each tick value.
The goal is to represent the date label in the smallest possible format without loss of information.
The challenge here is to adhere to locale specific formats while ensuring the complete label is shown.
There is an exhaustive map of all possible date/time units and their respective formats.
Based on the range of formatting granularity levels, a format spanning the range is returned.

This ensures consistent and refined formatting for each tick.
The format is completely flexible and the consumer can load their own locale to customize the formatted labels as per their requirements.

Also refer: https://d3js.org/d3-time-format#locale_format

1. ![image](https://github.com/user-attachments/assets/1d84661f-dce8-41d7-a33a-5964b9391418)

2. ![image](https://github.com/user-attachments/assets/85e25ee7-7711-415f-aed2-fe285eb8fa90)
 
3. ![image](https://github.com/user-attachments/assets/ddedd88e-c2c2-43ac-8594-647f1afc539a)





<!-- This is the behavior we should expect with the changes in this PR -->

## Related Issue(s)

<!-- Please link the issue being fixed so it gets closed when this is merged. -->

- Fixes #



Diff:
diff --git a/change/@fluentui-react-charting-5478752c-cd72-4c41-b24e-a1fdacdf91bb.json b/change/@fluentui-react-charting-5478752c-cd72-4c41-b24e-a1fdacdf91bb.json
new file mode 100644
index 00000000000000..1520b667fe3b9e
--- /dev/null
+++ b/change/@fluentui-react-charting-5478752c-cd72-4c41-b24e-a1fdacdf91bb.json
@@ -0,0 +1,7 @@
+{
+  "type": "patch",
+  "comment": "feat(react-charting): Rehaul default date label formatting",
+  "packageName": "@fluentui/react-charting",
+  "email": "98592573+AtishayMsft@users.noreply.github.com",
+  "dependentChangeType": "patch"
+}
diff --git a/packages/charts/react-charting/src/components/AreaChart/AreaChartRTL.test.tsx b/packages/charts/react-charting/src/components/AreaChart/AreaChartRTL.test.tsx
index 12977fac2432d6..5ba0acde92a2c1 100644
--- a/packages/charts/react-charting/src/components/AreaChart/AreaChartRTL.test.tsx
+++ b/packages/charts/react-charting/src/components/AreaChart/AreaChartRTL.test.tsx
@@ -702,6 +702,9 @@ describe('Area chart - Subcomponent xAxis Labels', () => {
       // Assert
       expect(getById(container, /showDots/i)[0]!.textContent!).toEqual('Jan ...');
     },
+    undefined,
+    undefined,
+    !(isTimezoneSet(Timezone.UTC) && isTestEnv()),
   );
 
   testWithWait(
diff --git a/packages/charts/react-charting/src/components/DeclarativeChart/__snapshots__/DeclarativeChartRTL.test.tsx.snap b/packages/charts/react-charting/src/components/DeclarativeChart/__snapshots__/DeclarativeChartRTL.test.tsx.snap
index af33dd2516b510..0e180f0c0e559a 100644
--- a/packages/charts/react-charting/src/components/DeclarativeChart/__snapshots__/DeclarativeChartRTL.test.tsx.snap
+++ b/packages/charts/react-charting/src/components/DeclarativeChart/__snapshots__/DeclarativeChartRTL.test.tsx.snap
@@ -18830,7 +18830,25 @@ exports[`DeclarativeChart Should render linechart in DeclarativeChart 1`] = `
             <g
               class="tick"
               opacity="1"
-              transform="translate(56.86363636363637,0)"
+              transform="translate(40.5,0)"
+            >
+              <line
+                stroke="currentColor"
+                y2="6"
+              />
+              <text
+                aria-hidden="true"
+                dy="0.71em"
+                fill="currentColor"
+                y="16"
+              >
+                Dec 28
+              </text>
+            </g>
+            <g
+              class="tick"
+              opacity="1"
+              transform="translate(76.5,0)"
             >
               <line
                 stroke="currentColor"
@@ -18848,7 +18866,7 @@ exports[`DeclarativeChart Should render linechart in DeclarativeChart 1`] = `
             <g
               class="tick"
               opacity="1"
-              transform="translate(95.04545454545455,0)"
+              transform="translate(112.5,0)"
             >
               <line
                 stroke="currentColor"
@@ -18866,7 +18884,7 @@ exports[`DeclarativeChart Should render linechart in DeclarativeChart 1`] = `
             <g
               class="tick"
               opacity="1"
-              transform="translate(133.22727272727275,0)"
+              transform="translate(148.5,0)"
             >
               <line
                 stroke="currentColor"
@@ -18884,7 +18902,7 @@ exports[`DeclarativeChart Should render linechart in DeclarativeChart 1`] = `
             <g
               class="tick"
               opacity="1"
-              transform="translate(171.40909090909093,0)"
+              transform="translate(184.5,0)"
             >
               <line
                 stroke="currentColor"
@@ -18902,7 +18920,7 @@ exports[`DeclarativeChart Should render linechart in DeclarativeChart 1`] = `
             <g
               class="tick"
               opacity="1"
-              transform="translate(209.59090909090912,0)"
+              transform="translate(220.5,0)"
             >
               <line
                 stroke="currentColor"
@@ -18914,13 +18932,13 @@ exports[`DeclarativeChart Should render linechart in DeclarativeChart 1`] = `
                 fill="currentColor"
                 y="16"
               >
-                February
+                Feb 01
               </text>
             </g>
             <g
               class="tick"
               opacity="1"
-              transform="translate(247.77272727272725,0)"
+              transform="translate(256.5,0)"
             >
               <line
                 stroke="currentColor"
@@ -18938,7 +18956,7 @@ exports[`DeclarativeChart Should render linechart in DeclarativeChart 1`] = `
             <g
               class="tick"
               opacity="1"
-              transform="translate(285.95454545454544,0)"
+              transform="translate(292.5,0)"
             >
               <line
                 stroke="currentColor"
@@ -18956,7 +18974,7 @@ exports[`DeclarativeChart Should render linechart in DeclarativeChart 1`] = `
             <g
               class="tick"
               opacity="1"
-              transform="translate(324.1363636363636,0)"
+              transform="translate(328.5,0)"
             >
               <line
                 stroke="currentColor"
@@ -18974,7 +18992,7 @@ exports[`DeclarativeChart Should render linechart in DeclarativeChart 1`] = `
             <g
               class="tick"
               opacity="1"
-              transform="translate(362.3181818181818,0)"
+              transform="translate(364.5,0)"
             >
               <line
                 stroke="currentColor"
@@ -18986,7 +19004,7 @@ exports[`DeclarativeChart Should render linechart in DeclarativeChart 1`] = `
                 fill="currentColor"
                 y="16"
               >
-                March
+                Mar 01
               </text>
             </g>
             <g
@@ -19010,7 +19028,7 @@ exports[`DeclarativeChart Should render linechart in DeclarativeChart 1`] = `
             <g
               class="tick"
               opacity="1"
-              transform="translate(438.6818181818182,0)"
+              transform="translate(436.5,0)"
             >
               <line
                 stroke="currentColor"
@@ -19028,7 +19046,7 @@ exports[`DeclarativeChart Should render linechart in DeclarativeChart 1`] = `
             <g
               class="tick"
               opacity="1"
-              transform="translate(476.8636363636364,0)"
+              transform="translate(472.5,0)"
             >
               <line
                 stroke="currentColor"
@@ -19046,7 +19064,7 @@ exports[`DeclarativeChart Should render linechart in DeclarativeChart 1`] = `
             <g
               class="tick"
               opacity="1"
-              transform="translate(515.0454545454545,0)"
+              transform="translate(508.5,0)"
             >
               <line
                 stroke="currentColor"
@@ -19064,7 +19082,7 @@ exports[`DeclarativeChart Should render linechart in DeclarativeChart 1`] = `
             <g
               class="tick"
               opacity="1"
-              transform="translate(553.2272727272726,0)"
+              transform="translate(544.5,0)"
             >
               <line
                 stroke="currentColor"
@@ -19079,6 +19097,24 @@ exports[`DeclarativeChart Should render linechart in DeclarativeChart 1`] = `
                 Apr 05
               </text>
             </g>
+            <g
+              class="tick"
+              opacity="1"
+              transform="translate(580.5,0)"
+            >
+              <line
+                stroke="currentColor"
+                y2="6"
+              />
+              <text
+                aria-hidden="true"
+                dy="0.71em"
+                fill="currentColor"
+                y="16"
+              >
+                Apr 12
+              </text>
+            </g>
           </g>
           <g
             class=
@@ -19225,7 +19261,7 @@ exports[`DeclarativeChart Should render linechart in DeclarativeChart 1`] = `
                 role="region"
               >
                 <path
-                  d="M40,-15.57L45.455,-14.518L50.909,-14.477L56.364,-13.23L61.818,-13.415L67.273,-12.964L72.727,-15.472L78.182,-13.949L83.636,-16.429L89.091,-15.789L94.545,-14.185L100,-13.501L105.455,-14.286L110.909,-14.62L116.364,-13.214L121.818,-12.321L127.273,-12.508L132.727,-15.057L138.182,-12.872L143.636,-12.215L149.091,-15.292L154.545,-15.036L160,-15.199L165.455,-16.57L170.909,-15.602L176.364,-16.178L181.818,-18.02L187.273,-17.795L192.727,-19.054L198.182,-19.761L203.636,-22.471L209.091,-21.628L214.545,-19.744L220,-21.236L225.455,-23.071L230.909,-22.66L236.364,-22.202L241.818,-21.087L247.273,-19.913L252.727,-19.218L258.182,-18.927L263.636,-17.289L269.091,-20.751L274.545,-23.605L280,-25.412L285.455,-26.263L290.909,-28.068L296.364,-24.119L301.818,-24.977L307.273,-25.337L312.727,-25.363L318.182,-25.724L323.636,-26.132L329.091,-24.784L334.545,-24.956L340,-26.963L345.455,-26.764L350.909,-25.946L356.364,-27.863L361.818,-26.334L367.273,-25.43L372.727,-28.273L378.182,-29.509L383.636,-28.86L389.091,-29.072L394.545,-31.733L400,-31.503L405.455,-33.08L410.909,-33.993L416.364,-32.286L421.818,-32.675L427.273,-31.507L432.727,-31.001L438.182,-32.834L443.636,-30.498L449.091,-30.699L454.545,-33.238L460,-33.862L465.455,-34.008L470.909,-32.688L476.364,-32.18L481.818,-33.849L487.273,-31.42L492.727,-32.372L498.182,-32.803L503.636,-32.984L509.091,-32.344L514.545,-30.893L520,-29.169L525.455,-28.482L530.909,-25.502L536.364,-26.719L541.818,-22.382L547.273,-22.601L552.727,-19.33L558.182,-18.289L563.636,-16.153L569.091,-14.789L574.545,-9.641L580,-10.706"
+                  d="M60.571,-15.57L65.714,-14.518L70.857,-14.477L76,-13.23L81.143,-13.415L86.286,-12.964L91.429,-15.472L96.571,-13.949L101.714,-16.429L106.857,-15.789L112,-14.185L117.143,-13.501L122.286,-14.286L127.429,-14.62L132.571,-13.214L137.714,-12.321L142.857,-12.508L148,-15.057L153.143,-12.872L158.286,-12.215L163.429,-15.292L168.571,-15.036L173.714,-15.199L178.857,-16.57L184,-15.602L189.143,-16.178L194.286,-18.02L199.429,-17.795L204.571,-19.054L209.714,-19.761L214.857,-22.471L220,-21.628L225.143,-19.744L230.286,-21.236L235.429,-23.071L240.571,-22.66L245.714,-22.202L250.857,-21.087L256,-19.913L261.143,-19.218L266.286,-18.927L271.429,-17.289L276.571,-20.751L281.714,-23.605L286.857,-25.412L292,-26.263L297.143,-28.068L302.286,-24.119L307.429,-24.977L312.571,-25.337L317.714,-25.363L322.857,-25.724L328,-26.132L333.143,-24.784L338.286,-24.956L343.429,-26.963L348.571,-26.764L353.714,-25.946L358.857,-27.863L364,-26.334L369.143,-25.43L374.286,-28.273L379.429,-29.509L384.571,-28.86L389.714,-29.072L394.857,-31.733L400,-31.503L405.143,-33.08L410.286,-33.993L415.429,-32.286L420.571,-32.675L425.714,-31.507L430.857,-31.001L436,-32.834L441.143,-30.498L446.286,-30.699L451.429,-33.238L456.571,-33.862L461.714,-34.008L466.857,-32.688L472,-32.18L477.143,-33.849L482.286,-31.42L487.429,-32.372L492.571,-32.803L497.714,-32.984L502.857,-32.344L508,-30.893L513.143,-29.169L518.286,-28.482L523.429,-25.502L528.571,-26.719L533.714,-22.382L538.857,-22.601L544,-19.33L549.143,-18.289L554.286,-16.153L559.429,-14.789L564.571,-9.641L569.714,-10.706"
                   data-is-focusable="true"
                   fill="transparent"
                   id="lineID1_1"
@@ -19251,7 +19287,7 @@ exports[`DeclarativeChart Should render linechart in DeclarativeChart 1`] = `
                 role="region"
               >
                 <path
-                  d="M40,-10.657L45.455,-12.053L50.909,-13.614L56.364,-14.151L61.818,-14.683L67.273,-11.49L72.727,-10.234L78.182,-8.872L83.636,-9.502L89.091,-13.6L94.545,-14.439L100,-15.918L105.455,-18.688L110.909,-17.421L116.364,-17.034L121.818,-19.914L127.273,-16.748L132.727,-17.119L138.182,-17.571L143.636,-18.723L149.091,-17.984L154.545,-15.849L160,-15.629L165.455,-15.65L170.909,-14.742L176.364,-12.72L181.818,-10.082L187.273,-10.181L192.727,-10.629L198.182,-9.185L203.636,-6.482L209.091,-7.573L214.545,-8.434L220,-8.538L225.455,-6.745L230.909,-4.324L236.364,-2.812L241.818,-4.697L247.273,-0.718L252.727,-0.391L258.182,0.598L263.636,0.354L269.091,2.452L274.545,2.709L280,1.43L285.455,3.28L290.909,6.133L296.364,8.284L301.818,11.138L307.273,13.523L312.727,11.324L318.182,12.439L323.636,14.297L329.091,11.099L334.545,12.065L340,12.846L345.455,12.035L350.909,13.261L356.364,9.119L361.818,11.015L367.273,12.592L372.727,11.237L378.182,10.412L383.636,8.083L389.091,10.647L394.545,9.489L400,9.868L405.455,8.917L410.909,7.723L416.364,6.398L421.818,7.591L427.273,9.873L432.727,11.517L438.182,12.895L443.636,12.788L449.091,14.587L454.545,14.276L460,13.461L465.455,13.968L470.909,14.403L476.364,12.163L481.818,11.814L487.273,9.222L492.727,10.647L498.182,12.04L503.636,10.751L509.091,10.37L514.545,10.065L520,12.036L525.455,11.384L530.909,11.592L536.364,10.036L541.818,9.577L547.273,8.23L552.727,8.61L558.182,8.425L563.636,7.623L569.091,9.931L574.545,10.429L580,10.555"
+                  d="M60.571,-10.657L65.714,-12.053L70.857,-13.614L76,-14.151L81.143,-14.683L86.286,-11.49L91.429,-10.234L96.571,-8.872L101.714,-9.502L106.857,-13.6L112,-14.439L117.143,-15.918L122.286,-18.688L127.429,-17.421L132.571,-17.034L137.714,-19.914L142.857,-16.748L148,-17.119L153.143,-17.571L158.286,-18.723L163.429,-17.984L168.571,-15.849L173.714,-15.629L178.857,-15.65L184,-14.742L189.143,-12.72L194.286,-10.082L199.429,-10.181L204.571,-10.629L209.714,-9.185L214.857,-6.482L220,-7.573L225.143,-8.434L230.286,-8.538L235.429,-6.745L240.571,-4.324L245.714,-2.812L250.857,-4.697L256,-0.718L261.143,-0.391L266.286,0.598L271.429,0.354L276.571,2.452L281.714,2.709L286.857,1.43L292,3.28L297.143,6.133L302.286,8.284L307.429,11.138L312.571,13.523L317.714,11.324L322.857,12.439L328,14.297L333.143,11.099L338.286,12.065L343.429,12.846L348.571,12.035L353.714,13.261L358.857,9.119L364,11.015L369.143,12.592L374.286,11.237L379.429,10.412L384.571,8.083L389.714,10.647L394.857,9.489L400,9.868L405.143,8.917L410.286,7.723L415.429,6.398L420.571,7.591L425.714,9.873L430.857,11.517L436,12.895L441.143,12.788L446.286,14.587L451.429,14.276L456.571,13.461L461.714,13.968L466.857,14.403L472,12.163L477.143,11.814L482.286,9.222L487.429,10.647L492.571,12.04L497.714,10.751L502.857,10.37L508,10.065L513.143,12.036L518.286,11.384L523.429,11.592L528.571,10.036L533.714,9.577L538.857,8.23L544,8.61L549.143,8.425L554.286,7.623L559.429,9.931L564.571,10.429L569.714,10.555"
                   data-is-focusable="true"
                   fill="transparent"
                   id="lineID1_0"
diff --git a/packages/charts/react-charting/src/components/LineChart/LineChartRTL.test.tsx b/packages/charts/react-charting/src/components/LineChart/LineChartRTL.test.tsx
index be9e0b52dc61e0..e7902e006eb272 100644
--- a/packages/charts/react-charting/src/components/LineChart/LineChartRTL.test.tsx
+++ b/packages/charts/react-charting/src/components/LineChart/LineChartRTL.test.tsx
@@ -708,6 +708,9 @@ describe('Line chart - Subcomponent xAxis Labels', () => {
       // Assert
       expect(getById(container, /showDots/i)[0]!.textContent!).toEqual('Febr...');
     },
+    undefined,
+    undefined,
+    !(isTimezoneSet(Timezone.UTC) && isTestEnv()),
   );
 });
 
@@ -741,6 +744,9 @@ describe('Line chart - Subcomponent Event', () => {
       expect(screen.queryByText('event 3 message')).not.toBeNull();
       expect(screen.queryByText('event 4 message')).toBeNull();
     },
+    undefined,
+    undefined,
+    !(isTimezoneSet(Timezone.UTC) && isTestEnv()),
   );
 });
 
diff --git a/packages/charts/react-charting/src/utilities/utilities.ts b/packages/charts/react-charting/src/utilities/utilities.ts
index 7f8b03cb3a59ed..c74b2b2dbce3ca 100644
--- a/packages/charts/react-charting/src/utilities/utilities.ts
+++ b/packages/charts/react-charting/src/utilities/utilities.ts
@@ -208,7 +208,7 @@ export function createNumericXAxis(
 ) {
   const {
     domainNRangeValues,
-    showRoundOffXTickValues = false,
+    showRoundOffXTickValues = true,
     xAxistickSize = 6,
     tickPadding = 10,
     xAxisCount,
@@ -256,18 +256,83 @@ export function createNumericXAxis(
   return { xScale: xAxisScale, tickValues };
 }
 
-function multiFormat(date: Date, locale?: d3TimeLocaleObject, useUTC?: boolean) {
+/**
+ * This function returns a multilevel formatter for a given date range.
+ * It determines the appropriate date format to accommodate each tick value.
+ * The goal is to represent the date label in the smallest possible format without loss of information.
+ * The challenge here is to adhere to locale specific formats while ensuring the complete label is shown.
+ * There is an exhaustive map of all possible date/time units and their respective formats.
+ * Based on the range of formatting granularity levels, a format spanning the range is returned.
+ * @param startLevel - The starting level of the date format.
+ * @param endLevel - The ending level of the date format.
+ * @param locale - The locale object for formatting.
+ * @param useUTC
+ * @returns
+ */
+function getMultiLevelDateFormatter(
+  startLevel: number,
+  endLevel: number,
+  locale?: d3TimeLocaleObject,
+  useUTC?: boolean,
+) {
   const timeFormat = locale ? (useUTC ? locale.utcFormat : locale.format) : useUTC ? d3UtcFormat : d3TimeFormat;
 
-  const formatMillisecond = timeFormat('.%L');
-  const formatSecond = timeFormat(':%S');
-  const formatMinute = timeFormat('%I:%M');
-  const formatHour = timeFormat('%I %p');
-  const formatDay = timeFormat('%a %d');
-  const formatWeek = timeFormat('%b %d');
-  const formatMonth = timeFormat('%B');
-  const formatYear = timeFormat('%Y');
+  // Refer to https://d3js.org/d3-time-format#locale_format to see explanation about each format specifier
+  const DEFAULT = '%c';
+  const MS = '.%L';
+  const MS_S = ':%S.%L';
+  const MS_S_MIN = '%M:%S.%L';
+  const MS_S_MIN_H = '%-I:%M:%S.%L %p';
+  const MS_S_MIN_H_D = '%a %d, %X';
+  const MS_S_MIN_H_D_W = '%b %d, %X';
+  const MS_S_MIN_H_D_W_M = MS_S_MIN_H_D_W;
+  const MS_S_MIN_H_D_W_M_Y = DEFAULT;
+  const S = ':%S';
+  const S_MIN = '%-I:%M:%S';
+  const S_MIN_H = '%X';
+  const S_MIN_H_D = MS_S_MIN_H_D;
+  const S_MIN_H_D_W = MS_S_MIN_H_D_W;
+  const S_MIN_H_D_W_M = MS_S_MIN_H_D_W_M;
+  const S_MIN_H_D_W_M_Y = DEFAULT;
+  const MIN = '%-I:%M %p';
+  const MIN_H = MIN;
+  const MIN_H_D = '%a %d, %-I:%M %p';
+  const MIN_H_D_W = '%b %d, %-I:%M %p';
+  const MIN_H_D_W_M = MIN_H_D_W;
+  const MIN_H_D_W_M_Y = '%x, %-I:%M %p';
+  const H = '%-I %p';
+  const H_D = '%a %d, %-I %p';
+  const H_D_W = '%b %d, %-I %p';
+  const H_D_W_M = H_D_W;
+  const H_D_W_M_Y = '%x, %-I %p';
+  const D = '%a %d';
+  const D_W = '%b %d';
+  const D_W_M = D_W;
+  const D_W_M_Y = '%x';
+  const W = D_W;
+  const W_M = W;
+  const W_M_Y = D_W_M_Y;
+  const M = '%B';
+  const M_Y = '%b %Y';
+  const Y = '%Y';
+
+  const MULTI_LEVEL_DATE_TIME_FORMATS = [
+    // ms, s, min, h, d, w, m, y
+    [MS, MS_S, MS_S_MIN, MS_S_MIN_H, MS_S_MIN_H_D, MS_S_MIN_H_D_W, MS_S_MIN_H_D_W_M, MS_S_MIN_H_D_W_M_Y], // ms
+    [DEFAULT, S, S_MIN, S_MIN_H, S_MIN_H_D, S_MIN_H_D_W, S_MIN_H_D_W_M, S_MIN_H_D_W_M_Y], // s
+    [DEFAULT, DEFAULT, MIN, MIN_H, MIN_H_D, MIN_H_D_W, MIN_H_D_W_M, MIN_H_D_W_M_Y], // min
+    [DEFAULT, DEFAULT, DEFAULT, H, H_D, H_D_W, H_D_W_M, H_D_W_M_Y], // h
+    [DEFAULT, DEFAULT, DEFAULT, DEFAULT, D, D_W, D_W_M, D_W_M_Y], // d
+    [DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, W, W_M, W_M_Y], // w
+    [DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, M, M_Y], // m
+    [DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, DEFAULT, Y], // y
+  ];
+
+  const formatter = timeFormat(MULTI_LEVEL_DATE_TIME_FORMATS[startLevel][endLevel]);
+  return formatter;
+}
 
+function getDateFormatLevel(date: Date, useUTC?: boolean) {
   const timeSecond = useUTC ? d3UtcSecond : d3TimeSecond;
   const timeMinute = useUTC ? d3UtcMinute : d3TimeMinute;
   const timeHour = useUTC ? d3UtcHour : d3TimeHour;
@@ -276,23 +341,20 @@ function multiFormat(date: Date, locale?: d3TimeLocaleObject, useUTC?: boolean)
   const timeWeek = useUTC ? d3UtcWeek : d3TimeWeek;
   const timeYear = useUTC ? d3UtcYear : d3TimeYear;
 
-  return (
-    timeSecond(date) < date
-      ? formatMillisecond
-      : timeMinute(date) < date
-      ? formatSecond
-      : timeHour(date) < date
-      ? formatMinute
-      : timeDay(date) < date
-      ? formatHour
-      : timeMonth(date) < date
-      ? timeWeek(date) < date
-        ? formatDay
-        : formatWeek
-      : timeYear(date) < date
-      ? formatMonth
-      : formatYear
-  )(date);
+  const formats = [
+    { formatLevel: 0, condition: (d: Date) => timeSecond(d) < d }, // Milliseconds
+    { formatLevel: 1, condition: (d: Date) => timeMinute(d) < d }, // Seconds
+    { formatLevel: 2, condition: (d: Date) => timeHour(d) < d }, // Minutes
+    { formatLevel: 3, condition: (d: Date) => timeDay(d) < d }, // Hours
+    { formatLevel: 4, condition: (d: Date) => timeMonth(d) < d && timeWeek(d) < d }, // Days
+    { formatLevel: 5, condition: (d: Date) => timeMonth(d) < d }, // Weeks
+    { formatLevel: 6, condition: (d: Date) => timeYear(d) < d }, // Months
+    { formatLevel: 7, condition: () => true }, // Years (default)
+  ];
+
+  const matchedFormat = formats.find(({ condition }) => condition(date));
+
+  return matchedFormat?.formatLevel ?? 7;
 }
 
 function isPowerOf10(num: number): boolean {
@@ -321,20 +383,38 @@ export function createDateXAxis(
   customDateTimeFormatter?: (dateTime: Date) => string,
   useUTC?: boolean,
 ) {
-  const {
-    domainNRangeValues,
-    xAxisElement,
-    tickPadding = 6,
-    xAxistickSize = 6,
-    xAxisCount,
-    hideTickOverlap,
-  } = xAxisParams;
+  const { domainNRangeValues, xAxisElement, tickPadding = 6, xAxistickSize = 6, xAxisCount } = xAxisParams;
   const xAxisScale = useUTC ? d3ScaleUtc() : d3ScaleTime();
   xAxisScale
     .domain([domainNRangeValues.dStartValue, domainNRangeValues.dEndValue])
-    .range([domainNRangeValues.rStartValue, domainNRangeValues.rEndValue]);
+    .range([domainNRangeValues.rStartValue, domainNRangeValues.rEndValue])
+    .nice();
 
   let tickCount = xAxisCount ?? 6;
+
+  let lowestFormatLevel = 100;
+  let highestFormatLevel = -1;
+
+  const locale = timeFormatLocale ? d3TimeFormatLocale(timeFormatLocale) : undefined;
+
+  xAxisScale.ticks().forEach((domainValue: Date) => {
+    const formatLevel = getDateFormatLevel(domainValue, useUTC);
+
+    if (formatLevel > highestFormatLevel) {
+      highestFormatLevel = formatLevel;
+    }
+    if (formatLevel < lowestFormatLevel) {
+      lowestFormatLevel = formatLevel;
+    }
+  });
+
+  const formatFn: (date: Date) => string = getMultiLevelDateFormatter(
+    lowestFormatLevel,
+    highestFormatLevel,
+    locale,
+    useUTC,
+  );
+
   const tickFormat = (domainValue: Date, _index: number) => {
     if (customDateTimeFormatter) {
       return customDateTimeFormatter(domainValue);
@@ -343,8 +423,7 @@ export function createDateXAxis(
       return domainValue.toLocaleString(culture, options);
     }
     if (timeFormatLocale) {
-      const locale: d3TimeLocaleObject = d3TimeFormatLocale(timeFormatLocale!);
-      return multiFormat(domainValue, locale, useUTC);
+      return formatFn(domainValue);
     }
     if (culture === undefined && tickParams.tickFormat) {
       if (useUTC) {
@@ -353,14 +432,14 @@ export function createDateXAxis(
         return d3TimeFormat(tickParams.tickFormat)(domainValue);
       }
     }
-    return multiFormat(domainValue, undefined, useUTC);
+    return formatFn(domainValue);
   };
-  if (hideTickOverlap && typeof xAxisCount === 'undefined') {
-    const longestLabelWidth =
-      calculateLongestLabelWidth(xAxisScale.ticks().map(tickFormat), '[class^="xAxis-"] text') + 40;
-    const [start, end] = xAxisScale.range();
-    tickCount = Math.min(Math.max(1, Math.floor(Math.abs(end - start) / longestLabelWidth)), 10);
-  }
+
+  const longestLabelWidth =
+    calculateLongestLabelWidth(xAxisScale.ticks().map(tickFormat), '[class^="xAxis-"] text') + 40;
+  const [start, end] = xAxisScale.range();
+  tickCount = Math.min(Math.max(1, Math.floor(Math.abs(end - start) / longestLabelWidth)), 10);
+  tickCount = Math.min(tickCount, xAxisCount ?? tickCount);
 
   const xAxis = d3AxisBottom(xAxisScale)
     .tickSize(xAxistickSize)


Reviews:
user: login: github-actions[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-12T10:40:24Z
user: login: Anush2303
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T15:19:05Z
user: login: AtishayMsft
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-12T15:57:58Z
user: login: krkshitij
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-06-13T17:36:42Z

Review comments:
diff_hunk: @@ -208,7 +208,7 @@ export function createNumericXAxis(
 ) {
path: packages/charts/react-charting/src/utilities/utilities.ts
user: login: github-actions[bot]
body: ## 🕵🏾‍♀️ [<b data-uid='vrt-gist'></b> visual changes to review in the  Visual Change Report](https://aka.ms/visual-changes-review?organization=microsoft&project=fluentui&prId=34422&commitId=31f59b222ffdda50e62f8585cc902915775fbb89&env=prod&clientType=FLUENTUI)


  <details><summary><b>vr-tests/Callout</b> 9 screenshots</summary>

|Image Name|Diff(in Pixels)| Image Type
|-|-|-|
|vr-tests/Callout.Left bottom edge.default.chromium.png|3123| Changed|
|vr-tests/Callout.Bottom right edge - RTL.default.chromium.png|1114| Changed|
|vr-tests/Callout.Left center.default.chromium.png|2544| Changed|
|vr-tests/Callout.Gap space 25.default.chromium.png|2181| Changed|
|vr-tests/Callout.Bottom left edge - RTL.default.chromium.png|2186| Changed|
|vr-tests/Callout.Root.default.chromium.png|2181| Changed|
|vr-tests/Callout.Top center.default.chromium.png|2113| Changed|
|vr-tests/Callout.Top right edge.default.chromium.png|1134| Changed|
|vr-tests/Callout.Top left edge.default.chromium.png|2196| Changed|

</details>


  <details><summary><b>vr-tests/Keytip</b> 1 screenshots</summary>

|Image Name|Diff(in Pixels)| Image Type
|-|-|-|
|vr-tests/Keytip.Disabled.default.chromium.png|24| Changed|

</details>


  <details><summary><b>vr-tests/react-charting-LineChart</b> 10 screenshots</summary>

|Image Name|Diff(in Pixels)| Image Type
|-|-|-|
|vr-tests/react-charting-LineChart.Events.default.chromium.png|1| Changed|
|vr-tests/react-charting-LineChart.Basic - Dark Mode.hover.chromium.png|1379| Changed|
|vr-tests/react-charting-LineChart.Basic - RTL.default.chromium.png|1329| Changed|
|vr-tests/react-charting-LineChart.Gaps - Dark Mode.default.chromium.png|1175| Changed|
|vr-tests/react-charting-LineChart.Basic.hover.chromium.png|1323| Changed|
|vr-tests/react-charting-LineChart.Gaps - RTL.default.chromium.png|1132| Changed|
|vr-tests/react-charting-LineChart.Gaps.default.chromium.png|1127| Changed|
|vr-tests/react-charting-LineChart.Multiple.default.chromium.png|192| Changed|
|vr-tests/react-charting-LineChart.Multiple - Dark Mode.default.chromium.png|181| Changed|
|vr-tests/react-charting-LineChart.Multiple - RTL.default.chromium.png|200| Changed|

</details>


  <details><summary><b>vr-tests/react-charting-VerticalBarChart</b> 3 screenshots</summary>

|Image Name|Diff(in Pixels)| Image Type
|-|-|-|
|vr-tests/react-charting-VerticalBarChart.Stacked - Date Axis - RTL.default.chromium.png|556| Changed|
|vr-tests/react-charting-VerticalBarChart.Stacked - Date Axis.default.chromium.png|557| Changed|
|vr-tests/react-charting-VerticalBarChart.Stacked - Date Axis - Dark Mode.default.chromium.png|581| Changed|

</details>

*There were 3 duplicate changes discarded. Check the build logs for more information.*


<div id="vrtCommentFluent UI"/>
created_at: 2025-05-12T10:40:24Z
author_association: NONE
diff_hunk: @@ -208,7 +208,7 @@ export function createNumericXAxis(
 ) {
path: packages/charts/react-charting/src/utilities/utilities.ts
user: login: AtishayMsft
body: changes are as expected
created_at: 2025-05-12T15:57:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -353,14 +432,14 @@ export function createDateXAxis(
         return d3TimeFormat(tickParams.tickFormat)(domainValue);
       }
     }
-    return multiFormat(domainValue, undefined, useUTC);
+    return formatFn(domainValue);
   };
-  if (hideTickOverlap && typeof xAxisCount === 'undefined') {
path: packages/charts/react-charting/src/utilities/utilities.ts
user: login: krkshitij
body: why was this condition removed?
created_at: 2025-06-13T17:36:42Z
author_association: CONTRIBUTOR

PR comments:
user: login: github-actions[bot]
created_at: 2025-05-12T10:03:31Z
body: ## 📊 Bundle size report

✅ No changes found

<!-- Sticky Pull Request Commentbundle-size-report -->
author_association: NONE
user: login: github-actions[bot]
created_at: 2025-05-12T10:08:53Z
body: Pull request demo site: [URL](https://fluentuipr.z22.web.core.windows.net/pull/34422/)
<!-- Sticky Pull Request Commentdeploy-pr-site -->
author_association: NONE