- 3.0 - Add & Subtract Time Units from Date
- 3.1 - Get DateTime Components
- 3.2 - Get Interval Between Dates
- 3.3 - Convert Date's Region (Locale/TimeZone/Calendar)
- 3.4 - Rounding Date
- 3.5 - Truncating Date
- 3.6 - Set Time in Date
- 3.7 - Set DateTime Components
- 3.8 - Generate Related Dates (
nextYear, nextWeeekday, startOfMonth, startOfWeek, prevMonth
...) - 3.9 - Date at start/end of time component
- 3.10 - Enumerate Dates
- 3.11 - Enumerate Dates for Weekday in Range
- 3.12 - Random Dates
- 3.13 - Sort Dates
- 3.14 - Get the next weekday
- 3.15 - Get date at given week number/weekday
- 3.16 - Difference between dates with components
- 3.17 - Create date at components preserving small components
- 3.18 - Date at given weekday after # weeks
- 3.19 - Next Date
Dates can be manipulated as you need by using classic math operators and readable time units.
SwiftDate allows you to use numbers to work with time components in dates. By extending the Int
type it defines a list of time units:
nanoseconds
seconds
minutes
days
weeks
months
quarters
years
You can use a value followed by one of these unit specifications to add or remove the component from a date.
So, for example, you can produce a date which can be a mix of intuitive math operations:
let oneYearAhead = DateInRegion() + 1.years
let someMinutesAgo = date1 - 2.minutes
let fancyDate = date1 + 3.hours - 5.minutes + 1.weeks
IMPORTANT NOTE: These values are converted automatically to
DateComponents
evaluated in the same context of the targetDate
orDateInRegion
'scalendar
.
Another way to add time components to a date is to use the dateByAdding()
function:
func dateByAdding(_ count: Int, _ component: Calendar.Component) -> DateInRegion
takes two arguments:
count | Int
value to add (maybe negative)component | Calendar.Component
the time unit components to add.
IMPORTANT NOTE: New date is evaluated in the same context of the target
Date
orDateInRegion
'scalendar
.
let nextDate = dateA.dateByAdding(5, .years) // 5 years from dateA
With SwiftDate you have several convenience properties to inspect each datetime unit of a date, both for Date
and DateInRegion
.
These properties are strictly correlated to the date's calendar (and some also with locale): if you are manipulating a DateInRegion
remember these properties return values in the context of the associated region
attributes (Locale, TimeZone and Calendar).
IMPORTANT NOTE: If you are working with plain
Date
properties uses as reference the currently setSwiftDate.defaultRegion
which, unless you modify it, is set to Gregorian/UTC/Device's Language.
This a complete list of the properties you can inspect for a date object:
PROPERTY | DESCRIPTION |
---|---|
year |
current year number |
month |
current month number (1 is January) |
monthName(_ style: SymbolFormatStyle) |
name of the current month with given style (uses region's locale) |
monthDays |
number of the days into the current month |
day |
day number in the current month |
dayOfYear |
day of the year |
ordinalDay |
The number of day in ordinal style format for the receiver in current locale. |
hour |
current hour |
nearestHour |
nearest rounded hour |
minute |
current minute |
second |
current second |
nanosecond |
current nanosecond |
msInDay |
"Milliseconds in day of the receiver. This field behaves exactly like a composite of all time-related fields, not including the zone fields.As such, it also reflects discontinuities of those fields on DST transition days. |
On a day of DST onset, it will jump forward. On a day of DST cessation, it will jump backward. | |
This reflects the fact that is must be combined with the offset field to obtain a unique local time value." | |
weekday |
Weekday unit of the receiver. The weekday units are the numbers 1-N (where for the Gregorian calendar N=7 and 1 is Sunday). |
weekdayName(_ style: SymbolFormatStyle) |
Name of the weekday expressed in given format style. |
weekOfYear |
Week of a year of the receiver. |
weekOfMonth |
Week of a month of the receiver. |
weekdayOrdinal |
Ordinal position within the month unit of the corresponding weekday unit. |
firstDayOfWeek |
Return the first day number of the week where the receiver date is located |
lastDayOfWeek |
Return the last day number of the week where the receiver date is located. |
yearForWeekOfYear |
Relative year for a week within a year calendar unit. |
quarter |
Quarter value of the receiver. |
quarterName(_ style: SymbolFormatStyle) |
Quarter name expressed in given format style |
era |
Era value of the receiver. |
eraName(_ style: SymbolFormatStyle) |
Name of the era expressed in given format style |
DSTOffset |
The current daylight saving time offset of the represented date |
Several other properties defines additional attributes of the date:
date | Date
: return the absolute date instance (forDate
it just return itself)region | Region
: return the region associated with date (forDate
it returnSwiftDate.defaultRegion
).calendar | Calendar
: return the associated calendardateComponents | DateComponents
: return all the date components of the date in the context of its associated region.
You can get the interval between two dates and express it in form of time units easily with SwiftDate.
The .getInterval(toDate:component:)
function allows you to express the difference between to dates in form of a passed time component.
func getInterval(toDate: DateInRegion?, component: Calendar.Component) -> Int64
Takes two arguments:
toDate | DateInRegion
: reference date to compare againstcomponent | Calendar.Component
: the component in which the difference must be returned.
Examples:
let dateA = DateInRegion("2017-07-22 00:00:00", format: format, region: rome)!
let dateB = DateInRegion("2017-07-23 12:00:00", format: format, region: rome)!
let hours = dateA.getInterval(toDate: dateB, component: .hour) // 36 hours
let days = dateA.getInterval(toDate: dateB, component: .day) // 1 day
DateInRegion
can be converted easily to another region just using .convertTo(region:)
or .convertTo(calendar: timezone:locale:)
functions.
convertTo(region:)
convert the receiver date to another region. Region may include a different time zone for example, or a locale.convertTo(calendar:timezone:locale:)
allows to convert the receiver date instance to a specific calendar/timezone/locale. All parameters are optional and only non-nil parameters alter the final region. For a nil param the current receiver's region attribute is kept.
Examples:
// Create a date in NY: "2001-09-11 12:00:05"
let regionNY = Region(calendar: Calendars.gregorian, zone: Zones.americaNewYork, locale: Locales.english)
let dateInNY = DateInRegion(components: {
$0.year = 2001
$0.month = 9
$0.day = 11
$0.hour = 12
$0.minute = 0
$0.second = 5
}, region: regionNY)
// Convert to GMT timezone (and locale)
let inGMT = dateInNY.convertTo(region: Region.UTC) // date is now: "2001-09-11 16:00:05"
.dateRoundedAt()
function allows to round a given date time to the passed style: off, up or down.
func dateRoundedAt(at style: RoundDateMode) -> DateInRegion
takes a single argument:
style | RoundDateMode
: define the style of rounding: it can beoff
,ceil
(up) orfloor
(down).RoundDateMode
defines a list of convenience rounding to 5,10,30 minutes but you can use.toMins()
,.toCeilMins()
or.toFloorMins()
and pass your own rounding values expressed in minutes.
Example:
let rome = Region(...)
let format = "yyyy-MM-dd HH:mm:ss"
// Round down 10mins
let date = DateInRegion("2017-07-22 00:03:50", format: format, region: rome)
let r10min = date.dateRoundedAt(.to10Mins) // 2017-07-22T00:00:00+02:00
// Round up 30min
let r30min = "2015-01-24 15:07:20".toDate(format: format, region: rome).dateRoundedAt(.toCeil30Mins) // 2015-01-24T15:30:00+01:00
Sometimes you may need to truncate a date by zeroing all values below certain time unit. .dateTruncated(from:) and .dateTruncated(to:)
functions can be used for this scope.
It creates a new instance by truncating the components starting from given components down the granurality.
func dateTruncated(from component: Calendar.Component) -> DateInRegion?
It creates a new instance by truncating all passed components.
func dateTruncated(at components: [Calendar.Component]) -> DateInRegion?
Examples:
let rome = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.italian)
let date = "2017-07-22 15:03:50".toDate("yyyy-MM-dd HH:mm:ss", region: rome)
let truncatedTime = date.dateTruncated(from: .hour) // 2017-07-22T00:00:00+02:00
let truncatedCmps = date.dateTruncated(at: [.month, .day, .minute]) // 2017-01-01T15:00:50+01:00
Sometimes you may need to alter time in a specified date. SwiftDate allows you to perform this using .dateBySet(hour:min:secs:options:)
function.
func dateBySet(hour: Int?, min: Int?, secs: Int?, ms: Int?, options: TimeAlterOptions = TimeAlterOptions()) -> DateInRegion?
takes five arguments:
hour | Int?
: set a nonnil
value to change the hour valuemin | Int?
: set a nonnil
value to change the minute valuesecs | Int?
: set a nonnil
value to change the seconds valuems | Int?
: set a nonnil
value to change the milliseconds valueoptions: TimeCalculationOptions
: allows to specify calculation options attributes (usually you don't need to set it).
Example:
let rome = Region(calendar: Calendars.gregorian, zone: Zones.europeRome, locale: Locales.italian)
let date = DateInRegion("2010-01-01 00:00:00", format: "yyyy-MM-dd HH:mm:ss", region: rome)!
let alteredDate = date.dateBySet(hour: 20, min: 13, secs: 15) // 2010-01-01 20:13:15
SwiftDate allows you to return a new date representing the date calculated by setting specific components of a given date to given values, while trying to keep lower components the same (altering more components at the same time may result in different-than-expected results, this is because lower components maybe need to be recalculated).
dateBySet(_ components: [Calendar.Component: Int]) -> DateInRegion?
Takes one argument:
components | [Calendar.Component: Int]
a dictionary of key/values for each component you want to alter.
NOTE: While the algorithm try to keep lower compoments the same, the resulting date may differ from what expected when you alter more than a component a time.
Example:
let _ = date.dateBySet([.month: 1, .day: 1, .hour: 9, .minute: 26, .second: 0])
Sometimes you may need to generate a related date from a specified instance; maybe the next sunday, the first day of the next week or the start datetime of a date.
SwiftDate includes 20+ different "interesting" dates you can obtain by calling .dateAt()
function from any Date
or DateInRegion
instance.
func dateAt(_ type: DateRelatedType) -> DateInRegion
takes just an argument which defines the type of date you want to obtain starting from the receiver date.
DateRelatedType
is an enum which has the following options:
startOfDay
endOfDay
startOfWeek
endOfWeek
startOfMonth
endOfMonth
tomorrow
tomorrowAtStart
yesterday
yesterdayAtStart
nearestMinute(minute:Int)
nearestHour(hour:Int)
nextWeekday(_: WeekDay)
nextDSTDate
prevMonth
nextMonth
prevWeek
nextWeek
nextYear
prevYear
CONTRIBUTE! Have you a new related date you want to be part of this list? Create a new PR with the code and unit tests and we'll be happy to add it to the list!
Examples:
// Return today's datetime at 00:00:00
let _ = DateInRegion().dateAt(.startOfDay)
// Return today's datetime at 23:59:59
let _ = DateInRegion().dateAt(.endOfDay)
// Return the date at the start of this week
let _ = DateInRegion().dateAt(.startOfWeek)
// Return current time tomorrow
let _ = DateInRegion().dateAt(.tomorrow)
// Return the next sunday from specified date
let _ = date.dateAt(.nextWeekday(.sunday))
// and so on...
Two functions called .dateAtStartOf()
and .dateAtEndOf()
allows you to get the related date from a Date
/DateInRegion
instance moved at the start or end of the specified component.
You can, for example, get the date at the start of the week, or the year, or a the end of the quarter.
func dateAtStartOf(_ unit: Calendar.Component) -> DateInRegion
: return the date at the start of the specified time component.func dateAtEndOf(_ unit: Calendar.Component) -> DateInRegion
: return the date at the end of the specified time component.
Examples:
// Return today's date at 23:59:59
let _ = DateInRegion().dateAtEndOf(.day)
// Return the first day's date of the month described in date1
let _ = date1.dateAtStartOf(.month)
// Return the first day of this year at 00:00:00
let _ = DateInRegion().dateAtStartOf(.year)
Dates enumeration function allows you to generate a list of dates in a closed date intervals incrementing date components by a fixed or variable interval at each new date.
- VARIABLE INCREMENT
static func enumerateDates(from startDate: DateInRegion, to endDate: DateInRegion, increment: ((DateInRegion) -> (DateComponents))) -> [DateInRegion]
- FIXED INCREMENT
static func enumerateDates(from startDate: DateInRegion, to endDate: DateInRegion, increment: DateComponents) -> [DateInRegion]
Both of these functions are pretty similar; it takes:
startDate | DateInRegion
: the initial date of the enumeration (it will be item #0 of the final array)endDate | DateInRegion
: the upper bound limit date. The last item of the array is evaluated automatically and maybe not equal toendDate
.increment | DateComponents or Function
: for fixed increment it takes aDateComponents
instance which define the increment of time components at each new date; for variable it provides the latest date generated and require as return object of the closure the increment asDateComponents
.
Examples:
let increment = DateComponents.create {
$0.hour = 1
$0.minute = 30
}
// Generate an array of dates where the first item is fromDate
// and each new date is incremented by 1h30m from the previous.
// Latest date is < endDate (but maybe not the same).
let dates = DateInRegion.enumerateDates(from: fromDate, to: toDate, increment: increment)
The following function allows you to enumerate all dates of a particular weekdays which are in a determinated date range.
Both Date
and DateInRegion
implements this static function:
public static func datesForWeekday(_ weekday: WeekDay, from startDate: Date, to endDate: Date, region: Region = SwiftDate.defaultRegion) -> [Date]
Another shortcut method allows you to get weekdays in a particular month of a year:
public static func datesForWeekday(_ weekday: WeekDay, inMonth month: Int, ofYear year: Int, region: Region = SwiftDate.defaultRegion) -> [Date]
Examples:
// Get all mondays in Jan 2019
let mondaysInJan2019 = Date.datesForWeekday(.monday, inMonth: 1, ofYear: 2019)
// You will get 4 results
// - 2019-01-07T00:00:00Z
// - 2019-01-14T00:00:00Z
// - 2019-01-21T00:00:00Z
// - 2019-01-28T00:00:00Z
// Get all fridays between May 27, 2019 and June 8, 2019
let fromDate = Date(year: 2019, month: 5, day: 27, hour: 0, minute: 0)
let toDate = Date(year: 2019, month: 6, day: 8, hour: 0, minute: 0)
let fridaysInJunePartial = Date.datesForWeekday(.friday, from: fromDate, to: toDate, region: Region.UTC)
// You will get 2 results:
// - 2019-05-31T00:00:00Z
// - 2019-06-07T00:00:00Z
SwiftDate exposes a set of functions to generate a random date or array of random dates in a bounds. There are several functions to perform this operation:
randomDate(region:)
generate a random date into the specified region.randomDate(withinDaysBeforeToday:region:)
generate a random date between now and a specified amount days ealier into the specified region.randomDate(between:and:region:)
generate a random date into the specified region between two given date bounds.
randomDates(count:between:and:region:)
returncount
random generated dates into the specified region between two given date bounds.
IMPORTANT: For all of thes function if you don't specify the
region
,SwiftDate.defaultRegion
is used instead.
Examples:
// Generate random dates array in limit
let now = DateInRegion()
let someYearsAgo = (upperBound - 3.years)
// generate 40 random dates between 3 years ago today and today
let randomDates = DateInRegion.randomDates(count: 40, between: someYearsAgo, and: now)
// Generate a random date between now and 7 days ago
let rome: Region = ...
let aDate = DateInRegion.randomDate(withinDaysBeforeToday: 7, region: rome)
Two conveniences function allows you to sort an array of dates by newest or oldest. Naming is pretty simple:
sortedByOldest()
: sort dates by the oldestsortedByNewest()
: sort dates by the newest
Two other functions allows you to get the oldest/newest date in array:
oldestIn()
: return the oldest date in arraynewestIn()
: return the newest date in array
Examples:
let arrayOfDates: [DateInRegion] = [...]
// ordered array with the newest on top
let orderedByNewest = DateInRegion.sortedByNewest(list: datesArray)
// get the oldest date of the list
let oldestDate = DateInRegion.oldestIn(list: arrayOfDates)
In order to get the next weekday preserving smaller components (hour, minute, seconds) you can use the nextWeekday()
function:
let date1 = DateInRegion("2019-05-11 00:00:00", format: dateFormat, region: regionRome)!
let nextFriday = date1.nextWeekday(.friday) // 2019-05-17T00:00:00+02:00
To returns the date at the given week number and week day preserving smaller components (hour, minute, seconds) you can use the dateAt(weekdayOrdinal:weekday:monthNumber:yearNumber:)
function:
let date = DateInRegion("2019-05-11 00:00:00", format: dateFormat, region: regionRome)!
let _ = date1.dateAt(weekdayOrdinal: 3, weekday: .friday, monthNumber: date1.month + 1) // 2019-06-21T00:00:00+02:00
To get differences between two dates using given time components you can choose between two methods:
difference(in:from:)
: Returns the difference in the calendar component given (like day, month or year) with respect to the other date as a positive integer.differences(in:from:)
: Returns the differences in the calendar components given (like day, month and year) with respect to the other date as dictionary with the calendar component as the key and the diffrence as a positive integer as the value.
Example:
let format = "yyyy-MM-dd HH:mm"
let d1 = "2019-01-01 00:00".toDate(format, region: Region.current)
let d2 = "2019-01-10 14:20".toDate(format, region: Region.current)
let diff = d1!.differences(in: [.day, .hour], from: d2!) // 9 days, 14 hours
You can create a derivated date from a receiver by preserving small components (hour, minute and second) by using the: dateAt(dayOfMonth:monthNumber:yearNumber:)
method:
let date = "2019-01-01 00:00".toDate("yyyy-MM-dd HH:mm", region: Region.current)! // Rome zone
let onMarch = date.dateAt(dayOfMonth: 31, monthNumber: 3) // 2019-03-31T00:00:00+01:00
Returns the date after given number of weeks on the given day of week using dateAfter(weeks:on:)
function.
let d = "2019-09-14 00:00".toDate("yyyy-MM-dd HH:mm", region: Region.current)! // Rome Region
let nextTwoMondays = d.dateAfter(weeks: 2, on: .monday) // 2019-09-23T00:00:00+02:00
There are 3 functions to get the next date starting from a receiver date:
nextWeekday()
: Returns the next weekday preserving smaller componentsnextWeekday(:withWeekOfMonth:andMonthNumber)
: Returns next date with the given weekday and the given week numbernext(dayOfMonth:monthOfYear:)
: Returns the next day of month preserving smaller components (hour, minute, seconds)