Job Board
Consulting

Spark Scala last_day and next_day

last_day returns the last day of the month that a given date falls in, and next_day returns the first date after a given date that lands on a particular weekday. Both are handy for building reporting periods, billing cycles, and scheduling logic.

last_day

def last_day(e: Column): Column

Takes a date (or anything castable to a date — including a string like "2026-01-15") and returns the last day of that month. The function takes care of leap years and short months automatically.

val df = Seq(
  "2026-01-15",
  "2026-02-10",
  "2026-02-28",
  "2024-02-15",
  "2026-12-01",
).toDF("event_date")

val df2 = df
  .withColumn("month_end", last_day(col("event_date")))

df2.show(false)
// +----------+----------+
// |event_date|month_end |
// +----------+----------+
// |2026-01-15|2026-01-31|
// |2026-02-10|2026-02-28|
// |2026-02-28|2026-02-28|
// |2024-02-15|2024-02-29|
// |2026-12-01|2026-12-31|
// +----------+----------+

Notice that 2024-02-15 returns 2024-02-29last_day correctly handles leap years. Passing a date that already sits on the last day of the month (2026-02-28) just returns the same date.

next_day

def next_day(date: Column, dayOfWeek: String): Column

def next_day(date: Column, dayOfWeek: Column): Column

next_day returns the first date strictly after the input date that falls on the specified weekday. The day name can be passed as a string literal or as a column, and Spark accepts a few different forms — full names ("Monday"), abbreviations ("Mon"), and even shorter forms ("Mo") all work.

val df = Seq(
  "2026-03-10",
  "2026-03-11",
  "2026-03-12",
  "2026-03-13",
  "2026-03-14",
).toDF("event_date")

val df2 = df
  .withColumn("next_friday", next_day(col("event_date"), "Friday"))

df2.show(false)
// +----------+-----------+
// |event_date|next_friday|
// +----------+-----------+
// |2026-03-10|2026-03-13 |
// |2026-03-11|2026-03-13 |
// |2026-03-12|2026-03-13 |
// |2026-03-13|2026-03-20 |
// |2026-03-14|2026-03-20 |
// +----------+-----------+

2026-03-13 is itself a Friday, but next_day returns the following Friday (2026-03-20) — the result is always strictly after the input date, never equal to it.

Passing the Day Name as a Column

If different rows need different target weekdays, pass a column instead of a string literal:

val df = Seq(
  ("2026-05-04", "Mon"),
  ("2026-05-04", "Wed"),
  ("2026-05-04", "Sun"),
  ("2026-05-04", "Tuesday"),
  ("2026-05-04", "FR"),
).toDF("event_date", "target_day")

val df2 = df
  .withColumn("next_target", next_day(col("event_date"), col("target_day")))

df2.show(false)
// +----------+----------+-----------+
// |event_date|target_day|next_target|
// +----------+----------+-----------+
// |2026-05-04|Mon       |2026-05-11 |
// |2026-05-04|Wed       |2026-05-06 |
// |2026-05-04|Sun       |2026-05-10 |
// |2026-05-04|Tuesday   |2026-05-05 |
// |2026-05-04|FR        |2026-05-08 |
// +----------+----------+-----------+

2026-05-04 is a Monday — and asking for the next Monday returns 2026-05-11, again confirming the "strictly after" behavior. All the day-name spellings (Mon, Wed, Sun, Tuesday, FR) resolve correctly.

Handling Nulls and Invalid Dates

Both functions return null when the input is null or cannot be parsed as a valid date. This includes string values that aren't dates at all, and date strings with out-of-range months or days:

val df = Seq(
  "2026-04-15",
  null,
  "not-a-date",
  "2026-13-40",
).toDF("event_date")

val df2 = df
  .withColumn("month_end", last_day(col("event_date")))
  .withColumn("next_monday", next_day(col("event_date"), "Mon"))

df2.show(false)
// +----------+----------+-----------+
// |event_date|month_end |next_monday|
// +----------+----------+-----------+
// |2026-04-15|2026-04-30|2026-04-20 |
// |null      |null      |null       |
// |not-a-date|null      |null       |
// |2026-13-40|null      |null       |
// +----------+----------+-----------+

Garbage input silently becomes null rather than raising an error, so it's worth running a sanity check (e.g., counting nulls) on the output if you're not sure your source data is clean. If you need stricter parsing, see to_date and to_timestamp for explicit format handling.

For shifting a date forward or backward by a fixed number of days or months, see date_add and date_sub. For truncating a date to the start of its month or quarter (the natural counterpart to last_day), see date_trunc. For extracting individual fields like year, month, or day-of-week from a date, see year, month, and dayofmonth.

Example Details

Created: 2026-05-14 10:27:43 PM

Last Updated: 2026-05-14 10:27:43 PM