$dayOfYear is a MongoDB aggregation expression that returns the day number within the year for a date as an integer from 1 to 366. January 1 is 1; December 31 is 365 or 366 in a leap year.
Remember
$dayOfYear = day number inside the year
{ $dayOfYear: <dateExpression> }
{ $dayOfYear: { date: <date>, timezone: <tz> } }
Jan 1 → 1
Feb 29 → 60 (leap years only)
Dec 31 → 365 or 366
default timezone → UTC
string date → error
null / missing → null
Ensure the argument is a Date / Timestamp / ObjectId (not a plain string).
Pass that expression to $dayOfYear (add timezone when you need a local calendar day).
Optionally filter ranges with $match (for example late-year days).
Piece
Required?
Role
$dayOfYear
Yes
Day-of-year → integer 1–366
Date expression
Yes
Field, ISODate, Timestamp, or ObjectId
timezone
No
Local day-of-year instead of UTC
Pseudocode
Pseudocode
for each document:
d = evaluate(dateExpression) // Date / Timestamp / ObjectId
if d is null or missing:
result = null
else if d is a string:
error
else:
// optional timezone shifts the calendar day first
result = dayNumberInYear(d) // 1 … 366
emit { ..., field: result }
Reference
Cheat Sheet
Keep these facts handy while extracting day-of-year values.
Watch $dayOfYear turn a UTC date into a day number inside the year.
Simulates { $dayOfYear: ISODate("…") } in UTC (same idea as the mongosh examples below).
Result
when = 2024-02-29T12:00:00.000Z
$dayOfYear → 60
{ title: "Leap Day", dayOfYear: 60 }
Trace
Worked Walkthrough — Why Leap Day Is 60
In 2024 (a leap year), February 29 is the 60th day. March 1 becomes day 61—one later than in a non-leap year.
Calculation
UTC date: 2024-02-29T12:00:00Z
Days before Feb 29 in a leap year:
Jan (31) + Feb 1–28 (28) = 59
Feb 29 is the next day → 60
$dayOfYear(...) → 60
Compare:
2023-03-01 → day 60 (no Feb 29)
2024-03-01 → day 61 (leap day pushed March forward)
2024-12-31 → day 366
Code
MongoDB Examples
We store three 2024 dates (New Year, leap day, year end), extract the day-of-year, then keep late-year events. Run these in mongosh after inserting the sample data once.
Example 1 — Sample Data
Imagine a tiny milestones collection with three UTC dates in the leap year 2024.
Add the day number first.$addFields computes dayOfYear for every milestone.
Keep late days only.$match with $gte: 300 drops Jan 1 and leap day.
Trim the output.$project keeps title and dayOfYear.
Easy check. Only Year End (366) is at or after day 300.
Edge Cases & Pitfalls
Check these before using $dayOfYear in production pipelines.
leap
366 only in leap years
Do not assume December 31 is always 366. In 2023 it is 365. Leap day itself is day 60.
timezone
UTC by default
A late local evening can be the next UTC day and bump the day-of-year. Pass timezone when the local calendar day matters.
types
Strings are not dates
"2024-01-01" as a string errors. Store BSON dates, or convert with $dateFromString first.
null
Null and missing become null
Missing date fields return null, not an error. Filter them before range checks if nulls are invalid.
Analysis
Performance Notes
Approach
Cost
Best for
$dayOfYear alone
Per-document date math
Reports and season buckets
+ $match on day number
Still scans after compute
Early/late year filters
Date-range $match first
Can use indexes on the date field
Narrowing to one year before extracting
Date extraction is cheap compared with large joins. Prefer an early date-range $match when you only need one year of data.
Remember
Key Takeaways
Role: day number in the year → 1–366.
Leap years: Feb 29 = 60, Dec 31 = 366.
Timezone: UTC by default; pass timezone for local days.
Siblings:$dayOfMonth / $dayOfWeek for other day scales.
One line:$dayOfYear answers “which day of the year is this date?”—from 1 on Jan 1 up to 365 or 366.
Frequently Asked Questions
$dayOfYear returns an integer from 1 to 366 for the day number within the year. January 1 is 1. December 31 is 365 in a common year and 366 in a leap year. Use it inside aggregation expression stages such as $project and $addFields.
Short form: { $dayOfYear: <dateExpression> }. Document form: { $dayOfYear: { date: <dateExpression>, timezone: <tz> } }. The date must resolve to a Date, Timestamp, or ObjectId—not a string.
Yes by default. Pass timezone (Olson id like "America/New_York" or an offset like "-0400") in the document form when you need a local day-of-year.
In a leap year, February 29 is day 60 and December 31 is 366. In a non-leap year, December 31 is 365 and there is no day 60 for Feb 29.
$dayOfYear counts from the start of the year (1–366). $dayOfMonth counts from the start of the month (1–31).
$dayOfYear returns null when the input is null or the field is missing. A plain string date causes an aggregation error.