All eazyBI for Jira eazyBI for Confluence Private eazyBI

Custom Time hierarchies

eazyBI automatically creates two hierarchies in the Time dimension - default and weekly.

For week numbering in the Weekly hierarchy, eazyBI uses ISO standard, which means the first week of the year always has January 4.

You can add additional custom hierarchies to the Time dimension with different rules using the Add custom hierarchy option.

There are three types of custom hierarchies available:

This option is available for users with account OwnerUser Admin, or Data Admin user roles.

We recommend using the Custom Time hierarchy Assistant to add new custom Time hierarchies.

On this page:

Fiscal monthly hierarchy

You can create your custom Fiscal hierarchy by selecting the month your fiscal year starts and the Year's name.

Multiple weeks hierarchy

The Multiple weeks hierarchy groups days into repeating periods of one to four weeks.

Calculated time hierarchy

You can add calculated hierarchies using JavaScript code that describes how the hierarchy should work.

  1. Set a unique name for the hierarchy [1].
  2. Specify the hierarchy's levels- Year, Quarter, Month, Week, and Day [2].
  3. The custom JavaScript code should calculate a numeric value and a display name value for each level of the date [3].
  4. Test the custom hierarchy on specific dates [4].

The JavaScript code should include the return statement containing a numeric and a name value for each of the selected levels, and must follow these rules:

The example creates a custom hierarchy using the calendar year, month, and day values. The hierarchy excludes the quarter level and will have only the following levels: Year, Month, and Day.

return {
  year: timeValue.getFullYear(),
  year_name: "" + timeValue.getFullYear(),
  month: timeValue.getMonth() + 1,
  month_name: strftime("%B %Y", timeValue),
  day: timeValue.getDate(),
  day_name: strftime("%B %d %Y", timeValue)
}

The strftime function formats dates as strings; in addition to the standard format specifiers, %V (ISO week number) and %G (ISO week year) are supported.

If you would like to apply different rules when calculating the hierarchy, you can override default calendar values by calculating each level's value. Use examples below as guides.

Custom Time hierarchy Assistant

We recommend using the Custom Time hierarchy Assistant to add new custom time hierarchies. The assistant generates a configuration including JavaScript code (*if needed) for a custom hierarchy based on your prompts. 

But you are welcome to explore our JavaScript code examples below.

Monthly, with a different start date

The code below creates a hierarchy in which each month starts on a specific day of the previous calendar month - with monthStartDay = 25, the month Jul 2026 covers June 25 – July 24, 2026.

Levels used: Year, Quarter, Month, Day

// Each month starts on the 25th of the previous calendar month:
// for example, "Jul 2026" covers June 25 - July 24, 2026.
const monthStartDay = 25;
const monthName = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

let year = timeValue.getFullYear();
let month = timeValue.getMonth() + 1; // 1-12 calendar month

// From the start day on, the date belongs to the next month's period.
if (timeValue.getDate() >= monthStartDay) {
  month++;
  if (month > 12) {
    month = 1;
    year++;
  }
}

// The period starts on the monthStartDay of the previous calendar month.
const monthStartDate = new Date(year, month - 2, monthStartDay);
const quarter = Math.ceil(month / 3);
const dayInMonth =
  Math.ceil((timeValue.getTime() - monthStartDate.getTime()) / 86400000) + 1;

return {
  year: year,
  year_name: "" + year,
  quarter: quarter,
  quarter_name: "Q" + quarter + " " + year,
  month: month,
  month_name: monthName[month - 1] + " " + year,
  day: dayInMonth,
  day_name: strftime("%b %d %Y", timeValue)
}

Months can also start on a day in the same calendar month (for example, Jul 2026 covering July 5 – August 4) — the only difference is naming each period after the calendar month it starts in rather than ends in. Ask the Custom Time hierarchy Assistant to adjust the code for that variant.

Fiscal or academic year with a custom start month

For a standard fiscal year with Year, Quarter, Month, and Day levels, use the built-in Fiscal monthly hierarchy type.

Use this calculated version when you need a second fiscal calendar in the account, a hierarchy without the Quarter level, or custom year names such as 2026/27.

The example starts the year in September: 2026/27 covers September 2026 – August 2027.

// Academic/fiscal year starting in September:
// "2026/27" covers September 2026 - August 2027.
const firstMonth = 9;

const year = timeValue.getFullYear();
const month = timeValue.getMonth() + 1; // 1-12 calendar month

// Months before firstMonth belong to the year started the previous year.
const fiscalYear = month >= firstMonth ? year : year - 1;
// Renumber months so the first fiscal month gets number 1.
const fiscalMonth = month >= firstMonth
  ? month - firstMonth + 1
  : month + 12 - firstMonth + 1;

return {
  year: fiscalYear,
  year_name: fiscalYear + "/" + ("" + (fiscalYear + 1)).slice(2),
  month: fiscalMonth,
  month_name: strftime("%b %Y", timeValue),
  day: timeValue.getDate(),
  day_name: strftime("%b %d %Y", timeValue)
}

The year name can follow any convention - for example, name the year by its ending calendar year with year_name: "FY" + (fiscalYear + 1).

Semesters, quadrimesters, and other multi-month periods

The code below splits each year into equal multi-month periods, using the Quarter level for the periods. With monthsPerPeriod = 6, the year has two semesters; use monthsPerPeriod = 4 for three four-month periods (quadrimesters).

Levels used: Year, Quarter, Month, Day

// Split each year into equal multi-month periods: 6 months per period
// gives two semesters, 4 gives three quadrimesters.
const monthsPerPeriod = 6;

const year = timeValue.getFullYear();
const month = timeValue.getMonth() + 1; // 1-12 calendar month
const period = Math.ceil(month / monthsPerPeriod);

return {
  year: year,
  year_name: "" + year,
  quarter: period,
  quarter_name: "S" + period + " " + year,
  month: month,
  month_name: strftime("%b %Y", timeValue),
  day: timeValue.getDate(),
  day_name: strftime("%b %d %Y", timeValue)
}

Adjust the period name prefix to the period type - for example, S1 2026 for semesters.

A 3-month split matches the default hierarchy's Quarter level - use a calculated hierarchy for that only if you need different quarter names.

ISO weeks with custom names

The built-in Weekly hierarchy already provides Year, Week, and Day levels with ISO week numbering and members like W35, Aug 24 2026.

Use a calculated hierarchy only when you want different week names - the example below produces the shorter W35 2026 - or as a starting point for custom week logic.

Levels used: Year, Week, Day

// ISO week numbering: weeks start on Monday, and week 1 is the week
// containing January 4 - so "W01 2026" covers Dec 29 2025 - Jan 4 2026.
const isoYear = strftime("%G", timeValue);
const isoWeek = strftime("%V", timeValue);

return {
  year: +isoYear,
  year_name: isoYear,
  week: +isoWeek,
  week_name: "W" + isoWeek + " " + isoYear,
  day: (timeValue.getDay() + 6) % 7 + 1, // Monday = 1 ... Sunday = 7
  day_name: strftime("%b %d %Y", timeValue)
}

Note that the year value uses the ISO week-based year (%G), not the calendar year - Dec 29–31, 2025 belong to 2026 in this hierarchy, keeping every week entirely within one year member.

5-4-4 weekly

The hierarchy includes all levels and will be based on a week split by months and quarters using pattern 5-4-4. It will use Sunday as the starting day, and 1st January is always in the first week of the year.

Levels used: Year, Quarter, Month, Week, Day

// 5-4-4 week-based calendar: each quarter has 13 weeks split into
// months of 5, 4, and 4 weeks. Weeks start on Sunday, and January 1
// is always in the first week of the year.
const monthName = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
  "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];

// Build constructed dates at midnight UTC to match timeValue, so day
// differences are exact whole days in any server timezone.
const tzOffset = timeValue.getTimezoneOffset();

// The Sunday on or before the date - the start of its week.
const weekStartDate = new Date(timeValue);
weekStartDate.setDate(weekStartDate.getDate() - weekStartDate.getDay());

// A week containing January 1 belongs entirely to the new year.
let year = timeValue.getFullYear();
const nextYearStart = new Date(year + 1, 0, 1, 0, -tzOffset);
if ((nextYearStart.getTime() - weekStartDate.getTime()) / 86400000 < 7) {
  year++;
}

// The year's first week starts on the Sunday on or before January 1.
const yearStart = new Date(year, 0, 1, 0, -tzOffset);
yearStart.setDate(yearStart.getDate() - yearStart.getDay());

const week = Math.floor(
  (weekStartDate.getTime() - yearStart.getTime()) / 86400000 / 7) + 1;
const dayInWeek = Math.floor(
  (timeValue.getTime() - weekStartDate.getTime()) / 86400000) + 1;

let quarter, month;
if (week > 52) {
  // Week 53 stays in the last month of Q4.
  quarter = 4;
  month = 12;
} else {
  quarter = Math.ceil(week / 13);
  const weekInQuarter = week - (quarter - 1) * 13; // 1-13
  // Months within a quarter contain 5, 4, and 4 weeks.
  const monthInQuarter = weekInQuarter <= 5 ? 1 : (weekInQuarter <= 9 ? 2 : 3);
  month = (quarter - 1) * 3 + monthInQuarter;
}

return {
  year: year,
  year_name: "Y " + year,
  quarter: quarter,
  quarter_name: "Q" + quarter + " " + year,
  month: month,
  month_name: monthName[month - 1] + " " + year,
  week: week,
  week_name: "W" + week,
  day: dayInWeek,
  day_name: strftime("%b %d %Y", timeValue)
}

Monthly, using a specific start date in each month

The code below uses the zodiac calendar as an example of periods that start on a specific day of each calendar month, following the same pattern every year.

Each "month" is a zodiac sign named after the sign; the sign that crosses the year boundary (Capricorn) stays under the year it started in.

Levels used: Year, Month, Day

// Each "month" is a zodiac sign period starting on a fixed day of its
// calendar month: for example, Aquarius starts on January 20. The sign
// crossing the year boundary (Capricorn) stays under the year it
// started in.
const zodiac = [
  { startDate: 20, name: "♒ Aquarius" },    // starts Jan 20
  { startDate: 19, name: "♓ Pisces" },
  { startDate: 21, name: "♈ Aries" },
  { startDate: 20, name: "♉ Taurus" },
  { startDate: 21, name: "♊ Gemini" },
  { startDate: 22, name: "♋ Cancer" },
  { startDate: 23, name: "♌ Leo" },
  { startDate: 23, name: "♍ Virgo" },
  { startDate: 23, name: "♎ Libra" },
  { startDate: 24, name: "♏ Scorpio" },
  { startDate: 22, name: "♐ Sagittarius" },
  { startDate: 22, name: "♑ Capricorn" }    // starts Dec 22
];

// Build constructed dates at midnight UTC to match timeValue, so day
// differences are exact whole days in any server timezone.
const tzOffset = timeValue.getTimezoneOffset();

let year = timeValue.getFullYear();
let month = timeValue.getMonth(); // 0-11 calendar month

// Before this month's sign start day, the date still belongs to the
// sign that started in the previous month.
if (timeValue.getDate() < zodiac[month].startDate) {
  month--;
  if (month < 0) {
    month = 11;
    year--;
  }
}

const signStart =
  new Date(year, month, zodiac[month].startDate, 0, -tzOffset);
const dayInSign = Math.floor(
  (timeValue.getTime() - signStart.getTime()) / 86400000) + 1;

return {
  year: year,
  year_name: "Y " + year,
  month: month + 1,
  month_name: zodiac[month].name + " " + year,
  day: dayInSign,
  day_name: strftime("%b %d %Y", timeValue)
}