A comprehensive Swift package providing powerful and intuitive extensions for working with dates, times, and date components. Built with dependency injection support and comprehensive test coverage.
- Safe date initialization with automatic validation
- Returns
nil
for invalid dates (Feb 30, hour 25, etc.) - Supports leap year validation
- Intuitive operators:
date + 1.day
,date - 2.weeks
- Safe arithmetic methods that return optionals
- Complex calculations:
date + 1.year + 6.months + 2.days
- Start/end of day, week, month, year
- First/last day of month
- Comprehensive boundary calculations
isToday
,isTomorrow
,isYesterday
isThisWeek
,isThisMonth
,isThisYear
isWeekend
with weekend navigation
- Convenient time constants:
TimeInterval.hour
,.day
,.week
- Duration formatting:
"2h"
,"3d"
,"1w"
- Conversion utilities
- Natural language:
"2 hours ago"
,"in 3 days"
- Smart formatting:
"yesterday"
,"tomorrow"
- Automatic singular/plural handling
- Business day calculations
- Weekday navigation
- Age calculations
- DateComponents validation
- Custom format styles
Add DateExtensions to your Swift Package Manager dependencies:
dependencies: [
.package(url: "https://github.com/coenttb/swift-foundation-extensions.git", from: "1.0.0")
]
import DateExtensions
// Create dates safely
let date = Date(year: 2025, month: 7, day: 26)!
let invalidDate = Date(year: 2025, month: 2, day: 30) // Returns nil
// Date arithmetic
let tomorrow = Date() + 1.day
let nextWeek = Date() + 1.week
let complex = Date() + 1.year + 6.months + 2.days
// Date boundaries
let startOfDay = Date().startOfDay
let endOfMonth = Date().endOfMonth
let startOfYear = Date().startOfYear
// State checks
if Date().isToday {
print("It's today!")
}
// Relative formatting
let pastDate = Date() - 2.hours
print(pastDate.relativeFormatted) // "2 hours ago"
let futureDate = Date() + 3.days
print(futureDate.relativeFormatted) // "in 3 days"
// Basic creation
let date1 = Date(year: 2025, month: 7, day: 26)
let date2 = Date(year: 2025, month: 12, day: 25, hour: 15, minute: 30, second: 45)
// Validation - returns nil for invalid dates
let invalid1 = Date(year: 2025, month: 13, day: 1) // nil - invalid month
let invalid2 = Date(year: 2025, month: 2, day: 30) // nil - Feb doesn't have 30 days
let invalid3 = Date(year: 2025, month: 1, day: 1, hour: 25) // nil - invalid hour
// Basic arithmetic
let date = Date()
let tomorrow = date + 1.day
let lastWeek = date - 1.week
let nextMonth = date + 1.month
// Safe arithmetic (returns optionals)
let safeResult = date.adding(1.day) // Date?
let safeSubtract = date.subtracting(1.week) // Date?
// Complex calculations
let complex = date + 1.year + 6.months + 2.days + 3.hours + 30.minutes
// Time components
1.second, 30.seconds
1.minute, 45.minutes
1.hour, 12.hours
1.day, 7.days
1.week, 2.weeks
1.month, 6.months
1.year, 5.years
// Calendar components
1.weekday, 1.quarter
1.weekOfMonth, 1.weekOfYear
let date = Date()
// Day boundaries
let startOfDay = date.startOfDay // 00:00:00
let endOfDay = date.endOfDay // 23:59:59
// Week boundaries
let startOfWeek = date.startOfWeek
let endOfWeek = date.endOfWeek
// Month boundaries
let startOfMonth = date.startOfMonth
let endOfMonth = date.endOfMonth
let firstDay = date.firstDayOfMonth // Same as startOfMonth but different time
let lastDay = date.lastDayOfMonth // Last day at 00:00:00
// Year boundaries
let startOfYear = date.startOfYear
let endOfYear = date.endOfYear
let date = Date()
// Relative to today
date.isToday // true if date is today
date.isTomorrow // true if date is tomorrow
date.isYesterday // true if date was yesterday
// Relative to current periods
date.isThisWeek // true if date is in current week
date.isThisMonth // true if date is in current month
date.isThisYear // true if date is in current year
// Weekend checks
date.isWeekend // true if Saturday or Sunday
let date1 = Date()
let date2 = Date() + 1.day
// Readable comparisons
date2.isAfter(date1) // true
date1.isBefore(date2) // true
date1.isSameDay(as: date1) // true
let date = Date()
// Weekend handling
if date.isWeekend {
let nextWorkday = date.ifWeekendThenNextWorkday()
let prevWorkday = date.ifWeekendThenPreviousWorkday()
}
let nextWeekday = date.nextWeekday // Next Monday-Friday
// Business day calculations
let fiveBusinessDaysLater = date.addingBusinessDays(5)
let fiveBusinessDaysEarlier = date.addingBusinessDays(-5)
let date = Date()
// Navigate to specific weekdays (1=Sunday, 2=Monday, ..., 7=Saturday)
let nextMonday = date.next(2) // Next Monday
let previousFriday = date.previous(6) // Previous Friday
let startDate = Date()
let endDate = Date() + 10.days
// Calculate differences
let daysBetween = startDate.daysBetween(endDate) // 10
let age = birthDate.age() // Age in years from birth date to now
let ageAt = birthDate.age(at: someDate) // Age at specific date
// Constants
TimeInterval.minute // 60
TimeInterval.hour // 3600
TimeInterval.day // 86400
TimeInterval.week // 604800
// Conversions
let twoHours: TimeInterval = 2.hours // 7200
let thirtyMinutes: TimeInterval = 30.minutes // 1800
// As conversions
let interval: TimeInterval = 7200
interval.asHours // 2.0
interval.asMinutes // 120.0
interval.asDays // 0.083...
// Formatted duration
(30.0).formattedDuration // "30s"
(90.0).formattedDuration // "2m"
(3660.0).formattedDuration // "1.0h"
(86500.0).formattedDuration // "1.0d"
let now = Date()
// Past dates
let pastDate = now - 2.hours
pastDate.timeAgoSince(now) // "2 hours ago"
pastDate.relativeFormatted // "2 hours ago"
// Future dates
let futureDate = now + 3.days
futureDate.timeUntil(now) // "in 3 days"
futureDate.relativeFormatted // "in 3 days"
// Special cases
let yesterday = now - 1.day
yesterday.relativeFormatted // "yesterday"
let tomorrow = now + 1.day
tomorrow.relativeFormatted // "tomorrow"
// Very recent
let recent = now - 5.seconds
recent.relativeFormatted // "just now"
let date = Date(year: 2025, month: 7, day: 26, hour: 15, minute: 30)!
// Basic components
date.year // 2025
date.month // 7
date.day // 26
date.hour // 15
date.minute // 30
date.second // 0
// Advanced components
date.weekday // 1-7 (Sunday=1)
date.weekOfYear // Week number in year
date.weekOfMonth // Week number in month
date.quarter // 1-4
date.era // Calendar era
// Calendar & timezone info
date.calendarIdentifier // Calendar.Identifier
date.timeZone // TimeZone
// Combine components
let components = 1.day + 2.hours + 30.minutes
let result = Date() + components
// Multiply components
let threeDays = 1.day * 3
let sixMonths = 1.month * 6
// Subtract components
let difference = 2.weeks - 3.days
// Negate components
let negated = components.negated()
let components = DateComponents(year: 2025, month: 7, day: 26)
// Basic validation
components.isValid // true for valid ranges
// Calendar-specific validation
let calendar = Calendar.current
components.isValid(for: calendar) // true if can create valid date
// Invalid examples
let invalid = DateComponents(month: 13, day: 1)
invalid.isValid // false - month 13 doesn't exist
// DateFormatter extensions
let formatter = DateFormatter.dateFormat("yyyy-MM-dd")
let dateString = formatter.string(from: Date()) // "2025-07-26"
// FormatStyle extensions (iOS 15+)
let formatted = Date().formatted(.dateFormat("MMM d, yyyy")) // "Jul 26, 2025"
- iOS 13.0+ / macOS 10.15+ / tvOS 13.0+ / watchOS 6.0+
- Swift 5.5+
- Dependencies framework
This package uses the Dependencies framework for dependency injection, making it testable and allowing for calendar mocking in tests.
The package includes comprehensive test coverage with 67+ tests covering all functionality:
swift test
Test categories include:
- Date initialization and validation
- Arithmetic operations
- Date boundaries and state checks
- Component properties and validation
- TimeInterval extensions
- Relative date formatting
- Performance tests
- Edge cases
Contributions are welcome! Please feel free to submit a Pull Request. Make sure to:
- Add tests for new functionality
- Update documentation
- Follow existing code style
- Ensure all tests pass
This project is licensed under the Apache 2.0 License - see the LICENSE file for details.
If you’re working on your own Swift web project, feel free to learn, fork, and contribute.
Got thoughts? Found something you love? Something you hate? Let me know! Your feedback helps make this project better for everyone. Open an issue or start a discussion—I’m all ears.