Part of Swift with Copilot

How many days are there in the month of December in the year 2023?

Sandy LaneSandy Lane

Video: How many days are there in the month of December in the year 2023? by Taught by Celeste AI - AI Coding Coach

Take the quiz on the full lesson page
Test what you've read · interactive walkthrough

Swift with Copilot: How Many Days in December 2023?

Calendar.range(of: .day, in: .month, for: date)?.count returns 31 for December, 28/29 for February, etc. Don't hardcode "30 days hath September..." — let the calendar handle it.

A practical date question that shows up in scheduling and reporting code.

The Copilot prompt

// How many days are in December 2023?
import Foundation

Copilot completes:

import Foundation

var components = DateComponents()
components.year = 2023
components.month = 12
let date = Calendar.current.date(from: components)!

let range = Calendar.current.range(of: .day, in: .month, for: date)!
print("December 2023: \(range.count) days")
// December 2023: 31 days

Walkthrough

Build a Date for the month

var components = DateComponents()
components.year = 2023
components.month = 12
let date = Calendar.current.date(from: components)!

DateComponents is "I want to specify some calendar fields, you fill in the rest." With just year and month, the resulting Date is the start of that month (Dec 1, midnight).

Calendar.current.date(from:) returns Date?. For valid components, it's never nil — but force-unwrap with ! is acceptable for hardcoded values.

Range of days in the month

let range = Calendar.current.range(of: .day, in: .month, for: date)!

range(of:in:for:) answers "what values can the smaller unit have within the larger unit, for this specific date?"

Here: "days within month, for December 2023" → range is 1..<32 (32 exclusive). The .count is 31.

For February 2024: 1..<30 → 29 days. For February 2023: 1..<29 → 28 days.

Other ranges

let cal = Calendar.current

// Months in a year
cal.range(of: .month, in: .year, for: date)?.count
// 12

// Hours in a day (24, except DST transitions)
cal.range(of: .hour, in: .day, for: date)?.count
// 24

// Minutes in an hour
cal.range(of: .minute, in: .hour, for: date)?.count
// 60

Days in any month

import Foundation

func daysInMonth(year: Int, month: Int) -> Int {
  var c = DateComponents()
  c.year = year; c.month = month
  let cal = Calendar.current
  guard let date = cal.date(from: c),
        let range = cal.range(of: .day, in: .month, for: date) else {
    return 0
  }
  return range.count
}

print(daysInMonth(year: 2023, month: 12))    // 31
print(daysInMonth(year: 2024, month: 2))     // 29 — leap year
print(daysInMonth(year: 2023, month: 2))     // 28
print(daysInMonth(year: 2023, month: 4))     // 30

Generic, works for any year/month, handles leap years automatically.

Why use Calendar instead of a hard-coded array

// Hard-coded — works for Gregorian, fails for non-Gregorian calendars
let daysPerMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]

This works for Gregorian and you have to special-case February. For Islamic, Hebrew, Persian, Buddhist calendars: different. Calendar handles them all:

let cal = Calendar(identifier: .islamic)
// ... rest of code unchanged ...

For business code dealing with international dates, always use Calendar.

Days between two dates

let cal = Calendar.current
let start = cal.date(from: DateComponents(year: 2023, month: 12, day: 1))!
let end   = cal.date(from: DateComponents(year: 2024, month: 1, day: 1))!

let days = cal.dateComponents([.day], from: start, to: end).day!
print(days)    // 31

dateComponents([.day], from:to:) returns the difference in those units.

For "days between any two dates":

func daysBetween(_ d1: Date, _ d2: Date) -> Int {
  Calendar.current.dateComponents([.day], from: d1, to: d2).day ?? 0
}

First and last day of month

let cal = Calendar.current
let date = Date()    // any date in the month

let comps = cal.dateComponents([.year, .month], from: date)
let firstDay = cal.date(from: comps)!

let range = cal.range(of: .day, in: .month, for: date)!
var lastComps = comps
lastComps.day = range.count
let lastDay = cal.date(from: lastComps)!

Useful for monthly reports.

Today's day-of-month

let cal = Calendar.current
let day = cal.component(.day, from: Date())
print("Today is the \(day)th")

component(_:from:) extracts a single component as Int.

Common stumbles

! on date(from:). Mostly safe for valid components, but for user input prefer guard let. Same for range(...).

Calendar identity. Calendar.current follows the user's settings. For storage/server, use Calendar(identifier: .gregorian) explicitly.

Time zone. DateComponents without time zone uses the calendar's default (usually current). For UTC consistency, set c.timeZone = TimeZone(identifier: "UTC") and use Calendar.current (or set the calendar's timeZone).

.range returns Range?, not Int. Use .count on the range.

Year 0. Gregorian has no year 0; Swift's calendar handles it correctly. For astronomical numbering, you'd need different math.

February 29 + 1 year. Calendar.date(byAdding:value:to:) returns Feb 28 (since 2025 has no Feb 29). Aware of leap years; you don't need to be.

What's next

Episode 12: Sum an array with reduce. Functional aggregation.

Recap

Calendar.range(of: .day, in: .month, for: date)?.count returns the number of days. Build the date with DateComponents and Calendar.current.date(from:). Works across calendars (Islamic, Hebrew, etc.). For "days between dates": dateComponents([.day], from: a, to: b).day. Don't hardcode "30 days hath September" — use the calendar.

Next episode: reduce.

Ready? Take the quiz on the full lesson page →
Test what you've learned. Watch the lesson and try the interactive quiz on the same page.