Shankstee
Shankstee

Reputation: 31

Displaying time data on horizontal calendar

I have a horizontal calendar where each month has a width of 183px(pixels). I want to display a timespan bar on top of this horizontal calendar (See photo below) depending on a starting date and a ending date. So if I have a start date of:01-01-2020 and an end date of: 03-15-2020 that is about 2.5 months * 183px which is the closes I could get, but what could I do or use to determine that: 03-15-2020 - 01-01-2020 = ~2.5 months as accurate as possible? In addition I need this timespan bar to start on the current date of my horizontal calendar so if it starts on 02-01-2020 then the timespan div would start at left: 183px; I am currently exploring date-fns but have had no luck yet. Anything could help, thank you so much for your time. I am using React.js, Javascript, & Typescript.

Image of horizontal calendar and time span bar

Upvotes: 0

Views: 418

Answers (2)

RobG
RobG

Reputation: 147403

You can calculate the width of the bar for a particular date range by getting the number of days in the range, then calculate the width as a percentage of the full width based on the percentage of days in the range to a full year.

i.e. eventWidth = daysInRange ÷ daysInYear * grahpFullWidth

If you just want to position something from the start of the year, then always use 1 Jan of the current year as the start of the range.

E.g. the following puts today's date on a bar that is 183px wide. You can change the graph by changing the style properties, e.g. set the width to 300px.

function getDayOfYear(date) {
  // Use UTC to avoid any DST issues
  let d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
  let yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
  return Math.ceil((d - yearStart) / 8.64e7) + 1;
}

function getDaysInYear(date) {
  return getDayOfYear(new Date(date.getFullYear(), 11, 31));
}

function graphDate(date) {
  let dayNum = getDayOfYear(date);
  let daysInYear = getDaysInYear(date);
  let timeline = document.getElementById('timeline');
  let someEvent = document.getElementById('someEvent');
  let dateText = document.getElementById('dateText');
  let fullWidth = parseInt(window.getComputedStyle(timeline).getPropertyValue('width'));
  let partWidth = fullWidth * dayNum / daysInYear;
  someEvent.style.width = partWidth + 'px';
  dateText.textContent = date.toLocaleString('en-GB',{
    day: 'numeric',
    month: 'short'
  });
  dateText.style.marginLeft = partWidth - 1 + 'px';
  document.getElementById('yearLabel').textContent = date.getFullYear();
}

// Graph today
graphDate(new Date());
#timeline {
  border: 1px solid red;
  width: 183px;
  height: 10px;
}

#someEvent {
  border: 0;
  height: 10px;
  background-color: green;
  width: 0px;
}
#startLabel {
  width: 90px;
  display: inline-block;
}
#yearLabel {
  width: 90px;
  display: inline-block;
  font-size: 130%;
}
#dateText {
  padding: 15px 0 0 3px;
  border-left: 1px solid blue;
}
<div id="labels">
  <span id="startLabel">1 Jan</span>
  <span id="yearLabel"></span>
  <span id="endLabel">31 Dec</span>
</div>
<div id="timeline">
  <div id="someEvent"></div>
</div>
<div id="dateText"></div>

Upvotes: 1

russiandobby
russiandobby

Reputation: 79

The laziest way i can think of is to write the style for your bar component(which i assume is absolute and relative to calendat) with js, and based on between which months it is set the width calculated based on that. something like this

    var newStyles = document.createElement('style')
document.head.append(newStyles)
newStyles.innerHTML = ".my-element {" +
  "height: " + height + "px;" +
  "width: 40px;" +
  "color: #9b806b;" +
"}"

Upvotes: 0

Related Questions