My website provides many examples of the Quarto publishing and the Dec measurement systems in action. I leverage Quarto support for the Observable data analysis and visualization system to create animated and interactive graphics like the clocks🕓and bar📊charts below, which display a Dec time like the one on the left side of the navigation bar (navbar) above.
Dec times are measured in fractional days. The shortest, longest, and thinnest clock🕓hands and the top, middle, and bottom bars📊indicate the decidays, millidays, and centimillidays, respectively, of the time since the start, +, or until the end, -, of the day in the Dec time zone, , selected by the red⭕️circle on the solar☀️terminator map🗺️beneath the bars📊.
barChart = {const W = width;const H =88;const barX =1;const firstBarY =1;const svg = d3.create("svg").attr("width", W).attr("viewBox", [0,0, W * (W <370?.73: W <420?.76: W <470?.79: W <520?.81: W <570?.83: W <620?.84: W <670?.85: W <720?.86:.87), H]);const xRange = [0, W -100];const scaleDD = d3.scaleLinear().domain([0,10]).range(xRange);const scaleMandB = d3.scaleLinear().domain([0,100]).range(xRange);// Background bars to show where 100% lies svg.selectAll('.background').data(['dd',"mils",'beats']).enter().append('rect').attr('class','background timeBar').attr('width', W-100).attr('y', (d,i)=>i*30+firstBarY)// Beats svg.append('rect').attr('class','timeBar').attr('y', firstBarY+60).attr('width', d =>scaleMandB(Number(barBeats))) svg.append('rect').attr('class','timeBarFull').attr('y', firstBarY+60).attr('width', d =>scaleMandB(barBeats))// Cents/Mils svg.append('rect').attr('class','timeBar').attr('y', firstBarY).attr('width', d =>scaleDD(Number(barDD)+Number(barMils)/100+Number(barBeats)/10000)) svg.append('rect').attr('class','timeBarFull').attr('y', firstBarY).attr('width', d =>scaleDD(barDD)) svg.append('rect').attr('class','timeBar').attr('y', firstBarY+30).attr('width', d =>scaleMandB(Number(barMils)+Number(barBeats)/100)) svg.append('rect').attr('class','timeBarFull').attr('y', firstBarY+30).attr('width', d =>scaleMandB(barMils))// Cent ticks svg.selectAll('.tickC').data(d3.range(width >500?10:10,100)).enter().append('rect').attr('class','tickC').attr('x', d=>scaleDD(d/10)+barX).attr('y', firstBarY+30).attr('height', d=>d%2===0?8:5) svg.selectAll('.tickB1').data(d3.range(width >500?10:10,100)).enter().append('rect').attr('class','tickB1').attr('x', d=>scaleDD(d/10)+barX).attr('y', d=>d%2===0? firstBarY+77:firstBarY+80).attr('height', d=>d%2===0?8:5) svg.selectAll('.tickC1').data(d3.range(width >500?10:10,100)).enter().append('rect').attr('class','tickC1').attr('x', d=>scaleDD(d/10)+barX).attr('y', d=>d%2===0? firstBarY+47:firstBarY+50).attr('height', d=>d%2===0?8:5)// Mil ticks svg.selectAll('.tickM').data(d3.range(width >500?1:1,10)).enter().append('rect').attr('class','tickM').attr('x', d=>scaleDD(d)+barX).attr('y', firstBarY+20).attr('height',6) svg.selectAll('.tickLabel1').data(d3.range(width >500?1:1,10)).enter().append('text').attr('class','tickLabel1').attr('x', d=>scaleDD(d)+barX+.5).attr('y', firstBarY+18)//.style("font-size", `{W < 550 ? 12 : W < 650 ? 14 : W < 750 ? 16 : W < 850 ? 18 : 20}px`).text(d=>d)// Cent ticks svg.selectAll('.tickC2').data(d3.range(width >500?10:10,100)).enter().append('rect').attr('class','tickC2').attr('x', d=>scaleDD(d/10)+barX).attr('y', firstBarY+10).attr('height', d=>d%2===0?9:6)// Beat ticks svg.selectAll('.tickB').data(d3.range(width >500?10:10,100)).enter().append('rect').attr('class','tickB').attr('x', d=>scaleDD(d/10)+barX).attr('y', firstBarY+60).attr('height', d=>d%2===0?9:6)// Labels svg.selectAll('.timeLabel').data([`+${barDD}`,`${barMils}`,`${barBeats}`]).enter().append('text').attr('class','timeLabel').attr('x', barX+2).attr('y', (d,i)=>i*30+firstBarY+20).style("font-size",`${W <300?14: W <400?16: W <500?18: W <600?20:22}px`).text(d=>d); svg.attr("id","barClock");return svg.node();}barChart1 = {const W = width;const H =88;const barX =1;const firstBarY =1;const svg = d3.create("svg").attr("width", W).attr("viewBox", [0,0, W * (W <370?.73: W <420?.76: W <470?.79: W <520?.81: W <570?.83: W <620?.84: W <670?.85: W <720?.86:.87), H]);const xRange = [0, W -100];const scaleDD = d3.scaleLinear().domain([0,10]).range(xRange);const scaleMandB = d3.scaleLinear().domain([0,100]).range(xRange);// const scaleDek = d3.scaleLinear()// .domain([0, 37])// .range(xRange);// Background bars to show where 100% lies svg.selectAll('.background').data([// 'dek', 'dotd','dd',"mils",'beats']).enter().append('rect').attr('class','background timeBar').attr('width', W-100).attr('y', (d,i)=>i*30+firstBarY)// Beats svg.append('rect').attr('class','timeBar').attr('y', firstBarY+60).attr('width', d =>scaleMandB(Number(barBeatsN))) svg.append('rect').attr('class','timeBarFullN').attr('y', firstBarY+60).attr('width', d =>scaleMandB(barBeatsN))// Cents/Mils svg.append('rect').attr('class','timeBar').attr('y', firstBarY).attr('width', d =>scaleDD(Number(barDDN)+Number(barMilsN)/100+Number(barBeatsN)/10000)) svg.append('rect').attr('class','timeBarFullN').attr('y', firstBarY).attr('width', d =>scaleDD(barDDN)) svg.append('rect').attr('class','timeBar').attr('y', firstBarY+30).attr('width', d =>scaleMandB(Number(barMilsN)+Number(barBeatsN)/100)) svg.append('rect').attr('class','timeBarFullN').attr('y', firstBarY+30).attr('width', d =>scaleMandB(barMilsN))// Cent ticks svg.selectAll('.tickC').data(d3.range(width >500?10:10,100)).enter().append('rect').attr('class','tickC').attr('x', d=>scaleDD(d/10)+barX).attr('y', firstBarY+30).attr('height', d=>d%2===0?8:5) svg.selectAll('.tickB1').data(d3.range(width >500?10:10,100)).enter().append('rect').attr('class','tickB1').attr('x', d=>scaleDD(d/10)+barX).attr('y', d=>d%2===0? firstBarY+77:firstBarY+80).attr('height', d=>d%2===0?8:5) svg.selectAll('.tickC1').data(d3.range(width >500?10:10,100)).enter().append('rect').attr('class','tickC1').attr('x', d=>scaleDD(d/10)+barX).attr('y', d=>d%2===0? firstBarY+47:firstBarY+50).attr('height', d=>d%2===0?8:5)// Mil ticks svg.selectAll('.tickM').data(d3.range(width >500?1:1,10)).enter().append('rect').attr('class','tickM').attr('x', d=>scaleDD(d)+barX).attr('y', firstBarY+20).attr('height',6) svg.selectAll('.tickLabel1').data(d3.range(width >500?1:1,10)).enter().append('text').attr('class','tickLabel1').attr('x', d=>scaleDD(d)+barX+.5).attr('y', firstBarY+18)//.style("font-size", `{W < 350 ? 12 : W < 450 ? 14 : W < 550 ? 16 : W < 650 ? 18 : 20}px`).text(d=>d)// Cent ticks svg.selectAll('.tickC2').data(d3.range(width >500?10:10,100)).enter().append('rect').attr('class','tickC2').attr('x', d=>scaleDD(d/10)+barX).attr('y', firstBarY+10).attr('height', d=>d%2===0?9:6)// Beat ticks svg.selectAll('.tickB').data(d3.range(width >500?10:10,100)).enter().append('rect').attr('class','tickB').attr('x', d=>scaleDD(d/10)+barX).attr('y', firstBarY+60).attr('height', d=>d%2===0?9:6)// Labels svg.selectAll('.timeLabel').data([`-${barDDN}`,`${barMilsN}`,`${barBeatsN}`]).enter().append('text').attr('class','timeLabel').attr('x', barX+2).attr('y', (d,i)=>i*30+firstBarY+20).style("font-size",`${W <300?14: W <400?16: W <500?18: W <600?20:22}px`).text(d=>d); svg.attr("id","barClock");return svg.node();}viewof location =worldMapCoordinates([162,0], [width *.998,Math.round((21/40) * width)])// https://observablehq.com/@dbridges/visualizing-seasonal-daylightapp = {const svg = d3.select(DOM.svg(width, height - (width <400?10: width <600?15: width <700?30:40))); svg.style("user-select","none").style("-webkit-user-select","none"); svg.attr("id","daylightapp")const margin = {top:0,left:10,right:10,bottom:0,inner:3};const contentWidth = width - margin.left- margin.right- margin.inner;const columnWidth = contentWidth /2;const date2022 =newDate(2022,newDate().getMonth(),newDate().getDate(),newDate().getHours())let selection = {date: date2022,hour: date2022.getHours() }const renderPlot = () => { svg.selectAll("#daylightplot *").remove(); svg.select("#daylightplot").call(daylightPlot, {width: columnWidth /1.26,height: height /1.51- margin.top- margin.bottom,year: date2022.getFullYear(),latitude: location[1],defaultDate: selection.date,defaultHour: selection.hour }) }const renderGlobe = () => { svg.selectAll("#globe *").remove(); svg.selectAll("#globe").call(globe, { width: columnWidth *1.2, location,...selection }); }const renderSolarSystem = () => { svg.selectAll("#solar-system *").remove(); svg.selectAll("#solar-system").call(solarSystem, columnWidth /1.1, location, selection.date, selection.hour); }const setSelection = (newSelection, forceRender =false) => {const prev = {...selection}; selection = newSelection; svg.node().value= selection;if (forceRender) {renderPlot();renderSolarSystem();renderGlobe(); } elseif (prev.hour!== selection.hour|| prev.date!== selection.date) {renderSolarSystem();renderGlobe(); } } svg.append("g").attr("id","solar-system").attr("transform",`translate(${margin.left+2}, ${margin.top+ height /7.5})`);const plot = svg.append("g").attr("id","daylightplot").attr("transform",`translate(${margin.left}, ${margin.top+ height /4})`); svg.append("g").attr("id","globe").attr("transform",`translate(${margin.left+ margin.inner+ columnWidth /1.25}, ${margin.top+Number(columnWidth <300) *12})`);setSelection(selection,true);const handleDateHourChange = ({ target,detail: { date, hour }}) => {if (date !=null&& hour !=null) setSelection({...selection, date, hour}); } svg.node().addEventListener(EventType.DateHourChange, handleDateHourChange,false);return svg.node();}// https://observablehq.com/@d3/simple-clock// https://observablehq.com/@drio/lets-build-an-analog-clockclock = {const clockRadius =200, margin =50, w = (clockRadius + margin) *2, h = (clockRadius + margin) *2, hourHandLength = (2* clockRadius) /3, minuteHandLength = clockRadius, secondHandLength = clockRadius -12, secondHandBalance =30, secondTickStart = clockRadius, secondTickLength =-10, hourTickStart = clockRadius, hourTickLength =-18, secondLabelRadius = clockRadius +16, secondLabelYOffset =5, hourLabelRadius = clockRadius -40, hourLabelYOffset =7, radians =Math.PI/180;const ten = d3.scaleLinear().range([0,360]).domain([0,10]);const sto = d3.scaleLinear().range([0,360]).domain([0,100]);const handData = [ {type:"hour",value:0,length:-hourHandLength,scale: ten }, {type:"minute",value:0,length:-minuteHandLength,scale: sto }, {type:"second",value:0,length:-secondHandLength,scale: sto,balance: secondHandBalance } ];functiondrawClock() {// create all the clock elementsupdateData();//draw them in the correct starting positionconst face = svg.append("g").attr("id","clock-face").attr("transform",`translate(${[w /2, h /2]})`);// add marks for seconds face.selectAll(".second-tick").data(d3.range(0,100)).enter().append("line").attr("class","second-tick").attr("x1",0).attr("x2",0).attr("y1", secondTickStart).attr("y2", secondTickStart + secondTickLength).attr("transform", d =>`rotate(${sto(d)})`);// and labels... face.selectAll(".second-label").data(d3.range(0,100,5)).enter().append("text").attr("class","second-label").attr("text-anchor","middle").attr("x", d => secondLabelRadius *Math.sin(sto(d) * radians)).attr("y", d =>-secondLabelRadius *Math.cos(sto(d) * radians) + secondLabelYOffset ).text(d => d);// ... and hours face.selectAll(".hour-tick").data(d3.range(0,10,1)).enter().append("line").attr("class","hour-tick").attr("x1",0).attr("x2",0).attr("y1", hourTickStart).attr("y2", hourTickStart + hourTickLength).attr("transform", d =>`rotate(${ten(d)})`); face.selectAll(".hour-label").data(d3.range(0,10,1)).enter().append("text").attr("class","hour-label").attr("text-anchor","middle").attr("x", d => hourLabelRadius *Math.sin(ten(d) * radians)).attr("y", d =>-hourLabelRadius *Math.cos(ten(d) * radians) + hourLabelYOffset ).text(d => d);const hands = face.append("g").attr("id","clock-hands"); hands.selectAll("line").data(handData).enter().append("line").attr("class", d => d.type+"-hand").attr("x1",0).attr("y1", d => d.balance||0).attr("x2",0).attr("y2", d => d.length).attr("transform", d =>`rotate(${d.scale(d.value)})`); face.append("g").attr("id","face-overlay").append("circle").attr("class","hands-cover").attr("x",0).attr("y",0).attr("r", clockRadius /20); }functionmoveHands() {const sel = d3.select("#clock-hands-final").selectAll("line").data(handData).transition();if (fancySecondsOFF) sel.ease(d3.easeElastic.period(0.5)); sel.attr("transform", d =>`rotate(${d.scale(d.value)})`); }functionupdateData() { handData[0].value=!fancySecondsOFF ?Math.floor(selectedExact *10) : decTime[0]; handData[1].value=!fancySecondsOFF ?Math.floor(selectedExact *10%1*100) : decTime.slice(2,4); handData[2].value=!fancySecondsOFF ? selectedExact *10%1*100%1*100: decTime.slice(4,6); }const svg = d3.create("svg").attr("viewBox", [0,0, w, h]).style("max-width",`${width /2.1}px`).attr("class","clock-top").attr("id","clock"); svg.append("text").text(`+${decTime}-${selectedZone}`).attr("x", clockRadius + margin).attr("y", clockRadius *2+ margin *1.975).attr("text-anchor","middle").attr("font-size",32).attr("font-family","monospace");drawClock();// Animationconst interval =setInterval( () => {updateData();moveHands(); },!fancySecondsOFF ?10:864 ); invalidation.then(() =>clearInterval(interval));return svg.node();}clock1 = {const clockRadius =200, margin =50, w = (clockRadius + margin) *2, h = (clockRadius + margin) *2, hourHandLength = (2* clockRadius) /3, minuteHandLength = clockRadius, secondHandLength = clockRadius -12, secondHandBalance =30, secondTickStart = clockRadius, secondTickLength =-10, hourTickStart = clockRadius, hourTickLength =-18, secondLabelRadius = clockRadius +16, secondLabelYOffset =5, hourLabelRadius = clockRadius -40, hourLabelYOffset =7, radians =Math.PI/180;const ten = d3.scaleLinear().range([0,360]).domain([0,10]);const sto = d3.scaleLinear().range([0,360]).domain([0,100]);const handData = [ {type:"hour",value:0,length:-hourHandLength,scale: ten }, {type:"minute",value:0,length:-minuteHandLength,scale: sto }, {type:"second",value:0,length:-secondHandLength,scale: sto,balance: secondHandBalance } ];functiondrawClock() {// create all the clock elementsupdateData();//draw them in the correct starting positionconst face = svg.append("g").attr("id","clock-face").attr("transform",`translate(${[w /2, h /2]})`);// add marks for seconds face.selectAll(".second-tick").data(d3.range(0,100)).enter().append("line").attr("class","second-tick").attr("x1",0).attr("x2",0).attr("y1", secondTickStart).attr("y2", secondTickStart + secondTickLength).attr("transform", d =>`rotate(${sto(d)})`);// and labels... face.selectAll(".second-label").data(d3.range(0,100,5)).enter().append("text").attr("class","second-label").attr("text-anchor","middle").attr("x", d => secondLabelRadius *Math.sin(sto(d) * radians)).attr("y", d =>-secondLabelRadius *Math.cos(sto(d) * radians) + secondLabelYOffset ).text(d => d);// ... and hours face.selectAll(".hour-tick").data(d3.range(0,10,1)).enter().append("line").attr("class","hour-tick").attr("x1",0).attr("x2",0).attr("y1", hourTickStart).attr("y2", hourTickStart + hourTickLength).attr("transform", d =>`rotate(${ten(d)})`); face.selectAll(".hour-label").data(d3.range(0,10,1)).enter().append("text").attr("class","hour-label").attr("text-anchor","middle").attr("x", d => hourLabelRadius *Math.sin(ten(d) * radians)).attr("y", d =>-hourLabelRadius *Math.cos(ten(d) * radians) + hourLabelYOffset ).text(d => d);const hands = face.append("g").attr("id","clock-hands"); hands.selectAll("line").data(handData).enter().append("line").attr("class", d => d.type+"-hand").attr("x1",0).attr("y1", d => d.balance||0).attr("x2",0).attr("y2", d => d.length).attr("transform", d =>`rotate(${d.scale(d.value)})`); face.append("g").attr("id","face-overlay").append("circle").attr("class","hands-cover").attr("x",0).attr("y",0).attr("r", clockRadius /20); }functionmoveHands() {const sel = d3.select("#clock-hands-final").selectAll("line").data(handData).transition();if (fancySecondsOFF) sel.ease(d3.easeElastic.period(0.5)); sel.attr("transform", d =>`rotate(${d.scale(d.value)})`); }functionupdateData() { handData[0].value=!fancySecondsOFF ?Math.floor(selectedExactN *10) : decTimeN[0]; handData[1].value=!fancySecondsOFF ?Math.floor(selectedExactN *10%1*100) : decTimeN.slice(2,4); handData[2].value=!fancySecondsOFF ? selectedExactN *10%1*100%1*100: decTimeN.slice(4,6); }const svg = d3.create("svg").attr("viewBox", [0,0, w, h]).style("max-width",`${width /2.1}px`).attr("class","clock-btm").attr("id","clock"); svg.append("text").text(`-${decTimeN}-${selectedZone}`).attr("x", clockRadius + margin).attr("y", clockRadius *2+ margin *1.975).attr("text-anchor","middle").attr("font-size",32).attr("font-family","monospace");drawClock();// Animationconst interval =setInterval( () => {updateData();moveHands(); },!fancySecondsOFF ?10:864 ); invalidation.then(() =>clearInterval(interval));return svg.node();}
The plot to the lower left of the map🗺️visualizes the night (blue) and day (yellow) time of day (x-axis) throughout every day of the year (y-axis) at the latitude of the red⭕️circle on the map🗺️. The vertical↕position of the red—line (time of day) and the↔︎️horizontal position of the red🔴dot (day of the year) on the plot control the🌐globes above and to the right of the plot.
Zone
Drag the red⭕️circle across the meridians (vertical↕gray lines) on the map🗺️to see how changing time zones affects the time. Only the first digit of the Dec times shown above, the deciday, varies across time zones, because the 10 Dec time zones, numbered 0 through 9 on the map🗺️, are each 1 deciturn (d\(\tau\)) wide. Simply put, a deciturn of longitude translates into a deciday of time.
The leftmost vertical↕line on the map🗺️is Meridian 0, the Dec International Date Line and prime meridian, which cuts across the Atlantic Ocean through Iceland🇮🇸just West of Africa🌍and is the boundary between Zone 9 and Zone 0, the rightmost and leftmost Dec time zones on the map🗺️, respectively. Arranging Dec time zones from 0 to 9 yields a Pacific-centric map🗺️.
While only positive Dec time zones are shown on the map🗺️, every Dec time zone can also be expressed as a negative number. Each pair of time zone numbers produces the same Dec time, but result in Dec dates🗓️that are 1 day apart. Negative time zone numbers can be useful for getting Dec dates🗓️to match Gregorian calendar dates🗓️with negative UTC offsets.
There are 37 UTC offsets, but only 10 Dec time zones. Conversion between Dec time zones and UTC offsets is inexact, because UTC offsets depend on geographic and political boundaries, whereas Dec time zones are determined solely by longitude. If you know your longitude in degrees (°) or centiturns (\(c\tau\)), you can look up your Dec time zone (TZ) in the table below.
TZ +
TZ -
Start °
Mid °
End °
Start \(c\tau\)
Mid \(c\tau\)
End \(c\tau\)
9
-1
-54
-36
-18
90
95
100
8
-2
-90
-72
-54
80
85
90
7
-3
-126
-108
-90
70
75
80
6
-4
-162
-144
-126
60
65
70
5
-5
162
180
-162
50
55
60
4
-6
126
144
162
40
45
50
3
-7
90
108
126
30
35
40
2
-8
54
72
90
20
25
30
1
-9
18
36
54
10
15
20
0
-10
-18
0
18
0
5
10
Dec times in Zone 0 and 5 can be directly converted to and from UTC times with an offset of 0 and 12 hours, respectively. The other Dec time zones will differ from the closest UTC time by 8.3, 16.6, 25, 33.3, 41.6 or 50 millidays. To find the difference δ, convert the UTC offset hours oH and minutes oM into deciturns of the longitude λ and then subtract the Dec zone number z:
\[\lambda = o_H \div 2.4 + o_M \div 144\]
\[z = \lfloor\lambda + 1 \div 2\rfloor\]
\[\delta = \lambda - z\]
We can avoid dealing with the time zone difference by shifting the time zone so that we always convert between Zone 0 and UTC+00:00 or Zone 5 and UTC+12:00. To obtain the Zone 0 time, we evaluate a Dec time as a math expression, add 10, and get the remainder after dividing by 10 to make sure the result is less than 10 decidays: ( + 10) mod 10 = .
Bad Pun Alert
Sorry if reading this takes a long time; I hope you don’t zone out!
Unit
Dec uses metric prefixes to create submultiples of a day that can naturally be combined together into a single decimal number. Conversion between decimal units is as simple as moving↔︎️or removing❌the decimal separator. In contrast, an hh:mm:ss time is a mixed-radix number, where hh is the base-12 or base-24 hour, mm is the base-60 minute, and ss is the base-60 second.
Prefix
Power
Day
hh:mm:ss.sss
0
1
24:00:00.000
deci
-1
.1
02:24:00.000
centi
-2
.01
00:14:24.000
milli
-3
.001
00:01:26.400
decimilli
-4
.0001
00:00:08.640
centimilli
-5
.00001
00:00:00.864
To convert the hour h, minute m, and second s into the decidayd, Dec uses the following equation: d = h ÷ 2.4 + m ÷ 144 + s ÷ 8640. The current equation values in Zone are: = ÷ 2.4 + ÷ 144 + ÷ 8640. Inversely, we can convert decidays into hours: h = d × 2.4, minutes: m = hmod 1 × 60, and seconds: s = mmod 1 × 60.
Instead of dealing with hours, minutes, and seconds, we can convert the UNIX timestampu into the Dec time d+0. First, we divide u by 86400 to convert seconds to days, then isolate the decimal part of the quotient, and finally multiply by 10: d + 0 = u ÷ 86400 mod 1 × 10. The current values in this equation are + 0 = ÷ 86400 mod 1 × 10.
The concept of measuring time in decimal days is not novel. In the late 1700s, the French Republican calendar time system referred to decidays as decimal hours, centidays as décimes, millidays as decimal minutes, and centimillidays as decimal seconds. Similarly, Swatch Internet Time, a decimal time system introduced in 1998, uses the term “.beats” for millidays.
Swatch Internet Time differs from Dec in that it has no time zones and is obtained from the hours, minutes, and seconds of UTC+01:00. In contrast, the major innovations described in this article are the Dec time zone system and the simple equation for obtaining the Dec time in Zone 0 from a UNIX timestamp, but Dec has much more to offer than deciday times and zones.
Next
The next article in the Dec section of my site compares Dec to the ISO 8601 international standard for dates and times. Like ISO 8601, Dec allows for combined date and time representations that can be paired up to express time intervals. In Dec, the combination of a date and time is called a snap🫰and a time interval expressed as a pair of snaps is called a span🌈.
My ISO 8601 article is unique because it avoids the use of Observable in favor of leveraging Jupyter support in Quarto to make the code underlying Dec available in multiple programming languages. Observable is a great visualization tool but does not translate well into Jupyter notebooks. After the next article, I return to the use of Observable in my Dec snap🫰and span🌈articles.
flowchart LR
B[date]-->C[time]-->D[iso]-->E[snap]-->F[span]
click B "/dec/date"
click C "/dec/time"
click D "/dec/iso"
click E "/dec/snap"
click F "/dec/span"
style C stroke:#99f,stroke-width:5px
Cite
Please spread the good word about Dec using the citation information at the bottom of this article. You may also want to cite the Observable notebooks that I adapted into the clock🕓, bar📊chart, map🗺️, and daylight☀️plot visualizations in this article or the 2014 blog post which proposed a system of 20 decimal time zones, each 5 centidays wide, based on the Greenwich Meridian: