getMonth()

ES1+

Returns the month of the date as a zero-based index according to local time: 0 for January through 11 for December. The zero-based convention exists so the value can index directly into arrays of month names, but it is the single most common source of off-by-one date bugs in JavaScript.

Syntax

date.getMonth()

Return Value

number

A number (0-11) representing the month

Examples

JavaScript
const date = new Date('2024-03-15');
console.log(date.getMonth()); // 3월은 2
console.log(date.getMonth() + 1); // 실제 월
Output:
// 2 3

📌 When to Use

Use getMonth() when you need the month component of a date for logic rather than display: grouping transactions into monthly buckets, checking whether two dates fall in the same month, driving a calendar grid, computing quarters (Math.floor(getMonth() / 3) + 1), building month navigation in a date picker, or feeding a month-names array. Remember its two defining characteristics every time: it is zero-based, and it is local-time-based. The zero-based value is genuinely convenient as an array index and as the month argument to the Date constructor, which expects the same convention, so new Date(d.getFullYear(), d.getMonth() + 1, 1) cleanly means "first day of next month" and the constructor normalizes December overflow into January of the next year for you. For anything a human reads, do not hand-build month names; pass the Date to toLocaleString or Intl.DateTimeFormat with { month: "long" } and get correctly localized names for free. And for server or storage logic that must not depend on where the code runs, use getUTCMonth() instead, since a timestamp near midnight at a month boundary belongs to different months in different time zones.

⚠️ Common Mistakes

Displaying the raw value: a date in March prints as 2. Every value shown to users needs + 1 (or, better, locale formatting). The inverse mistake is passing a human 1-12 month straight into the Date constructor, which shifts every date one month forward, silently.

Comparing against human-entered months without normalizing: if (date.getMonth() === Number(userMonth)) is off by one for every month, and it fails only in ways integration tests with January (0 vs 1) catch by luck.

The UTC parsing trap at month boundaries: new Date("2024-03-01").getMonth() returns 1 (February!) for users west of Greenwich, because the date-only string parses as UTC midnight and local getters read the previous evening. Construct local dates from numeric parts to avoid it.

Doing month arithmetic by hand: month + 1 === 12 ? 0 : month + 1 with a separate year fix-up is error-prone. setMonth() and the Date constructor already normalize overflow, including multi-month and negative offsets.

The end-of-month rollover bug in month arithmetic: calling setMonth(getMonth() + 1) on January 31 lands on March 2 or 3, because February has no day 31 and the excess days spill over. Clamp the day-of-month first when you mean "same day next month".

Trusting a hardcoded English month array for display. months[d.getMonth()] breaks the moment the product localizes; Intl.DateTimeFormat produces the correct name in every locale, including grammatical forms standalone month names need in languages like Russian.

✅ Best Practices

Keep zero-based month values confined to Date APIs and array indexing; the instant a month value crosses into JSON, a URL, a database, or a log line, convert it to human 1-12 form and document which convention the field uses.

Use months[date.getMonth()] with a locale-appropriate array, or better, date.toLocaleString(locale, { month: "long" }) / Intl.DateTimeFormat so month names localize automatically and stay consistent with the rest of your date formatting.

Exploit constructor normalization for month math: new Date(y, m + 1, 0) is the last day of month m, new Date(y, m + delta, 1) safely jumps months across year boundaries. These idioms are both correct and self-explanatory once known.

Build month-bucket keys with explicit year and padded month, for example `${getFullYear()}-${String(getMonth() + 1).padStart(2, "0")}`, so keys sort lexicographically and never collide across years.

The Temporal API fixes this footgun outright: Temporal.PlainDate months are 1-12, and date.add({ months: 1 }) does clamped, calendar-aware month arithmetic. For new code doing heavy month math, it (or a library like date-fns addMonths) prevents the whole bug class.

⚡ Performance Notes

getMonth() reads a cached field after the engine converts the internal timestamp to local time once per Date instance, so repeated getter calls on the same object are near-free, on the order of nanoseconds. Performance issues in month-related code come from the surrounding patterns instead: formatting month names with toLocaleString() inside a loop constructs a fresh Intl.DateTimeFormat each call, which is hundreds of times more expensive than the getter, so hoist a single formatter out of the loop and reuse its format() method. Likewise, when grouping thousands of records by month, compute each record's bucket key once, and if your source data is ISO 8601 strings in a known zone, slicing characters 5-6 of the string avoids Date construction entirely, which can shrink a grouping pass from milliseconds to microseconds.

🌍 Real World Example

Monthly Activity Calendar

Grouping time-stamped records by month is a staple of dashboards, statements, and activity feeds. This example builds a stable year-month key from getFullYear() and getMonth() so buckets never collide across years, while simultaneously producing a localized, human-readable month label with toLocaleString. Note the division of labor, index math for grouping logic and Intl for display, which is exactly the discipline that keeps zero-based month bugs out of production. The same structure powers expense reports, commit-history graphs, and monthly usage summaries.

function groupActivitiesByMonth(activities, locale = 'ko-KR') {
  const grouped = {};

  activities.forEach(activity => {
    const date = new Date(activity.date);
    const monthKey = `${date.getFullYear()}-${date.getMonth()}`;
    const monthName = date.toLocaleString(locale, { month: 'long', year: 'numeric' });

    if (!grouped[monthKey]) {
      grouped[monthKey] = { monthName, activities: [] };
    }
    grouped[monthKey].activities.push(activity);
  });

  return Object.values(grouped);
}

const activities = [
  { name: 'Meeting', date: '2024-03-15' },
  { name: 'Workshop', date: '2024-03-20' },
  { name: 'Conference', date: '2024-04-10' }
];

console.log(groupActivitiesByMonth(activities));
// [{ monthName: '2024년 3월', activities: [...] }, { monthName: '2024년 4월', activities: [...] }]

Related Methods