diff --git a/examples/compiled/bar_binned_yearmonth_grouped.png b/examples/compiled/bar_binned_yearmonth_grouped.png
new file mode 100644
index 00000000000..96f3a926395
Binary files /dev/null and b/examples/compiled/bar_binned_yearmonth_grouped.png differ
diff --git a/examples/compiled/bar_binned_yearmonth_grouped.svg b/examples/compiled/bar_binned_yearmonth_grouped.svg
new file mode 100644
index 00000000000..0d88d3b01a0
--- /dev/null
+++ b/examples/compiled/bar_binned_yearmonth_grouped.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/compiled/bar_binned_yearmonth_grouped.vg.json b/examples/compiled/bar_binned_yearmonth_grouped.vg.json
new file mode 100644
index 00000000000..4d0ef4b7d47
--- /dev/null
+++ b/examples/compiled/bar_binned_yearmonth_grouped.vg.json
@@ -0,0 +1,158 @@
+{
+ "$schema": "https://vega.github.io/schema/vega/v5.json",
+ "description": "Stock price over time.",
+ "background": "white",
+ "padding": 5,
+ "width": 200,
+ "height": 200,
+ "style": "cell",
+ "data": [
+ {
+ "name": "source_0",
+ "url": "data/stocks.csv",
+ "format": {"type": "csv", "parse": {"date": "date"}},
+ "transform": [
+ {
+ "type": "filter",
+ "expr": "inrange(time(datum[\"date\"]), [time(datetime(2005, 0, 1, 0, 0, 0, 0)), time(datetime(2005, 2, 1, 0, 0, 0, 0))])"
+ },
+ {
+ "type": "formula",
+ "expr": "timeOffset('month', datum['date'], 1)",
+ "as": "date_end"
+ },
+ {
+ "type": "stack",
+ "groupby": ["date", "symbol"],
+ "field": "price",
+ "sort": {"field": [], "order": []},
+ "as": ["price_start", "price_end"],
+ "offset": "zero"
+ },
+ {
+ "type": "filter",
+ "expr": "(isDate(datum[\"date\"]) || (isValid(datum[\"date\"]) && isFinite(+datum[\"date\"]))) && isValid(datum[\"price\"]) && isFinite(+datum[\"price\"])"
+ }
+ ]
+ }
+ ],
+ "marks": [
+ {
+ "name": "marks",
+ "type": "rect",
+ "style": ["bar"],
+ "from": {"data": "source_0"},
+ "encode": {
+ "update": {
+ "fill": {"scale": "color", "field": "symbol"},
+ "ariaRoleDescription": {"value": "bar"},
+ "description": {
+ "signal": "\"date: \" + (timeFormat(datum[\"date\"], timeUnitSpecifier([\"year\",\"month\"], {\"year-month\":\"%b %Y \",\"year-month-date\":\"%b %d, %Y \"}))) + \"; price: \" + (format(datum[\"price\"], \"\")) + \"; symbol: \" + (isValid(datum[\"symbol\"]) ? datum[\"symbol\"] : \"\"+datum[\"symbol\"])"
+ },
+ "xc": {
+ "scale": "x",
+ "field": "date",
+ "offset": {"scale": "xOffset", "field": "symbol", "band": 0.5}
+ },
+ "width": {"signal": "max(0.25, bandwidth('xOffset'))"},
+ "y": {"scale": "y", "field": "price_end"},
+ "y2": {"scale": "y", "field": "price_start"}
+ }
+ }
+ }
+ ],
+ "scales": [
+ {
+ "name": "x",
+ "type": "time",
+ "domain": {"data": "source_0", "fields": ["date", "date_end"]},
+ "range": [0, {"signal": "width"}]
+ },
+ {
+ "name": "y",
+ "type": "linear",
+ "domain": {"data": "source_0", "fields": ["price_start", "price_end"]},
+ "range": [{"signal": "height"}, 0],
+ "nice": true,
+ "zero": true
+ },
+ {
+ "name": "xOffset",
+ "type": "band",
+ "domain": {"data": "source_0", "field": "symbol", "sort": true},
+ "range": [
+ {
+ "signal": "0.1 * (scale('x', datetime(2001, 1, 1, 0, 0, 0, 0)) - scale('x', datetime(2001, 0, 1, 0, 0, 0, 0)))"
+ },
+ {
+ "signal": "0.9 * (scale('x', datetime(2001, 1, 1, 0, 0, 0, 0)) - scale('x', datetime(2001, 0, 1, 0, 0, 0, 0)))"
+ }
+ ]
+ },
+ {
+ "name": "color",
+ "type": "ordinal",
+ "domain": {"data": "source_0", "field": "symbol", "sort": true},
+ "range": "category"
+ }
+ ],
+ "axes": [
+ {
+ "scale": "x",
+ "orient": "bottom",
+ "gridScale": "y",
+ "grid": true,
+ "tickCount": {"signal": "ceil(width/40)"},
+ "tickMinStep": {
+ "signal": "datetime(2001, 1, 1, 0, 0, 0, 0) - datetime(2001, 0, 1, 0, 0, 0, 0)"
+ },
+ "domain": false,
+ "labels": false,
+ "aria": false,
+ "maxExtent": 0,
+ "minExtent": 0,
+ "ticks": false,
+ "zindex": 0
+ },
+ {
+ "scale": "y",
+ "orient": "left",
+ "gridScale": "x",
+ "grid": true,
+ "tickCount": {"signal": "ceil(height/40)"},
+ "domain": false,
+ "labels": false,
+ "aria": false,
+ "maxExtent": 0,
+ "minExtent": 0,
+ "ticks": false,
+ "zindex": 0
+ },
+ {
+ "scale": "x",
+ "orient": "bottom",
+ "grid": false,
+ "title": "date",
+ "format": {
+ "signal": "timeUnitSpecifier([\"year\",\"month\"], {\"year-month\":\"%b %Y \",\"year-month-date\":\"%b %d, %Y \"})"
+ },
+ "labelFlush": true,
+ "labelOverlap": true,
+ "tickCount": {"signal": "ceil(width/40)"},
+ "tickMinStep": {
+ "signal": "datetime(2001, 1, 1, 0, 0, 0, 0) - datetime(2001, 0, 1, 0, 0, 0, 0)"
+ },
+ "zindex": 0
+ },
+ {
+ "scale": "y",
+ "orient": "left",
+ "grid": false,
+ "title": "price",
+ "labelOverlap": true,
+ "tickCount": {"signal": "ceil(height/40)"},
+ "zindex": 0
+ }
+ ],
+ "legends": [{"fill": "color", "symbolType": "square", "title": "symbol"}]
+}
diff --git a/examples/compiled/bar_binned_yearmonth_label.png b/examples/compiled/bar_binned_yearmonth_label.png
new file mode 100644
index 00000000000..06d51e360f3
Binary files /dev/null and b/examples/compiled/bar_binned_yearmonth_label.png differ
diff --git a/examples/compiled/bar_binned_yearmonth_label.svg b/examples/compiled/bar_binned_yearmonth_label.svg
new file mode 100644
index 00000000000..a64a79e85d3
--- /dev/null
+++ b/examples/compiled/bar_binned_yearmonth_label.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/compiled/bar_binned_yearmonth_label.vg.json b/examples/compiled/bar_binned_yearmonth_label.vg.json
new file mode 100644
index 00000000000..5b9b9bcb5e7
--- /dev/null
+++ b/examples/compiled/bar_binned_yearmonth_label.vg.json
@@ -0,0 +1,197 @@
+{
+ "$schema": "https://vega.github.io/schema/vega/v5.json",
+ "description": "Google's stock price over time.",
+ "background": "white",
+ "padding": 5,
+ "width": 200,
+ "height": 200,
+ "style": "cell",
+ "data": [
+ {
+ "name": "source_0",
+ "url": "data/stocks.csv",
+ "format": {"type": "csv", "parse": {"date": "date"}},
+ "transform": [
+ {"type": "filter", "expr": "datum.symbol==='GOOG'"},
+ {
+ "type": "filter",
+ "expr": "inrange(time(datum[\"date\"]), [time(datetime(2005, 0, 1, 0, 0, 0, 0)), time(datetime(2005, 2, 1, 0, 0, 0, 0))])"
+ },
+ {
+ "type": "formula",
+ "expr": "timeOffset('month', datum['date'], 1)",
+ "as": "date_end"
+ }
+ ]
+ },
+ {
+ "name": "data_0",
+ "source": "source_0",
+ "transform": [
+ {
+ "type": "filter",
+ "expr": "(isDate(datum[\"date\"]) || (isValid(datum[\"date\"]) && isFinite(+datum[\"date\"]))) && isValid(datum[\"price\"]) && isFinite(+datum[\"price\"])"
+ }
+ ]
+ },
+ {
+ "name": "data_1",
+ "source": "source_0",
+ "transform": [
+ {
+ "type": "stack",
+ "groupby": ["date"],
+ "field": "price",
+ "sort": {"field": [], "order": []},
+ "as": ["price_start", "price_end"],
+ "offset": "zero"
+ },
+ {
+ "type": "filter",
+ "expr": "(isDate(datum[\"date\"]) || (isValid(datum[\"date\"]) && isFinite(+datum[\"date\"]))) && isValid(datum[\"price\"]) && isFinite(+datum[\"price\"])"
+ }
+ ]
+ }
+ ],
+ "marks": [
+ {
+ "name": "layer_0_marks",
+ "type": "rect",
+ "style": ["bar"],
+ "from": {"data": "data_1"},
+ "encode": {
+ "update": {
+ "fill": {"value": "#4c78a8"},
+ "ariaRoleDescription": {"value": "bar"},
+ "description": {
+ "signal": "\"date: \" + (timeFormat(datum[\"date\"], timeUnitSpecifier([\"year\",\"month\"], {\"year-month\":\"%b %Y \",\"year-month-date\":\"%b %d, %Y \"}))) + \"; price: \" + (format(datum[\"price\"], \"\"))"
+ },
+ "x2": {
+ "scale": "x",
+ "field": "date",
+ "offset": {
+ "signal": "0.5 + (abs(scale(\"x\", datum[\"date_end\"]) - scale(\"x\", datum[\"date\"])) < 0.25 ? -0.5 * (0.25 - (abs(scale(\"x\", datum[\"date_end\"]) - scale(\"x\", datum[\"date\"])))) : 0.5)"
+ }
+ },
+ "x": {
+ "scale": "x",
+ "field": "date_end",
+ "offset": {
+ "signal": "0.5 + (abs(scale(\"x\", datum[\"date_end\"]) - scale(\"x\", datum[\"date\"])) < 0.25 ? 0.5 * (0.25 - (abs(scale(\"x\", datum[\"date_end\"]) - scale(\"x\", datum[\"date\"])))) : -0.5)"
+ }
+ },
+ "y": {"scale": "y", "field": "price_end"},
+ "y2": {"scale": "y", "field": "price_start"}
+ }
+ }
+ },
+ {
+ "name": "layer_1_marks",
+ "type": "text",
+ "style": ["text"],
+ "from": {"data": "data_0"},
+ "encode": {
+ "update": {
+ "baseline": {"value": "bottom"},
+ "fill": {"value": "black"},
+ "description": {
+ "signal": "\"date: \" + (timeFormat(datum[\"date\"], timeUnitSpecifier([\"year\",\"month\"], {\"year-month\":\"%b %Y \",\"year-month-date\":\"%b %d, %Y \"}))) + \"; price: \" + (format(datum[\"price\"], \"\"))"
+ },
+ "x": {
+ "signal": "scale(\"x\", 0.5 * datum[\"date\"] + 0.5 * datum[\"date_end\"])"
+ },
+ "y": {"scale": "y", "field": "price"},
+ "text": {"signal": "format(datum[\"price\"], \"\")"},
+ "align": {"value": "center"}
+ }
+ }
+ }
+ ],
+ "scales": [
+ {
+ "name": "x",
+ "type": "time",
+ "domain": {
+ "fields": [
+ {"data": "data_1", "field": "date"},
+ {"data": "data_1", "field": "date_end"},
+ {"data": "data_0", "field": "date"},
+ {"data": "data_0", "field": "date_end"}
+ ]
+ },
+ "range": [0, {"signal": "width"}]
+ },
+ {
+ "name": "y",
+ "type": "linear",
+ "domain": {
+ "fields": [
+ {"data": "data_1", "field": "price_start"},
+ {"data": "data_1", "field": "price_end"},
+ {"data": "data_0", "field": "price"}
+ ]
+ },
+ "range": [{"signal": "height"}, 0],
+ "nice": true,
+ "zero": true
+ }
+ ],
+ "axes": [
+ {
+ "scale": "x",
+ "orient": "bottom",
+ "gridScale": "y",
+ "grid": true,
+ "tickCount": {"signal": "ceil(width/40)"},
+ "tickMinStep": {
+ "signal": "datetime(2001, 1, 1, 0, 0, 0, 0) - datetime(2001, 0, 1, 0, 0, 0, 0)"
+ },
+ "domain": false,
+ "labels": false,
+ "aria": false,
+ "maxExtent": 0,
+ "minExtent": 0,
+ "ticks": false,
+ "zindex": 0
+ },
+ {
+ "scale": "y",
+ "orient": "left",
+ "gridScale": "x",
+ "grid": true,
+ "tickCount": {"signal": "ceil(height/40)"},
+ "domain": false,
+ "labels": false,
+ "aria": false,
+ "maxExtent": 0,
+ "minExtent": 0,
+ "ticks": false,
+ "zindex": 0
+ },
+ {
+ "scale": "x",
+ "orient": "bottom",
+ "grid": false,
+ "title": "date",
+ "format": {
+ "signal": "timeUnitSpecifier([\"year\",\"month\"], {\"year-month\":\"%b %Y \",\"year-month-date\":\"%b %d, %Y \"})"
+ },
+ "labelFlush": true,
+ "labelOverlap": true,
+ "tickCount": {"signal": "ceil(width/40)"},
+ "tickMinStep": {
+ "signal": "datetime(2001, 1, 1, 0, 0, 0, 0) - datetime(2001, 0, 1, 0, 0, 0, 0)"
+ },
+ "zindex": 0
+ },
+ {
+ "scale": "y",
+ "orient": "left",
+ "grid": false,
+ "title": "price",
+ "labelOverlap": true,
+ "tickCount": {"signal": "ceil(height/40)"},
+ "zindex": 0
+ }
+ ]
+}
diff --git a/examples/compiled/bar_binnedyearmonth.png b/examples/compiled/bar_binnedyearmonth.png
new file mode 100644
index 00000000000..a1110895bb0
Binary files /dev/null and b/examples/compiled/bar_binnedyearmonth.png differ
diff --git a/examples/compiled/bar_binnedyearmonth.svg b/examples/compiled/bar_binnedyearmonth.svg
new file mode 100644
index 00000000000..371924fe1a9
--- /dev/null
+++ b/examples/compiled/bar_binnedyearmonth.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/examples/compiled/bar_binnedyearmonth.vg.json b/examples/compiled/bar_binnedyearmonth.vg.json
new file mode 100644
index 00000000000..d4f5d85d577
--- /dev/null
+++ b/examples/compiled/bar_binnedyearmonth.vg.json
@@ -0,0 +1,143 @@
+{
+ "$schema": "https://vega.github.io/schema/vega/v5.json",
+ "description": "Google's stock price over time.",
+ "background": "white",
+ "padding": 5,
+ "width": 200,
+ "height": 200,
+ "style": "cell",
+ "data": [
+ {
+ "name": "source_0",
+ "url": "data/stocks.csv",
+ "format": {"type": "csv", "parse": {"date": "date"}},
+ "transform": [
+ {"type": "filter", "expr": "datum.symbol==='GOOG'"},
+ {
+ "type": "formula",
+ "expr": "timeOffset('month', datum['date'], 1)",
+ "as": "date_end"
+ },
+ {
+ "type": "stack",
+ "groupby": ["date"],
+ "field": "price",
+ "sort": {"field": [], "order": []},
+ "as": ["price_start", "price_end"],
+ "offset": "zero"
+ },
+ {
+ "type": "filter",
+ "expr": "(isDate(datum[\"date\"]) || (isValid(datum[\"date\"]) && isFinite(+datum[\"date\"]))) && isValid(datum[\"price\"]) && isFinite(+datum[\"price\"])"
+ }
+ ]
+ }
+ ],
+ "marks": [
+ {
+ "name": "marks",
+ "type": "rect",
+ "style": ["bar"],
+ "from": {"data": "source_0"},
+ "encode": {
+ "update": {
+ "fill": {"value": "#4c78a8"},
+ "ariaRoleDescription": {"value": "bar"},
+ "description": {
+ "signal": "\"date: \" + (timeFormat(datum[\"date\"], timeUnitSpecifier([\"year\",\"month\"], {\"year-month\":\"%b %Y \",\"year-month-date\":\"%b %d, %Y \"}))) + \"; price: \" + (format(datum[\"price\"], \"\"))"
+ },
+ "x2": {
+ "scale": "x",
+ "field": "date",
+ "offset": {
+ "signal": "0.5 + (abs(scale(\"x\", datum[\"date_end\"]) - scale(\"x\", datum[\"date\"])) < 0.25 ? -0.5 * (0.25 - (abs(scale(\"x\", datum[\"date_end\"]) - scale(\"x\", datum[\"date\"])))) : 0.5)"
+ }
+ },
+ "x": {
+ "scale": "x",
+ "field": "date_end",
+ "offset": {
+ "signal": "0.5 + (abs(scale(\"x\", datum[\"date_end\"]) - scale(\"x\", datum[\"date\"])) < 0.25 ? 0.5 * (0.25 - (abs(scale(\"x\", datum[\"date_end\"]) - scale(\"x\", datum[\"date\"])))) : -0.5)"
+ }
+ },
+ "y": {"scale": "y", "field": "price_end"},
+ "y2": {"scale": "y", "field": "price_start"}
+ }
+ }
+ }
+ ],
+ "scales": [
+ {
+ "name": "x",
+ "type": "time",
+ "domain": {"data": "source_0", "fields": ["date", "date_end"]},
+ "range": [0, {"signal": "width"}]
+ },
+ {
+ "name": "y",
+ "type": "linear",
+ "domain": {"data": "source_0", "fields": ["price_start", "price_end"]},
+ "range": [{"signal": "height"}, 0],
+ "nice": true,
+ "zero": true
+ }
+ ],
+ "axes": [
+ {
+ "scale": "x",
+ "orient": "bottom",
+ "gridScale": "y",
+ "grid": true,
+ "tickCount": {"signal": "ceil(width/40)"},
+ "tickMinStep": {
+ "signal": "datetime(2001, 1, 1, 0, 0, 0, 0) - datetime(2001, 0, 1, 0, 0, 0, 0)"
+ },
+ "domain": false,
+ "labels": false,
+ "aria": false,
+ "maxExtent": 0,
+ "minExtent": 0,
+ "ticks": false,
+ "zindex": 0
+ },
+ {
+ "scale": "y",
+ "orient": "left",
+ "gridScale": "x",
+ "grid": true,
+ "tickCount": {"signal": "ceil(height/40)"},
+ "domain": false,
+ "labels": false,
+ "aria": false,
+ "maxExtent": 0,
+ "minExtent": 0,
+ "ticks": false,
+ "zindex": 0
+ },
+ {
+ "scale": "x",
+ "orient": "bottom",
+ "grid": false,
+ "title": "date",
+ "format": {
+ "signal": "timeUnitSpecifier([\"year\",\"month\"], {\"year-month\":\"%b %Y \",\"year-month-date\":\"%b %d, %Y \"})"
+ },
+ "labelFlush": true,
+ "labelOverlap": true,
+ "tickCount": {"signal": "ceil(width/40)"},
+ "tickMinStep": {
+ "signal": "datetime(2001, 1, 1, 0, 0, 0, 0) - datetime(2001, 0, 1, 0, 0, 0, 0)"
+ },
+ "zindex": 0
+ },
+ {
+ "scale": "y",
+ "orient": "left",
+ "grid": false,
+ "title": "price",
+ "labelOverlap": true,
+ "tickCount": {"signal": "ceil(height/40)"},
+ "zindex": 0
+ }
+ ]
+}
diff --git a/examples/specs/normalized/bar_binned_yearmonth_label_normalized.vl.json b/examples/specs/normalized/bar_binned_yearmonth_label_normalized.vl.json
new file mode 100644
index 00000000000..4ebf40095b9
--- /dev/null
+++ b/examples/specs/normalized/bar_binned_yearmonth_label_normalized.vl.json
@@ -0,0 +1,42 @@
+{
+ "$schema": "https://vega.github.io/schema/vega-lite/v5.json",
+ "description": "Google's stock price over time.",
+ "data": {"url": "data/stocks.csv"},
+ "layer": [
+ {
+ "mark": "bar",
+ "encoding": {
+ "x": {
+ "timeUnit": "binnedyearmonth",
+ "field": "date",
+ "type": "temporal",
+ "bandPosition": 0.5
+ },
+ "y": {"field": "price", "type": "quantitative"}
+ }
+ },
+ {
+ "mark": {"type": "text", "baseline": "bottom"},
+ "encoding": {
+ "x": {
+ "timeUnit": "binnedyearmonth",
+ "field": "date",
+ "type": "temporal",
+ "bandPosition": 0.5
+ },
+ "y": {"field": "price", "type": "quantitative"},
+ "text": {"field": "price", "type": "quantitative"}
+ }
+ }
+ ],
+ "transform": [
+ {"filter": "datum.symbol==='GOOG'"},
+ {
+ "filter": {
+ "timeUnit": "binnedyearmonth",
+ "field": "date",
+ "range": [{"year": 2005, "month": 1}, {"year": 2005, "month": 3}]
+ }
+ }
+ ]
+}
\ No newline at end of file