Date Mode Binding

Date Mode Binding lets Data Charts treat Date fields as time-based categories and group them by levels such as year, quarter, month, or day. It can also be used in hierarchical categories, so one Date field can produce calendar levels like year > quarter > month or month > day without adding extra fields to the data source.

Use Date scale when the chart should preserve real time intervals and show gaps in the timeline. Use Ordinal scale when each available date category should be spaced evenly for easier comparison. Date labels can also be formatted by level to match regional or business-specific conventions.

When charting time-series data, you often need to group dates by a specific granularity (year, quarter, month, week, or day) and choose how the axis represents time intervals. The dateMode option on the category encoding solves the grouping problem by automatically aggregating date values into the specified level, eliminating the need for manual preprocessing. The axis scale option with ValueScaleType.date or ValueScaleType.ordinal solves the display problem — use Date scale for a continuous proportional timeline where gaps such as weekends or holidays are visually accurate, or Ordinal scale for equally spaced data points regardless of actual time distance. Notes: The category field must contain JavaScript Date values for dateMode to take effect. With scale type "Date", the axis displays a continuous proportional timeline; with "Ordinal", data points are equally spaced and time gaps are not visible.
window.onload = async () => { const { spread, designer } = createSpreadAndDesigner(); await initDataManager(spread); const DataChartType = GC.Spread.Sheets.DataCharts.DataChartType; const DateMode = GC.Spread.Sheets.DataCharts.DateMode; const sheet1 = spread.getSheet(0); sheet1.name('Date Mode Binding'); // Column addChartToSheet(sheet1, 'DateScaleColumn', 20, 20, 500, 250, 'Date', DataChartType.column, DateMode); addChartToSheet(sheet1, 'OrdinalScaleColumn', 540, 20, 500, 250, 'Ordinal', DataChartType.column, DateMode); // Bar addChartToSheet(sheet1, 'DateScaleBar', 20, 290, 500, 250, 'Date', DataChartType.bar, DateMode); addChartToSheet(sheet1, 'OrdinalScaleBar', 540, 290, 500, 250, 'Ordinal', DataChartType.bar, DateMode); // Area addChartToSheet(sheet1, 'DateScaleArea', 20, 560, 500, 250, 'Date', DataChartType.area, DateMode); addChartToSheet(sheet1, 'OrdinalScaleArea', 540, 560, 500, 250, 'Ordinal', DataChartType.area, DateMode); // Line addChartToSheet(sheet1, 'DateScaleLine', 20, 830, 500, 250, 'Date', DataChartType.line, DateMode); addChartToSheet(sheet1, 'OrdinalScaleLine', 540, 830, 500, 250, 'Ordinal', DataChartType.line, DateMode); // Rose addChartToSheet(sheet1, 'OrdinalScaleRose', 20, 1100, 500, 250, 'Ordinal', DataChartType.rose, DateMode); // PolarCoordinatesBar addChartToSheet(sheet1, 'OrdinalScalePolarBar', 540, 1100, 500, 250, 'Ordinal', DataChartType.polarCoordinatesBar, DateMode); // Radar addChartToSheet(sheet1, 'OrdinalScaleRadar', 20, 1370, 500, 250, 'Ordinal', DataChartType.radar, DateMode); // Waterfall addChartToSheet(sheet1, 'OrdinalScaleWaterfall', 540, 1370, 500, 250, 'Ordinal', DataChartType.waterfall, DateMode); spread.addSheet(1); const sheet2 = spread.getSheet(1); sheet2.name('Hierarchical Date Mode'); // Column addHierarchicalChart(sheet2, 'DateScaleColumn', 20, 20, 500, 250, 'Date', DataChartType.column, DateMode); addHierarchicalChart(sheet2, 'OrdinalScaleColumn', 540, 20, 500, 250, 'Ordinal', DataChartType.column, DateMode); // Bar addHierarchicalChart(sheet2, 'DateScaleBar', 20, 290, 500, 250, 'Date', DataChartType.bar, DateMode); addHierarchicalChart(sheet2, 'OrdinalScaleBar', 540, 290, 500, 250, 'Ordinal', DataChartType.bar, DateMode); // Area addHierarchicalChart(sheet2, 'DateScaleArea', 20, 560, 500, 250, 'Date', DataChartType.area, DateMode); addHierarchicalChart(sheet2, 'OrdinalScaleArea', 540, 560, 500, 250, 'Ordinal', DataChartType.area, DateMode); // Line addHierarchicalChart(sheet2, 'DateScaleLine', 20, 830, 500, 250, 'Date', DataChartType.line, DateMode); addHierarchicalChart(sheet2, 'OrdinalScaleLine', 540, 830, 500, 250, 'Ordinal', DataChartType.line, DateMode); spread.addSheet(2); const sheet3 = spread.getSheet(2); sheet3.name('Stock Date Mode'); addStockChart(sheet3, 'DateScaleChart', 20, 20, 500, 300, 'Date', DataChartType.candlestick, DateMode); addStockChart(sheet3, 'OrdinalScaleChart', 540, 20, 500, 300, 'Ordinal', DataChartType.candlestick, DateMode); spread.addSheet(3); const sheet4 = spread.getSheet(3); sheet4.name('Custom Date Format'); // Single month binding: Month MMM addDateFormatChart(sheet4, 'SingleMonthFormat', 20, 20, 500, 250, 'Ordinal', DataChartType.column, { field: 'Date', dateMode: DateMode.month }, [{ value: '"Month "MMM' }], 'Monthly Reading Summary'); // Single quarter binding: QQQ period addDateFormatChart(sheet4, 'SingleQuarterFormat', 540, 20, 500, 250, 'Ordinal', DataChartType.column, { field: 'Date', dateMode: DateMode.quarter }, [{ value: 'QQQ" period"' }], 'Quarterly Reading Period Summary'); // Multi-level binding: Year yyyy > Qtr QQQ > Month M addDateFormatChart(sheet4, 'YearQuarterMonthFormat', 20, 290, 500, 250, 'Ordinal', DataChartType.column, { field: 'Date', dateMode: DateMode.year, child: { field: 'Date', dateMode: DateMode.quarter, child: { field: 'Date', dateMode: DateMode.month } } }, [{ value: '"Year "yyyy' }, { value: '"Qtr "QQQ' }, { value: '"Month "M' }], 'Reading Activity Calendar'); // Multi-level binding: Month MMM > Day d addDateFormatChart(sheet4, 'MonthDayFormat', 540, 290, 500, 250, 'Ordinal', DataChartType.column, { field: 'Date', dateMode: DateMode.month, child: { field: 'Date', dateMode: DateMode.day } }, [{ value: '"Period "MMM' }, { value: '"Day "d' }], 'Daily Reading Detail by Period'); spread.setActiveSheetIndex(0); spread.getActiveSheet().dataCharts.all()[0].isSelected(true); if (designer) { designer.refresh(); } } function addChartToSheet(sheet, chartName, x, y, width, height, scaleType, chartType, DateMode) { const AxisType = GC.Spread.Sheets.DataCharts.AxisType; const dataChart = sheet.dataCharts.add(chartName, x, y, width, height, chartType); const axes = [{ type: AxisType.x, scale: { type: scaleType } }]; dataChart.setChartConfig({ tableName: 'StudentActivity', plots: [{ type: chartType, encodings: { values: [{ field: 'ReadingMinutes', aggregate: GC.Spread.Sheets.DataCharts.Aggregate.sum }], category: { field: 'Date', dateMode: DateMode.month }, color: { field: 'Student' }, details: [{ field: 'Student' }], }, }], config: { header: { title: scaleType + ' Scale — Student Reading Activity (2024)', padding: { left: 10, right: 10, top: 10, bottom: 10 }, textStyle: { fontSize: 14, alignment: GC.Spread.Sheets.DataCharts.HAlign.left, }, }, plotAreas: [{ axes: axes }], }, }); } function addHierarchicalChart(sheet, chartName, x, y, width, height, scaleType, chartType, DateMode) { const AxisType = GC.Spread.Sheets.DataCharts.AxisType; const dataChart = sheet.dataCharts.add(chartName, x, y, width, height, chartType); const hierarchicalCategory = { field: 'Date', dateMode: DateMode.year, child: { field: 'Date', dateMode: DateMode.quarter, child: { field: 'Date', dateMode: DateMode.month } } }; const axes = [{ type: AxisType.x, scale: { type: scaleType } }]; dataChart.setChartConfig({ tableName: 'StudentActivity', plots: [{ type: chartType, encodings: { values: [{ field: 'ReadingMinutes', aggregate: GC.Spread.Sheets.DataCharts.Aggregate.sum }], category: hierarchicalCategory, color: { field: 'Student' }, details: [{ field: 'Student' }], }, }], config: { header: { title: scaleType + ' Scale — Student Reading Activity by Year/Quarter/Month', padding: { left: 10, right: 10, top: 10, bottom: 10 }, textStyle: { fontSize: 14, alignment: GC.Spread.Sheets.DataCharts.HAlign.left, }, }, plotAreas: [{ axes: axes }], }, }); } function addStockChart(sheet, chartName, x, y, width, height, scaleType, chartType, DateMode) { const AxisType = GC.Spread.Sheets.DataCharts.AxisType; const dataChart = sheet.dataCharts.add(chartName, x, y, width, height, chartType); const hierarchicalCategory = { field: 'Date', dateMode: DateMode.month, child: { field: 'Date', dateMode: DateMode.day } }; const axes = [{ type: AxisType.x, scale: { type: scaleType } }]; dataChart.setChartConfig({ tableName: 'StockPrice', plots: [{ type: chartType, encodings: { values: [{ vectors: { open: { field: 'Open' }, high: { field: 'High' }, low: { field: 'Low' }, close: { field: 'Close' }, }, }], category: hierarchicalCategory, }, }], config: { header: { title: scaleType + ' Scale — NovaPulse Corp (NVPL, fictional) Daily Price Q1 2025', padding: { left: 10, right: 10, top: 10, bottom: 10 }, textStyle: { fontSize: 14, alignment: GC.Spread.Sheets.DataCharts.HAlign.left, }, }, plotAreas: [{ axes: axes }], }, }); } function addDateFormatChart(sheet, chartName, x, y, width, height, scaleType, chartType, category, formats, title) { const AxisType = GC.Spread.Sheets.DataCharts.AxisType; const dataChart = sheet.dataCharts.add(chartName, x, y, width, height, chartType); const axes = [{ type: AxisType.x, scale: { type: scaleType }, format: formats, }]; dataChart.setChartConfig({ tableName: 'StudentActivity', plots: [{ type: chartType, encodings: { values: [{ field: 'ReadingMinutes', aggregate: GC.Spread.Sheets.DataCharts.Aggregate.sum }], category: category, color: { field: 'Student' }, details: [{ field: 'Student' }], }, }], config: { header: { title: scaleType + ' Scale — ' + title, padding: { left: 10, right: 10, top: 10, bottom: 10 }, textStyle: { fontSize: 14, alignment: GC.Spread.Sheets.DataCharts.HAlign.left, }, }, plotAreas: [{ axes: axes }], }, }); } function createSpreadAndDesigner() { const demoHost = document.getElementById('demo-host'); if (window !== top) { const spread = new GC.Spread.Sheets.Workbook('spread-host', { sheetCount: 1 }); new GC.Spread.Sheets.DataCharts.DataChartConfigPanel('panel-host', spread); return { spread } } else { const designer = new GC.Spread.Sheets.Designer.Designer(demoHost, undefined, undefined, { sheetCount: 1 }); return { designer, spread: designer.getWorkbook(), } } } async function initDataManager(spread) { const dataManager = spread.dataManager(); const activityData = []; // 2024 Chinese elementary school calendar // Spring semester: Jan - Jun | Fall semester: Sep - Dec // Winter break: Feb | Summer break: Jul - Aug const schoolMonths = [ { month: 1, days: [8, 15, 22] }, { month: 3, days: [4, 11, 18, 25] }, { month: 4, days: [1, 8, 15, 22, 29] }, { month: 5, days: [6, 13, 20, 27] }, { month: 6, days: [3, 10, 17, 24] }, { month: 9, days: [2, 9, 16, 23, 30] }, { month: 10, days: [7, 14, 21, 28] }, { month: 11, days: [4, 11, 18, 25] }, { month: 12, days: [2, 9, 16, 23] }, ]; const dates = []; for (const { month, days } of schoolMonths) { for (const day of days) { dates.push(new Date(2024, month - 1, day)); } } const variations = [5, -3, 8, -2, 10, -5, 7, -1, 6, -4, 3, -7, 9, -3, 4, -6, 8, -2, 5, -1, 7, -4, 6, -3, 10, -5, 4, -2, 8, -6, 5, -3, 7, -1, 9, -4, 6, -2]; const studentConfigs = [ { name: 'Alice', base: 35, varOffset: 0 }, { name: 'Bob', base: 28, varOffset: 19 }, ]; for (const student of studentConfigs) { for (let i = 0; i < dates.length; i++) { const variation = variations[(i + student.varOffset) % variations.length]; activityData.push({ Date: dates[i], Student: student.name, ReadingMinutes: Math.max(10, student.base + variation), }); } } await dataManager.addTable('StudentActivity', { data: activityData, schema: { columns: { Date: { dataType: 'date' }, } } }).fetch(); // Simulated stock data for fictional company NovaPulse Corp (NVPL) // NASDAQ trading hours: Mon-Fri, closed on weekends and holidays const stockData = []; let price = 142.50; const tradingDays = []; // Generate Q1 2025 trading days (skip weekends) for (let m = 0; m < 3; m++) { const daysInMonth = new Date(2025, m + 1, 0).getDate(); for (let d = 1; d <= daysInMonth; d++) { const date = new Date(2025, m, d); const dow = date.getDay(); if (dow !== 0 && dow !== 6) { // Skip New Year's Day and Presidents' Day if (m === 0 && d === 1) continue; if (m === 1 && d === 17) continue; tradingDays.push(date); } } } for (const date of tradingDays) { const change = (Math.random() - 0.48) * 6; const open = price; const close = price + change; const high = Math.max(open, close) + Math.random() * 3; const low = Math.min(open, close) - Math.random() * 3; stockData.push({ Date: date, Open: Math.round(open * 100) / 100, High: Math.round(high * 100) / 100, Low: Math.round(low * 100) / 100, Close: Math.round(close * 100) / 100, }); price = close; } await dataManager.addTable('StockPrice', { data: stockData, schema: { columns: { Date: { dataType: 'date' }, } } }).fetch(); }
<!doctype html> <html style="height:100%;font-size:14px;"> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <link rel="stylesheet" type="text/css" href="$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets/styles/gc.spread.sheets.excel2013white.css"> <link rel="stylesheet" type="text/css" href="$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-designer/styles/gc.spread.sheets.designer.light.min.css"> <link rel="stylesheet" type="text/css" href="styles.css"> <script src="$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets/dist/gc.spread.sheets.all.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-print/dist/gc.spread.sheets.print.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-shapes/dist/gc.spread.sheets.shapes.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-datacharts-addon/dist/gc.spread.sheets.datacharts.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-tablesheet/dist/gc.spread.sheets.tablesheet.min.js" type="text/javascript"></script> <script src="$DEMOROOT$/spread/source/js/license.js" type="text/javascript"></script> <script> const designerDependencyScripts = [ '$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-charts/dist/gc.spread.sheets.charts.min.js', '$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-barcode/dist/gc.spread.sheets.barcode.min.js', '$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-pdf/dist/gc.spread.sheets.pdf.min.js', '$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-pivot-addon/dist/gc.spread.pivot.pivottables.min.js', '$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-slicers/dist/gc.spread.sheets.slicers.min.js', '$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-reportsheet-addon/dist/gc.spread.report.reportsheet.min.js', '$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-ganttsheet/dist/gc.spread.sheets.ganttsheet.min.js', '$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-formula-panel/dist/gc.spread.sheets.formulapanel.min.js', '$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-io/dist/gc.spread.sheets.io.min.js', '$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-designer-resources-en/dist/gc.spread.sheets.designer.resource.en.min.js', '$DEMOROOT$/en/purejs/node_modules/@mescius/spread-sheets-designer/dist/gc.spread.sheets.designer.all.min.js', '$DEMOROOT$/spread/source/js/designer/license.js', ] function appendScriptNode (src) { const script = document.createElement('script'); script.src = src; script.async = false; script.type = 'text/javascript'; document.head.appendChild(script); } if (top === window) { // not in iframe designerDependencyScripts.forEach(appendScriptNode); } </script> <script src="app.js" type="text/javascript"></script> </head> <body> <div class="sample-tutorial"> <div id="demo-host"> <div id="spread-host"></div> <div id="panel-host"></div> </div> </div> </body> </html>
body { position: absolute; top: 0; bottom: 0; left: 0; right: 0; } .sample-tutorial { position: relative; height: 100%; overflow: hidden; } #demo-host { width: 100%; height: 100%; overflow: hidden; float: left; } #spread-host { position: absolute; top: 0; left: 0; width: calc(100% - 380px); height: 100%; } #panel-host { position: absolute; top: 0; right: 0; width: 380px; height: 100%; }