Use JavaScript to Find the Week Day from a Date

Abstract image used to represent Use JavaScript to Find the Week Day from a Date
Image by Behnam Norouzi.

In Brief

For a valid dateonly string in YYYY-MM-DD form, use getUTCDay() on the parsed Date and map 0 to Sunday through 6 to Saturday. The examples below accept Gregorian dates from year 0001 to 9999 and reject invalid inputs. The arithmetic version avoids Date altogether.

Whilst recently sitting in on a technical interview, our candidate was asked to write a JavaScript/TypeScript function that accepts a date and, by return, determines which day of the week that date falls (or fell) on.

Something like this:

getDayOfWeek('2024-11-01');  // => "Friday"
getDayOfWeek('2022-01-01');  // => "Saturday"

In JavaScript, this is a fairly straightforward problem to solve as long as you are familiar with JavaScript's builtin Date object, but what if that wasn't allowed, or that was an area of JavaScript that the candidate wasn't familiar with or didn't know how to use?

Fortunately, we're not that mean during interviews, but it did raise a question that I wanted to explore further...


A simple solution using Date

For these examples, I'm accepting dateonly strings in YYYY-MM-DD form, for valid Gregorian dates from year 0001 to 9999. An ISO dateonly string is parsed at midnight UTC, so getUTCDay() gives us a consistent weekday: 0 for Sunday through 6 for Saturday. We can use that number as an array index.

Here is the TypeScript version, including a check for invalid dates:

const getDayOfWeek = (dateString: string): string => {
  const daysOfWeek = [
    'Sunday',
    'Monday',
    'Tuesday',
    'Wednesday',
    'Thursday',
    'Friday',
    'Saturday',
  ];

  if (!/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/.test(dateString) ||
      dateString.startsWith('0000-')) {
    throw new RangeError('Expected a date from 0001-01-01 to 9999-12-31');
  }

  const date = new Date(dateString);

  if (Number.isNaN(date.getTime()) ||
      date.toISOString().slice(0, 10) !== dateString) {
    throw new RangeError('Invalid date');
  }

  return daysOfWeek[date.getUTCDay()];
};

How It Works

  1. Check the input shape and reject invalid calendar dates, including dates which Date would otherwise normalise into the next month.
  2. getUTCDay() retrieves the UTC weekday as an integer from the parsed date.
  3. We then use that integer as an index into the daysOfWeek array, returning the name of the day.

The format and calendar checks are deliberate. A string that produces a Date is not necessarily a valid input for this function: some outofrange day values are normalised into the following month. Comparing the resulting ISO date with the input catches that too.

The UTC weekday is independent of the client's time zone. That is the behaviour we want for a date without a time, but it is easy to lose if we mix dateonly parsing with localtime methods.


Time Zones and the Date() Solution

The distinction is worth explaining in an interview, because the same instant can fall on different local dates.

Date Parsing

Date parses different string forms differently. An ISO dateonly string such as 2024-11-01 represents midnight UTC. A datetime string without an offset is interpreted in the local time zone; that is a different input from the dateonly strings accepted here.

Calling getDay() on new Date('2024-11-01') first uses the client's local time. West of UTC, that instant can still be Thursday 31 October. getUTCDay() reads Friday directly from the UTC date.

Daylight Saving Time (DST)

Daylight saving changes local offsets, so it matters when translating instants into local calendar dates. Our dateonly function does not make that translation: it parses and reads the weekday in UTC.

Using Universal Time (UTC) instead

That is why the example above already uses getUTCDay(). Switching just the getter would not make every possible input format safe; the explicit YYYY-MM-DD restriction matters as well.

If we use this approach, then we can ensure consistent results from our function, regardless of the user's local time zone. Being able to explain this to your interviewer should hopefully gain you a few extra points too!


Another Approach Using Arithmetic and Remainders

We can also count Gregorian calendar days from 1 January AD 1, a Monday when that calendar is extended backwards. The % remainder operator then gives a weekday index from the nonnegative day count. This is calendar arithmetic, without converting the date to a local time.

Here's what that might look like:

const getDayOfWeekManual = (dateString: string): string => {
  const daysOfWeek = [
    'Monday',
    'Tuesday',
    'Wednesday',
    'Thursday',
    'Friday',
    'Saturday',
    'Sunday',
  ];

  if (!/^[0-9]{4}-[0-9]{2}-[0-9]{2}$/.test(dateString)) {
    throw new RangeError('Expected YYYY-MM-DD');
  }

  const [year, month, day] = dateString.split('-').map(Number);

  const isLeapYear = (year: number): boolean =>
    (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;

  const daysInMonth = (month: number, year: number): number => {
    const days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
    return month === 2 && isLeapYear(year) ? 29 : days[month - 1];
  };

  if (year < 1 || month < 1 || month > 12 ||
      day < 1 || day > daysInMonth(month, year)) {
    throw new RangeError('Invalid date');
  }

  const totalDays =
    Array.from({ length: year - 1 }, (_, i) => i + 1).reduce(
      (sum, y) => sum + (isLeapYear(y) ? 366 : 365),
      0
    ) +
    Array.from({ length: month - 1 }, (_, i) => i + 1).reduce(
      (sum, m) => sum + daysInMonth(m, year),
      0
    ) +
    day;

  const dayIndex = (totalDays - 1) % 7;

  return daysOfWeek[dayIndex];
};

How It Works

As you might expect, this has a few more moving parts than the first example, but it's still a relatively straightforward process:

  1. This array starts with Monday, the reference weekday, whereas the Date example starts with Sunday.
  2. Check the YYYY-MM-DD shape, then split the input into year, month and day. Reject dates outside the stated range or calendar rules.
  3. Check for Leap Years

    via a helper function which determines whether a year is a leap year so that we can account for the extra day in February.
  4. Calculate Days in a Month

    : another helper function which returns the number of days in a given month using an array of days per month, and adjusting for February if the year is a leap year.
  5. Sum the days for all years before the input year (adding 366 for leap years, 365 otherwise).
  6. Add the days for all months before the input month in the current year, using daysInMonth.
  7. Add the days in the current month.
  8. The inclusive total counts the reference date as day 1. Subtract 1, then use (totalDays - 1) % 7 to obtain the index into the Mondayfirst array.

This uses Gregorian leapyear and monthlength rules throughout, including years before countries adopted that calendar. It counts calendar days, not elapsed 24hour periods, so local time zones and daylight saving do not enter the calculation.

However, as you can see, it's a much more complex implementation and requires careful handling of leap years and month lengths.


Wrapping Up

Both solutions are valid and have their own merits. In an interview scenario, using Date is exactly what I and my colleagues would expect, leveraging native JavaScript capabilities and doing 'enough' to show your understanding of the subject.

The modulobased approach showcases deeper algorithmic thinking and avoids potential pitfalls with time zone handling, although it's also much more complicated and probably totally unnecessary in a realworld use case.

Key Takeaways

  • For valid YYYY-MM-DD dates, UTC parsing and getUTCDay() avoid a clienttimezone shift. Validate the calendar date as well as the string's shape.
  • A manual day count can give the same weekday without Date, but needs the same explicit date range and Gregorian calendar rules.

In this case, it boils down to simplicity vs. finite control.


Need a senior engineer involved?

I can work directly in the codebase, review the architecture, or support the team through delivery when the work needs more than extra hands.