while selecting sunday date it select date of next week [duplicate] - ios

I want to get the current week start and end date and I also want to use the previous
week start and end date and next week of the start and end date in current month.
Thanks in Advance.

rangeOfUnit:startDate:interval:forDate:. It gives you the start and the interval for a certain time unit. With it it is easy to find the start of the week in the used calendar and add the range-1 to get the latest second in that week.
NSCalendar *cal = [NSCalendar currentCalendar];
NSDate *now = [NSDate date];
NSDate *startOfTheWeek;
NSDate *endOfWeek;
NSTimeInterval interval;
[cal rangeOfUnit:NSWeekCalendarUnit
startDate:&startOfTheWeek
interval:&interval
forDate:now];
//startOfWeek holds now the first day of the week, according to locale (monday vs. sunday)
endOfWeek = [startOfTheWeek dateByAddingTimeInterval:interval-1];
// holds 23:59:59 of last day in week.

I solve the problem thanks for Support
Code :- it give the current week start and end date.
NSDate *today = [NSDate date];
NSLog(#"Today date is %#",today);
dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:#"yyyy-MM-dd"];// you can use your format.
//Week Start Date
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *components = [gregorian components:NSWeekdayCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:today];
int dayofweek = [[[NSCalendar currentCalendar] components:NSWeekdayCalendarUnit fromDate:today] weekday];// this will give you current day of week
[components setDay:([components day] - ((dayofweek) - 2))];// for beginning of the week.
NSDate *beginningOfWeek = [gregorian dateFromComponents:components];
NSDateFormatter *dateFormat_first = [[NSDateFormatter alloc] init];
[dateFormat_first setDateFormat:#"yyyy-MM-dd"];
dateString2Prev = [dateFormat stringFromDate:beginningOfWeek];
weekstartPrev = [[dateFormat_first dateFromString:dateString2Prev] retain];
NSLog(#"%#",weekstartPrev);
//Week End Date
NSCalendar *gregorianEnd = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *componentsEnd = [gregorianEnd components:NSWeekdayCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit fromDate:today];
int Enddayofweek = [[[NSCalendar currentCalendar] components:NSWeekdayCalendarUnit fromDate:today] weekday];// this will give you current day of week
[componentsEnd setDay:([componentsEnd day]+(7-Enddayofweek)+1)];// for end day of the week
NSDate *EndOfWeek = [gregorianEnd dateFromComponents:componentsEnd];
NSDateFormatter *dateFormat_End = [[NSDateFormatter alloc] init];
[dateFormat_End setDateFormat:#"yyyy-MM-dd"];
dateEndPrev = [dateFormat stringFromDate:EndOfWeek];
weekEndPrev = [[dateFormat_End dateFromString:dateEndPrev] retain];
NSLog(#"%#",weekEndPrev);

Here is Swift 3 Version:-
extension Date {
var startOfWeek: Date? {
let gregorian = Calendar(identifier: .gregorian)
guard let sunday = gregorian.date(from: gregorian.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self)) else { return nil }
return gregorian.date(byAdding: .day, value: 1, to: sunday)
}
var endOfWeek: Date? {
let gregorian = Calendar(identifier: .gregorian)
guard let sunday = gregorian.date(from: gregorian.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self)) else { return nil }
return gregorian.date(byAdding: .day, value: 7, to: sunday)
}
}
You can get the week start date and end date like this :
let startWeek = Date().startOfWeek
let endWeek = Date().endOfWeek
print(startWeek ?? "not found start date")
print(endWeek ?? "not found end date")

A Swift version
of vikingosegundo's answer:
let calendar = NSCalendar.currentCalendar()
var startOfTheWeek: NSDate?
var endOfWeek: NSDate!
var interval = NSTimeInterval(0)
calendar.rangeOfUnit(.WeekOfMonth, startDate: &startOfTheWeek, interval: &interval, forDate: NSDate())
endOfWeek = startOfTheWeek!.dateByAddingTimeInterval(interval - 1)

Here's an elegant way for Swift 3 (Xcode 8+):
extension Date {
var startOfWeek: Date {
let date = Calendar.current.date(from: Calendar.current.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self))!
let dslTimeOffset = NSTimeZone.local.daylightSavingTimeOffset(for: date)
return date.addingTimeInterval(dslTimeOffset)
}
var endOfWeek: Date {
return Calendar.current.date(byAdding: .second, value: 604799, to: self.startOfWeek)!
}
}
And we can use this extension like this:
print(Date().startOfWeek)
print(Date().endOfWeek)

Swift 4 Solution
I have figured out according to my requirement, where I have find out dates for following.
1. Today
2. Tomorrow
3. This Week
4. This Weekend
5. Next Week
6. Next Weekend
So, I have created Date Extension to get Dates of Current Week and Next Week.
CODE
extension Date {
func getWeekDates() -> (thisWeek:[Date],nextWeek:[Date]) {
var tuple: (thisWeek:[Date],nextWeek:[Date])
var arrThisWeek: [Date] = []
for i in 0..<7 {
arrThisWeek.append(Calendar.current.date(byAdding: .day, value: i, to: startOfWeek)!)
}
var arrNextWeek: [Date] = []
for i in 1...7 {
arrNextWeek.append(Calendar.current.date(byAdding: .day, value: i, to: arrThisWeek.last!)!)
}
tuple = (thisWeek: arrThisWeek,nextWeek: arrNextWeek)
return tuple
}
var tomorrow: Date {
return Calendar.current.date(byAdding: .day, value: 1, to: noon)!
}
var noon: Date {
return Calendar.current.date(bySettingHour: 12, minute: 0, second: 0, of: self)!
}
var startOfWeek: Date {
let gregorian = Calendar(identifier: .gregorian)
let sunday = gregorian.date(from: gregorian.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self))
return gregorian.date(byAdding: .day, value: 1, to: sunday!)!
}
func toDate(format: String) -> String {
let formatter = DateFormatter()
formatter.dateFormat = format
return formatter.string(from: self)
}
}
USAGE:
let arrWeekDates = Date().getWeekDates() // Get dates of Current and Next week.
let dateFormat = "MMM dd" // Date format
let thisMon = arrWeekDates.thisWeek.first!.toDate(format: dateFormat)
let thisSat = arrWeekDates.thisWeek[arrWeekDates.thisWeek.count - 2].toDate(format: dateFormat)
let thisSun = arrWeekDates.thisWeek[arrWeekDates.thisWeek.count - 1].toDate(format: dateFormat)
let nextMon = arrWeekDates.nextWeek.first!.toDate(format: dateFormat)
let nextSat = arrWeekDates.nextWeek[arrWeekDates.nextWeek.count - 2].toDate(format: dateFormat)
let nextSun = arrWeekDates.nextWeek[arrWeekDates.nextWeek.count - 1].toDate(format: dateFormat)
print("Today: \(Date().toDate(format: dateFormat))") // Sep 26
print("Tomorrow: \(Date().tomorrow.toDate(format: dateFormat))") // Sep 27
print("This Week: \(thisMon) - \(thisSun)") // Sep 24 - Sep 30
print("This Weekend: \(thisSat) - \(thisSun)") // Sep 29 - Sep 30
print("Next Week: \(nextMon) - \(nextSun)") // Oct 01 - Oct 07
print("Next Weekend: \(nextSat) - \(nextSun)") // Oct 06 - Oct 07
You can modify Extension according to your need.
Thanks!

Solution for Swift 5.x
Create Date extension:
func weekPeriod() -> (startDate: Date, endDate: Date) {
let calendar = Calendar(identifier: .gregorian)
guard let sundayDate = calendar.date(from: calendar.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self)) else { return (startDate: Date(), endDate: Date()) }
if calendar.isDateInToday(sundayDate) {
let startDate = calendar.date(byAdding: .day, value: -6, to: sundayDate) ?? Date()
let endDate = sundayDate
return (startDate: startDate, endDate: endDate)
}
let startDate = calendar.date(byAdding: .day, value: 1, to: sundayDate) ?? Date()
let endDate = calendar.date(byAdding: .day, value: 6, to: startDate) ?? Date()
return (startDate: startDate, endDate: endDate)
}

First find the current date...
NSDate *today = [NSDate date];
NSCalendar *gregorian = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
NSDateComponents *weekdayComponents = [gregorian components:NSWeekdayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit fromDate:today];
Calcuate number of days to substract from today, in order to get the first day of the week. In this case, the first day of the week is monday. This is represented by first subtracting 0 with the weekday integer followed by adding 2 to the setDay.
Sunday = 1, Monday = 2, Tuesday = 3, Wednesday = 4, Thursday = 5, Friday = 6 and Saturday = 7. By adding more to this integers, you will go into the next week.
NSDateComponents *componentsToSubtract = [[NSDateComponents alloc] init];
[componentsToSubtract setDay: (0 - [weekdayComponents weekday]) + 2];
[componentsToSubtract setHour: 0 - [weekdayComponents hour]];
[componentsToSubtract setMinute: 0 - [weekdayComponents minute]];
[componentsToSubtract setSecond: 0 - [weekdayComponents second]];
Create date for first day in week
NSDate *beginningOfWeek = [gregorian dateByAddingComponents:componentsToSubtract toDate:today options:0];
By adding 6 to the date of the first day, we can get the last day, in our example Sunday.
NSDateComponents *componentsToAdd = [gregorian components:NSDayCalendarUnit fromDate:beginningOfWeek];
[componentsToAdd setDay:6];
NSDate *endOfWeek = [gregorian dateByAddingComponents:componentsToAdd toDate:beginningOfWeek options:0];
for next and previous ....
-(IBAction)Week_CalendarActionEvents:(id)sender{
NSCalendar *gregorian = [[[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar] autorelease];
NSDateComponents *offsetComponents = [[[NSDateComponents alloc] init] autorelease];
NSDate *nextDate;
if(sender==Week_prevBarBtn) // Previous button events
[offsetComponents setDay:-7];
else if(sender==Week_nextBarBtn) // next button events
[offsetComponents setDay:7];
nextDate = [gregorian dateByAddingComponents:offsetComponents toDate:selectedDate options:0];
selectedDate = nextDate;
[selectedDate retain];
NSDateComponents *components = [gregorian components:NSWeekCalendarUnit fromDate:selectedDate];
NSInteger week = [components week];
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"MMMM YYYY"];
NSString *stringFromDate = [formatter stringFromDate:selectedDate];
[formatter release];
[Week_weekBarBtn setTitle:[NSString stringWithFormat:#"%#,Week %d",stringFromDate,week]];
}

Here's some code and it also checks an edge case where the beginning of the week starts in the prior month. You can get end of week by setting setWeekday to 7 and you can get the prior week by subtracting 1 from [components week]
// Finds the date for the first day of the week
- (NSDate *)getFirstDayOfTheWeekFromDate:(NSDate *)givenDate
{
NSCalendar *calendar = [NSCalendar currentCalendar];
// Edge case where beginning of week starts in the prior month
NSDateComponents *edgeCase = [[NSDateComponents alloc] init];
[edgeCase setMonth:2];
[edgeCase setDay:1];
[edgeCase setYear:2013];
NSDate *edgeCaseDate = [calendar dateFromComponents:edgeCase];
NSDateComponents *components = [calendar components:NSYearCalendarUnit|NSMonthCalendarUnit|NSWeekCalendarUnit|NSWeekdayCalendarUnit fromDate:edgeCaseDate];
[components setWeekday:1]; // 1 == Sunday, 7 == Saturday
[components setWeek:[components week]];
NSLog(#"Edge case date is %# and beginning of that week is %#", edgeCaseDate , [calendar dateFromComponents:components]);
// Find Sunday for the given date
components = [calendar components:NSYearCalendarUnit|NSMonthCalendarUnit|NSWeekCalendarUnit|NSWeekdayCalendarUnit fromDate:givenDate];
[components setWeekday:1]; // 1 == Sunday, 7 == Saturday
[components setWeek:[components week]];
NSLog(#"Original date is %# and beginning of week is %#", givenDate , [calendar dateFromComponents:components]);
return [calendar dateFromComponents:components];
}

Here is a solution for the swift4,
we can get all the days for the current week.
var calendar = Calendar(identifier: Calendar.Identifier.gregorian)
let today = calendar.startOfDay(for: Date())
let dayOfWeek = calendar.component(.weekday, from: today) - calendar.firstWeekday
let weekdays = calendar.range(of: .weekday, in: .weekOfYear, for: today)!
let days = (weekdays.lowerBound ..< weekdays.upperBound)
.flatMap { calendar.date(byAdding: .day, value: $0 - dayOfWeek, to: today) }

//Begining of Week Date
- (NSDate*) beginingOfWeekOfDate{
NSCalendar *tmpCalendar = [NSCalendar currentCalendar];
NSDateComponents *components = [tmpCalendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitWeekOfYear|NSCalendarUnitWeekday fromDate:self];//get the required calendar units
NSInteger weekday = tmpCalendar.firstWeekday;
components.weekday = weekday; //weekday
components.hour = 0;
components.minute = 0;
components.second = 0;
NSDate *fireDate = [tmpCalendar dateFromComponents:components];
return fireDate;
}
//End of Week Date
-(NSDate *)endOfWeekFromDate{
NSCalendar *tmpCalendar = [NSCalendar currentCalendar];
NSDateComponents *components = [tmpCalendar components:NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitWeekOfYear|NSCalendarUnitWeekday fromDate:self];//get the required calendar units
int weekday = 7; //Saturday
if (tmpCalendar.firstWeekday != 1) {
weekday = 1;
}
components.weekday = weekday;//weekday
components.hour = 23;
components.minute = 59;
components.second = 59;
NSDate *fireDate = [tmpCalendar dateFromComponents:components];
return fireDate;
}

You can get current day and date by following code:
NSDate *today = [NSDate date];
NSDateFormatter *dateFormat = [[[NSDateFormatter alloc] init] autorelease];
[dateFormat setDateFormat:#"EEEE"];
NSString *weekDay = [dateFormat stringFromDate:today];
[dateFormat setDateFormat:#"dd"];
NSString *thedate=[dateFormat stringFromDate:today];
//[dateFormat release];
NSLog(#"%# %#", weekDay,thedate);
Now, You need to put some logic in it to calculate starting date and end date of the week. The logic will be such kind,
if the week day is Monday
then
starting date = current date - 0
end date = current date + 6
and so on
I think you can get the idea of it.

By take advantage of the method rangeOfUnit:startDate:interval:forDate: of NSDate, there is a simpler way to achieve this:
- (void)startDate:(NSDate **)start andEndDate:(NSDate **)end ofWeekOn:(NSDate *)date{
NSDate *startDate = nil;
NSTimeInterval duration = 0;
BOOL b = [[NSCalendar currentCalendar] rangeOfUnit:NSWeekCalendarUnit startDate:&startDate interval:&duration forDate:date];
if(! b){
*start = nil;
*end = nil;
return;
}
NSDate *endDate = [startDate dateByAddingTimeInterval:duration-1];
*start = startDate;
*end = endDate;
}
NSDate *this_start = nil, *this_end = nil;
[self startDate:&this_start andEndDate:&this_end ofWeekOn:[NSDate date]];
So now you have the start date and end date of this week. Then last week:
NSDate *lastWeekDate = [this_start dateByAddingTimeInterval:-10];
NSDate *last_start = nil, *last_end = nil;
[self startDate:&last_start andEndDate:&last_end ofWeekOn:lastWeekDate];
Next week:
NSDate *nextWeekDate = [this_end dateByAddingTimeInterval:10];
NSDate *next_start = nil, *next_end = nil;
[self startDate:&next_start andEndDate:&next_end ofWeekOn:nextWeekDate];
Now you have them all.

Swift 3
First find the current date...
let today = Date()
let gregorian = Calendar(identifier: .gregorian)
var weekdayComponents: DateComponents? = gregorian.dateComponents([.weekday], from: today)
Sunday = 1, Monday = 2, Tuesday = 3, Wednesday = 4, Thursday = 5, Friday = 6 and Saturday = 7.
var componentsToSubtract = DateComponents()
componentsToSubtract.day = (0 - (weekdayComponents?.weekday!)!) + 2
beginningOfWeek = gregorian.date(byAdding: componentsToSubtract, to: today)
var componentsToAdd: DateComponents? = gregorian.dateComponents([.day], from: beginningOfWeek!)
componentsToAdd?.day = 6
endOfWeek = gregorian.date(byAdding: componentsToAdd!, to: beginningOfWeek!)
let components: DateComponents? = gregorian.dateComponents([.month], from: beginningOfWeek!)
let month: Int? = components?.month
let components1: DateComponents? = gregorian.dateComponents([.month], from: endOfWeek!)
let month1: Int? = components1?.month
print("\(month) - \(month1)")
showDate(start:beginningOfWeek!, end:endOfWeek!, strtMn:month!, endMn:month1!)
func showDate(start:Date, end:Date, strtMn:Int, endMn:Int) {
if strtMn == endMn{
let formatter = DateFormatter()
formatter.dateFormat = "MMM dd"
let stringFromDate: String = formatter.string(from: start)
let formatter1 = DateFormatter()
formatter1.dateFormat = "dd"
let stringFromDate1: String = formatter1.string(from: end)
print("\(stringFromDate) - \(stringFromDate1)")
lblDate.text = "\(stringFromDate) - \(stringFromDate1)"
}
else{
let formatter = DateFormatter()
formatter.dateFormat = "MMM dd"
let stringFromDate: String = formatter.string(from: start)
let formatter1 = DateFormatter()
formatter1.dateFormat = "MMM dd"
let stringFromDate1: String = formatter1.string(from: end)
print("\(stringFromDate) - \(stringFromDate1)")
lblDate.text = "\(stringFromDate) - \(stringFromDate1)"
}
}
for next and previous ....
#IBAction func week_CalendarActionEvents(_ sender: UIButton) {
let gregorian = Calendar(identifier: .gregorian)
var offsetComponents = DateComponents()
var nextStrtDate: Date?
var nextEndDate: Date?
var startDate: Date?
var endDate: Date?
startDate = beginningOfWeek
endDate = endOfWeek
if sender.tag == 1 {
offsetComponents.day = -7
}
else if sender.tag == 2 {
offsetComponents.day = 7
}
nextStrtDate = gregorian.date(byAdding: offsetComponents, to:startDate!)
startDate = nextStrtDate
beginningOfWeek = startDate
nextEndDate = gregorian.date(byAdding: offsetComponents, to: endDate!)
endDate = nextEndDate
endOfWeek = endDate
let components: DateComponents? = gregorian.dateComponents([.month], from: startDate!)
let month: Int? = components?.month
let components1: DateComponents? = gregorian.dateComponents([.month], from: endDate!)
let month1: Int? = components1?.month
print("\(month)- \(month1)")
showDate(start:startDate!, end:endDate!, strtMn:month!, endMn:month1!)
}

Swift 3+: Simple solution with extension
extension Date {
var startOfWeek: Date? {
let gregorian = Calendar(identifier: .gregorian)
guard let sunday = gregorian.date(from: gregorian.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self)) else { return nil }
return gregorian.date(byAdding: .day, value: 1, to: sunday)
}
var endOfWeek: Date? {
let gregorian = Calendar(identifier: .gregorian)
guard let sunday = gregorian.date(from: gregorian.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self)) else { return nil }
return gregorian.date(byAdding: .day, value: 7, to: sunday)
}
var yesterdayDate: Date? {
return NSCalendar.current.date(byAdding: .day, value: -1, to: noon)!
}
var tommorowDate: Date? {
return NSCalendar.current.date(byAdding: .day, value: 1, to: noon)!
}
var previousDate: Date? {
let oneDay:Double = 60 * 60 * 24
return self.addingTimeInterval(-(Double(oneDay)))
}
var nextDate: Date? {
let oneDay:Double = 60 * 60 * 24
return self.addingTimeInterval(oneDay)
}
var noon: Date {
return NSCalendar.current.date(bySettingHour: 12, minute: 0, second: 0, of: self)!
}
var month: Int {
return Calendar.current.component(.month, from: self)
}
}

Swift 4.2
If you want to show the current week dates, here is the code.
override func viewDidLoad() {
super.viewDidLoad()
for dateIndex in 0..<7 {
guard let startWeek = Date().startOfWeek else { return }
let date = Calendar.current.date(byAdding: .day, value: dateIndex + 1, to: startWeek)
print(date)
}
}
extension Date {
var startOfWeek: Date? {
let gregorian = Calendar(identifier: .gregorian)
guard let sunday = gregorian.date(from: gregorian.dateComponents([.yearForWeekOfYear, .weekOfYear], from: self)) else { return nil }
return gregorian.date(byAdding: .day, value: 1, to: sunday)
}
}

In swift 3.0
let cal = NSCalendar.current
//weekday
let weekday = cal.component(.weekday, from: Date())
var dateComp = cal.dateComponents([.hour, .minute, .second, .day, .month, .year], from: Date())
print(dateComp.day!)
//Start Date of the week - Sunday
dateComp.day = dateComp.day! - (weekday - 1)// start date of week
print(cal.date(from: dateComp)!)
//End Date of the Week - Saturday
dateComp = cal.dateComponents([.hour, .minute, .second, .day, .month, .year], from: Date())
dateComp.day = dateComp.day! + (7 - weekday)
print(cal.date(from: dateComp)!)

There are 3 steps to get Start date and End date of week from any region.
Find week day of current day.
func getTodayWeekDay() -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "EEEE"
let weekDay = dateFormatter.string(from: Date())
return weekDay
}
Make emun class for subtraction and addition of days.
i.e. if current week day is Wednesday then for getting Monday of current week we have to subtract -2 from current day.
And for getting Sunday of current week we have to add 4 into current day.
enum WeekDays: String {
case monday = "Monday"
case tuesday = "Tuesday"
case wednesday = "Wednesday"
case thursday = "Thursday"
case friday = "Friday"
case saturday = "Saturday"
case sunday = "Sunday"
var daysToSubstract: Int {
switch self {
case .monday: return 0
case .tuesday: return -1
case .wednesday: return -2
case .thursday: return -3
case .friday: return -4
case .saturday: return -5
case .sunday: return -6
}
}
var daysToAdd: Int {
switch self {
case .monday: return 6
case .tuesday: return 5
case .wednesday: return 4
case .thursday: return 3
case .friday: return 2
case .saturday: return 1
case .sunday: return 0
}
}
}
Get Start-week and End-week by subtraction and addition from current day.
extension Date {
var startOfWeek: String? {
let gregorian = Calendar(identifier: .gregorian)
let startWeek = dateFormatter.string(from:gregorian.date(byAdding: .day, value: WeekDays(rawValue: getTodayWeekDay())?.daysToSubstract ?? 0, to: self)!)
print("start-week--- \(startWeek)")
return startWeek
}
var endOfWeek: String? {
let gregorian = Calendar(identifier: .gregorian)
let endWeek = dateFormatter.string(from:gregorian.date(byAdding: .day, value: WeekDays(rawValue: getTodayWeekDay())?.daysToAdd ?? 6, to: self)!)
print("end-week--- \(endWeek)")
return endWeek
}
}
You can use functions like below:
Date().startOfWeek
Date().endOfWeek

Related

How to get all days in a month that are a specific weekday?

How do I get the actual date of the month based on a given a day ? For example, I would like to retrieve all the dates in June 2017 which are Saturday. How can I achieve that ? Sample code will be very much appreciated as I have struggled for days on this.
A DateComponents has a weekday property, representing the day of the week. The weekdays are (in Foundation's Gregorian calendar) numbered 1 for Sunday, 2 for Monday, …, and 7 for Saturday.
A DateComponents also has a weekdayOrdinal property, representing “the position of the weekday within the next larger calendar unit, such as the month. For example, 2 is the weekday ordinal unit for the second Friday of the month.”
So let's initialize a DateComponents for some Saturday in June 2017. It's generally a good idea to specify a time of noon if you don't care about the time, because midnight (the default time of day) can cause problems in some time zones on some days.
var components = DateComponents(era: 1, year: 2017, month: 06, hour: 12, weekday: 7)
And let's make a calendar.
var calendar = Calendar.autoupdatingCurrent
Now we can loop over all the possible weekday ordinals. For each, we'll ask the calendar to generate a date. Then we ask the calendar to convert the date back to year, month, and day components.
In the Gregorian calendar, some months have 5 Saturdays, but most have 4. So when we ask for the 5th Saturday, we'll probably get a date in the following month. When that happens, we want to suppress that date.
for i in 1 ... 5 {
components.weekdayOrdinal = i
let date = calendar.date(from: components)!
let ymd = calendar.dateComponents([.year, .month, .day], from: date)
guard ymd.month == components.month else { break }
print("\(ymd.year!)-\(ymd.month!)-\(ymd.day!)")
}
Output:
2017-6-3
2017-6-10
2017-6-17
2017-6-24
Objective-C version:
NSDateComponents *components = [NSDateComponents new];
components.era = 1;
components.year = 2017;
components.month = 6;
components.hour = 12;
components.weekday = 7;
NSCalendar *calendar = NSCalendar.autoupdatingCurrentCalendar;
for (NSInteger i = 1; i <= 5; ++i) {
components.weekdayOrdinal = i;
NSDate *date = [calendar dateFromComponents:components];
NSDateComponents *ymd = [calendar components:NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay fromDate:date];
if (ymd.month != components.month) { break; }
NSLog(#"%ld-%ld-%ld", (long)ymd.year, (long)ymd.month, (long)ymd.day);
}
This is another solution for your problem using calendar method called enumerateDates and using a Date extension
//month in MM format, year in yyyy format and dayNumber as Int 1 for sunday, 7 for saturday
func datesWith(dayNumber:Int,month:String,year:String) -> [Date]
{
assert(dayNumber >= 1 && dayNumber <= 7, "Day number is wrong")
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let date = dateFormatter.date(from: year + "-" + month + "-" + "01")
guard date != nil else {
return []
}
var resultDates : [Date] = []
//check if firstDay of month is desired weekday
if(Calendar.current.component(.weekday, from: date!) == dayNumber)
{
resultDates.append(date!)
}
Calendar.current.enumerateDates(startingAfter: date!, matching: DateComponents(weekday: dayNumber), matchingPolicy: Calendar.MatchingPolicy.nextTimePreservingSmallerComponents) { (currentDate, result, stop) in
if(currentDate! > date!.endOfMonth())
{
stop = true
return
}
resultDates.append(currentDate!)
}
return resultDates
}
Extension
extension Date {
func startOfMonth() -> Date {
return Calendar.current.date(from: Calendar.current.dateComponents([.year, .month], from: Calendar.current.startOfDay(for: self)))!
}
func endOfMonth() -> Date {
return Calendar.current.date(byAdding: DateComponents(month: 1, day: -1), to: self.startOfMonth())!
}
}
Using it
override func viewDidLoad() {
super.viewDidLoad()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
// Do any additional setup after loading the view, typically from a nib.
let datesArray = self.datesWith(dayNumber: 5, month: "06", year: "2017")
for currDate in datesArray {
debugPrint(dateFormatter.string(from: currDate))
}
}
Output
"2017-06-01"
"2017-06-08"
"2017-06-15"
"2017-06-22"
"2017-06-29"
Hope this helps

How to get dates for every thursday or other day of week in specific month?

I want to get date of particular day for every week.
Suppose I have a date: 2017-04-13. It is an April, and 13 April is Thursday. I need to get every date in April which is Thursday.
How can I do this?
The output should be: 2017-04-06, 2017-04-13, 2017-04-20, 2017-04-27
Short solution:
// Get current calendar and current date
let calendar = Calendar.current
let now = Date()
// Get the current date components for year, month, weekday and weekday ordinal
var components = calendar.dateComponents([.year, .month, .weekdayOrdinal, .weekday], from: now)
// get the range (number of occurrences) of the particular weekday in the month
let range = calendar.range(of: .weekdayOrdinal, in: .month, for: now)!
// Loop thru the range, set the components to the appropriate weekday ordinal and get the date
for ordinal in range.lowerBound..
Be aware that print prints dates always in UTC.
Edit:
range(of: .weekdayOrdinal, in: .month does not work, it returns 1..<6 regardless of the date.
This is a working alternative. It checks if the date exceeds the month bounds
// Get current calendar and date for 2017/4/13
let calendar = Calendar.current
let april13Components = DateComponents(year:2017, month:4, day:13)
let april13Date = calendar.date(from: april13Components)!
// Get the current date components for year, month, weekday and weekday ordinal
var components = calendar.dateComponents([.year, .month, .weekdayOrdinal, .weekday], from: april13Date)
// Loop thru the range, set the components to the appropriate weekday ordinal and get the date
for ordinal in 1..<6 { // maximum 5 occurrences
components.weekdayOrdinal = ordinal
let date = calendar.date(from: components)!
if calendar.component(.month, from: date) != components.month! { break }
print(calendar.date(from: components)!)
}
Try this playground:
import UIKit
let dateString = "2017-04-13"
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let referenceDate = dateFormatter.date(from: dateString)!
let calendar = Calendar.current
let firstDayComponents = calendar.dateComponents([.year, .month], from: referenceDate)
let monthFirst = calendar.date(from: firstDayComponents)!
let weekDay = calendar.component(.weekday, from: referenceDate)
var oneDay = DateComponents()
oneDay.day = 1
var checkDate = monthFirst
while calendar.component(.month, from: checkDate) == calendar.component(.month, from: referenceDate) {
if calendar.component(.weekday, from: checkDate) == weekDay {
let thisDay = dateFormatter.string(from: checkDate)
print(thisDay)
}
checkDate = calendar.date(byAdding: oneDay, to: checkDate)!
}
This code does the job. I added some logs to understand some logic behind it.
You can set dateInit as you wish, the rest of the code will find all the days that have the same weekday in the same year of the same month.
I printed two versions of date representations (NSDate objects and NSString objects), for the one having issue with timezones and "it's not the same day" cries.
It uses enumerateDatesStartingAfterDate:matchingComponents:options:usingBlock:
NSCalendar *calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian];
NSString *dateStr = #"2017-04-13";
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyy-MM-dd"];
NSDate *dateInit = [dateFormatter dateFromString:dateStr];
NSLog(#"dateInit: %#", dateInit);
NSDateComponents *componentsToMatch = [calendar components:(NSCalendarUnitMonth|NSCalendarUnitWeekday) fromDate:dateInit];
NSDate *startOfMonth = [calendar dateFromComponents:[calendar components:(NSCalendarUnitYear|NSCalendarUnitMonth) fromDate:dateInit]];
NSLog(#"StartOfTheMonth: %#", startOfMonth);
NSArray *daysToFind = #[#"2017-04-06", #"2017-04-13", #"2017-04-20", #"2017-04-27"]; //According to author
NSLog(#"DaysToFind: %#", daysToFind);
NSMutableArray *allDaysInMonthMatchingWeekDay = [[NSMutableArray alloc] init];
[calendar enumerateDatesStartingAfterDate:startOfMonth
matchingComponents:componentsToMatch
options:NSCalendarMatchStrictly
usingBlock:^(NSDate * _Nullable date, BOOL exactMatch, BOOL * _Nonnull stop) {
NSLog(#"DateBlock: %#", date);
[allDaysInMonthMatchingWeekDay addObject:date];
}];
NSLog(#"allDaysInMonthMatchingWeekDay: %#",allDaysInMonthMatchingWeekDay);
for (NSDate *aDate in allDaysInMonthMatchingWeekDay)
{
NSLog(#"Found: %#", [dateFormatter stringFromDate:aDate]);
}
The logs:
$>dateInit: 2017-04-12 22:00:00 +0000
$>StartOfTheMonth: 2017-03-31 22:00:00 +0000
$>DaysToFind: (
"2017-04-06",
"2017-04-13",
"2017-04-20",
"2017-04-27"
)
$>DateBlock: 2017-04-05 22:00:00 +0000
$>DateBlock: 2017-04-12 22:00:00 +0000
$>DateBlock: 2017-04-19 22:00:00 +0000
$>DateBlock: 2017-04-26 22:00:00 +0000
$>allDaysInMonthMatchingWeekDay: (
"2017-04-05 22:00:00 +0000",
"2017-04-12 22:00:00 +0000",
"2017-04-19 22:00:00 +0000",
"2017-04-26 22:00:00 +0000"
)
$>Found: 2017-04-06
$>Found: 2017-04-13
$>Found: 2017-04-20
$>Found: 2017-04-27
Note: For the componentsToMatch, I tried to set the Year/Month/WeekDay flags unit, but the enumeration stopped at the first occurence, didn't search long why, I came up with only month and weekday flag to get it work. Maybe some little issue that I missed.
EDIT:
In Swift 3 (it works, but since I'm an Objective-C developer and not a Swift one, it may have issues, like wrapping/unwrapping etc)
let calendar = NSCalendar.init(calendarIdentifier: .gregorian)
let dateStr = "2017-04-13"
let dateFormatter = DateFormatter.init()
dateFormatter.dateFormat = "yyyy-MM-dd"
let dateInit = dateFormatter.date(from: dateStr)!
print("dateInit: \(dateInit)")
let componentsToMatch = calendar?.components([.month,.weekday], from: dateInit)
let startOfMonth = calendar?.date(from: (calendar?.components([.year,.month], from: dateInit))!)
print("StartOfTheMonth:\(startOfMonth)")
calendar?.enumerateDates(startingAfter: startOfMonth!, matching: componentsToMatch!, options: .matchStrictly, using: { (date, extactMatch, stop) in
print("DateBlock: \(date)")
})
I would write an extension for Calendar for any given time span and use an enum to name the weekdays
enum WeekDay: Int {
case sunday = 1
case monday
case tuesday
case wednesday
case thursday
case friday
case saturday
}
struct TimeSpan {
let startDate: Date
let endDate: Date
}
extension Calendar {
func allOccurrenceOf(day: WeekDay, in timeSpan:TimeSpan) -> [Date] {
let startDateWeekDay = Int(self.component(.weekday, from: timeSpan.startDate))
let desiredDay = day.rawValue
let offset = (desiredDay - startDateWeekDay + 7) % 7
let firstOccurrence = self.startOfDay(for:self.date(byAdding: DateComponents(day:offset), to: timeSpan.startDate)!)
guard firstOccurrence.timeIntervalSince1970 < timeSpan.endDate.timeIntervalSince1970 else {
return []
}
var filtered = [firstOccurrence]
while true {
let nextDate = self.date(byAdding: DateComponents(day: 7), to: filtered.last!)!
if nextDate < timeSpan.endDate {
filtered.append(nextDate)
break
}
}
return filtered
}
}
Beware that I hacked this could rather fast. I am sure that this can be expressed swiftier. In real production code I would also try to eliminate all ! from it.
usage:
let tuesdays = Calendar.autoupdatingCurrent.allOccurrenceOf(day: .tuesday, in: TimeSpan(startDate: Date(), endDate: Calendar.autoupdatingCurrent.date(byAdding: DateComponents(month:1), to: Date())!))
As Suggested in comment. see updated code. updated with week day
func getNumberOfDaysInMonth (month : Int , Year : Int, weekday: Int) -> [String]{
let dateComponents = NSDateComponents()
dateComponents.year = Year
dateComponents.month = month
let calendar = Calendar.current
let date = calendar.date(from: dateComponents as DateComponents)
let range = calendar.range(of: .day, in: .month, for: date!)
let numDays:Int = (range?.upperBound)!
let thuFormatter = DateFormatter()
var dateArray:[String] = [String]()
thuFormatter.dateFormat = "yyyy-MM-dd"
for day in 1...numDays {
dateComponents.day = day
let date2 = calendar.date(from: dateComponents as DateComponents)
print(calendar.component(.weekday, from: date2!))
if calendar.component(.weekday, from: date2!) == weekday
{
let dateThu = thuFormatter.string(from: date2!)
dateArray.append(dateThu)
}
}
return dateArray
}
and then call it like
let myThu:[String] = getNumberOfDaysInMonth(month: 4, Year: 2017,weekday: 3)
print(myThu)

how to get NSDate of a specific next day and time

I have some events for which I need to calculate NSDates.
For example I'm trying to get the next Monday at 8:00 AM.
So I tried some stuff but nothing works:
1.
let nextMonday = NSCalendar.currentCalendar().dateBySettingUnit(NSCalendarUnit.Weekday, value: 2, ofDate: startDate, options: NSCalendarOptions.MatchNextTime)
let nextMondayEight = NSCalendar.currentCalendar().dateBySettingUnit(NSCalendarUnit.Hour, value: 8, ofDate: nextMonday!, options: NSCalendarOptions.MatchNextTime)
I get:
2016-04-12 05:00:00 +0000
That's Tuesday at 8:00 (the time difference is my local time GMT -3).
2.
let unitFlags: NSCalendarUnit = [.Day, .Month, .Year]
let comp = NSCalendar.currentCalendar().components(unitFlags, fromDate: NSDate())
comp.timeZone = NSTimeZone.localTimeZone()
comp.weekday = 1
comp.hour = 8
comp.minute = 0
comp.second = 0
let compDate = NSCalendar.currentCalendar().dateFromComponents(comp)
print("time: \(compDate!)")
I get:
2016-04-11 05:00:00 +0000
That's today at 8:00 and not next Monday at 8:00.
Any suggestions?
Thanks
NSCalendar has a method nextDateAfterDate:matchingComponents:options for this kind of date math.
let calendar = NSCalendar.currentCalendar()
let components = NSDateComponents()
components.hour = 8 // 8:00
components.weekday = 2 // Monday in Gregorian Calendar
let nextMondayEightOClock = calendar.nextDateAfterDate(NSDate(), matchingComponents: components, options: .MatchStrictly)

Iterating over a year to get each days NSDate object in Swift

Hello I have a method that returns an array of times for each day.
prayTimesDate(date: NSDate, latitide : Double, longitude : Double, timeZone : Double) -> NSMutableArray
I need to iterate through a whole year or maybe a date range to get an array of times for each day in a whole year. I found alot of references in ruby and python on how to do this but I couldn't find anything for swift or objective-c. Is there any built in methods in swift that will accomplish this? If not can someone help me out as I am still new in programming. Any input is greatly appreciated.
This is the objective-c code for the method I'm linking to my swift project
- (NSMutableArray *)prayerTimesDate:(NSDate *)date latitude:(double)latitude longitude:(double)longitude andTimezone:(double)timezone
{
unsigned unitFlags = NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay;
NSCalendar *calendar = [NSCalendar currentCalendar];
NSDateComponents *components = [calendar components:unitFlags fromDate:date];
NSInteger year = [components year];
NSInteger month = [components month];
NSInteger day = [components day];
return [self getDatePrayerTimesForYear:year month:month day:day latitude:latitude longitude:longitude andtimeZone:timezone];
}
Assuming your prayerTimesDate: method is already returning the expected result, you can loop through each day of the year while repeatedly call prayerTimesDate: to get an array containing the prayer times for each day, ex:
func yearlyPrayerDatesFromCurrentDate (latitude:Double, longitude:Double, timezone:Double) -> NSMutableArray {
// Set "date" to equal the current day
var date:NSDate! = NSDate()
// Increment "date" by one year to calculate the ending
// date for the loop
let gregorian:NSCalendar! = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)
let dateComponents = NSDateComponents()
dateComponents.year = 1
let endingDate:NSDate! = gregorian.dateByAddingComponents(dateComponents, toDate: date, options: nil)
// Create an array to hold *all* the returned
// results for the year
var datesArray = NSMutableArray()
// Loop through each date until the ending date is
// reached
while date.compare(endingDate) != NSComparisonResult.OrderedDescending {
// Call your prayerTimesDate: method on the current
// date to get that date's prayer times and add the
// times from the returned array to the datesArray
datesArray.addObjectsFromArray(prayerTimesDate(date, latitude: latitude, longitude: longitude, andTimezone: timezone))
// increment the date by 1 day
let dateComponents = NSDateComponents()
dateComponents.day = 1
date = gregorian.dateByAddingComponents(dateComponents, toDate: date, options: nil)
}
return datesArray
}
Here is another example for a period over 14 days (without NSCalendar):
let ti:NSTimeInterval = 24*60*60 //one day
let dateFrom = NSDate() //Now
let dateTo = dateFrom.dateByAddingTimeInterval(24*60*60*14) //14 Days later
var nextDate = NSDate()
var endDate = dateTo.dateByAddingTimeInterval(ti)
while nextDate.compare(endDate) == NSComparisonResult.OrderedAscending
{
print("nextDate:", nextDate)
nextDate = nextDate.dateByAddingTimeInterval(ti)
}
Create an NSDateComponents instance for 1 day and NSDate objects for each time on the first day. Now you can iterate over the number of days you want (or until you hit then end date) and then you can use dateByAddingComponents:toDate:options: of the calendar to get the new date for each day.
From Apple doc: To compute a sequence of dates, use the enumerateDatesStartingAfterDate:matchingComponents:options:usingBlock: method instead of calling this method ( - nextDateAfterDate:matchingComponents:options: ) in a loop with the previous loop iteration's result.
As I got, it will iterate all dates that matched with "matchingComponents" till you finish iteration with "stop.memory = true"
//: Playground - noun: a place where people can play
import UIKit
let calendar = NSCalendar.currentCalendar()
let startDate = calendar.startOfDayForDate(NSDate())
let finishDate = calendar.dateByAddingUnit(.Day, value: 10, toDate: startDate, options: [])
let dayComponent = NSDateComponents()
dayComponent.hour = 1
calendar.enumerateDatesStartingAfterDate(startDate, matchingComponents: dayComponent, options: [.MatchStrictly]) { (date, exactMatch, stop) in
print(date)
if date!.compare(finishDate!) == NSComparisonResult.OrderedDescending {
// .memory gets at the value of an UnsafeMutablePointer
stop.memory = true
}
}

How to convert NSDate in to relative format as "Today","Yesterday","a week ago","a month ago","a year ago"?

I want to convert nsdate in to relative format like "Today","Yesterday","a week ago","a month ago","a year ago","date as it is".
I have written following method for it.. but some how its just printing as it is date.. can you please tell me what should be the problem?
//Following is my function which converts the date into relative string
+(NSString *)getDateDiffrence:(NSDate *)strDate{
NSDateFormatter *df = [[NSDateFormatter alloc] init];
df.timeStyle = NSDateFormatterMediumStyle;
df.dateStyle = NSDateFormatterShortStyle;
df.doesRelativeDateFormatting = YES;
NSLog(#"STRING DATEEE : %# REAL DATE TODAY %#",[df stringFromDate:strDate],[NSDate date]);
return [df stringFromDate:strDate];
}
I have date string with the following format "2013-10-29T09:38:00"
When I tried to give the NSDate object then its always return me null date.
so I tried to convert that date in to yyyy-MM-dd HH:mm:ssZZZZ then I pass this date to function then it's just printing whole date..
How to solve this problem?
//Following is the code I call the above function
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"yyyy-MM-dd'T'HH:mm:ss"];
NSDate *date = [formatter dateFromString:[threadDict objectForKey:#"lastMessageDate"]];
[formatter setDateFormat:#"yyyy-MM-dd HH:mm:ssZZZZ"];
NSString *date1 = [formatter stringFromDate:date];
NSDate *date_d = [formatter dateFromString:date1];
NSString *resultstr=[UserManager getDateDiffrence:date];
self.dateLabel.text=resultstr;
For simplicity I'm assuming that the dates you are formatting are all in the past (no "tomorrow" or "next week"). It's not that it can't be done but it would be more cases to deal with and more strings to return.
You can use components:fromDate:toDate:options: with whatever combination of date components you are looking for to get the number of years, months, weeks, days, hours, etc. between two dates. By then going though them in order from most significant (e.g. year) to least significant (e.g. day), you can format a string based only on the most significant component.
For example: a date that is 1 week, 2 days and 7 hours ago would be formatted as "1 week".
If you want to create special strings for a special number of a unit, like "tomorrow" for "1 day ago" then you can check the value of that component after you have determined that it is the most significant component.
The code would look something like this:
- (NSString *)relativeDateStringForDate:(NSDate *)date
{
NSCalendarUnit units = NSCalendarUnitDay | NSCalendarUnitWeekOfYear |
NSCalendarUnitMonth | NSCalendarUnitYear;
// if `date` is before "now" (i.e. in the past) then the components will be positive
NSDateComponents *components = [[NSCalendar currentCalendar] components:units
fromDate:date
toDate:[NSDate date]
options:0];
if (components.year > 0) {
return [NSString stringWithFormat:#"%ld years ago", (long)components.year];
} else if (components.month > 0) {
return [NSString stringWithFormat:#"%ld months ago", (long)components.month];
} else if (components.weekOfYear > 0) {
return [NSString stringWithFormat:#"%ld weeks ago", (long)components.weekOfYear];
} else if (components.day > 0) {
if (components.day > 1) {
return [NSString stringWithFormat:#"%ld days ago", (long)components.day];
} else {
return #"Yesterday";
}
} else {
return #"Today";
}
}
If your dates could also be in the future then you can check the absolute value of the components in the same order and then check if it's positive or negative to return the appropriate strings. I'me only showing the year below:
if ( abs(components.year > 0) ) {
// year is most significant component
if (components.year > 0) {
// in the past
return [NSString stringWithFormat:#"%ld years ago", (long)components.year];
} else {
// in the future
return [NSString stringWithFormat:#"In %ld years", (long)components.year];
}
}
Please note that as of iOS 13 there is now RelativeDateTimeFormatter which does it all most of it for you! WWDC 2019 video here.
let formatter = RelativeDateTimeFormatter()
let dateString = formatter.localizedString(for: aDate, relativeTo: now)
// en_US: "2 weeks ago"
// es_ES: "hace 2 semanas"
// zh_TW: "2 週前"
I've left my previous answer below for posterity. Cheers!
⚠️ You will want to read through the previous answer for some key tips to avoid certain bugs. Hint: use the end of the current day's date/time for the relative date when comparing dates that are not today!
Here's my answer (in Swift 3!) and why it's better.
Answer:
func datePhraseRelativeToToday(from date: Date) -> String {
// Don't use the current date/time. Use the end of the current day
// (technically 0h00 the next day). Apple's calculation of
// doesRelativeDateFormatting niavely depends on this start date.
guard let todayEnd = dateEndOfToday() else {
return ""
}
let calendar = Calendar.autoupdatingCurrent
let units = Set([Calendar.Component.year,
Calendar.Component.month,
Calendar.Component.weekOfMonth,
Calendar.Component.day])
let difference = calendar.dateComponents(units, from: date, to: todayEnd)
guard let year = difference.year,
let month = difference.month,
let week = difference.weekOfMonth,
let day = difference.day else {
return ""
}
let timeAgo = NSLocalizedString("%# ago", comment: "x days ago")
let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.locale = Locale.autoupdatingCurrent
formatter.dateStyle = .medium
formatter.doesRelativeDateFormatting = true
return formatter
}()
if year > 0 {
// sample output: "Jan 23, 2014"
return dateFormatter.string(from: date)
} else if month > 0 {
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .brief // sample output: "1mth"
formatter.allowedUnits = .month
guard let timePhrase = formatter.string(from: difference) else {
return ""
}
return String(format: timeAgo, timePhrase)
} else if week > 0 {
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .brief; // sample output: "2wks"
formatter.allowedUnits = .weekOfMonth
guard let timePhrase = formatter.string(from: difference) else {
return ""
}
return String(format: timeAgo, timePhrase)
} else if day > 1 {
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .abbreviated; // sample output: "3d"
formatter.allowedUnits = .day
guard let timePhrase = formatter.string(from: difference) else {
return ""
}
return String(format: timeAgo, timePhrase)
} else {
// sample output: "Yesterday" or "Today"
return dateFormatter.string(from: date)
}
}
func dateEndOfToday() -> Date? {
let calendar = Calendar.autoupdatingCurrent
let now = Date()
let todayStart = calendar.startOfDay(for: now)
var components = DateComponents()
components.day = 1
let todayEnd = calendar.date(byAdding: components, to: todayStart)
return todayEnd
}
Remember to reuse your formatters to avoid any performance hit! Hint: extensions on DateFormatter and DateComponentsFormatter are good ideas.
Why it's better:
Utilizes DateFormatter's "Yesterday" and "Today". This is already translated by Apple, which saves you work!
Uses DateComponentsFormatter's already translated "1 week" string. (Again less work for you, courtesy of Apple.) All you have to do is translate the "%# ago" string. 🙂
The other answers incorrectly calculate the time when the day switches from "today" to "yesterday" to etc. Fixed constants are a big NO-NO because reasons. Also, the other answers use the current date/time when they should use the end of the current day's date/time.
Uses autoupdatingCurrent for Calendar & Locale which ensures your app is immediately up to date with the user's calendar and language preferences in Settings.app
This answer was inspired by DateTools on GitHub.
Swift update, thanks to objective-c answer of David Rönnqvist, it will work for the past dates.
func relativeDateStringForDate(date : NSDate) -> NSString {
let todayDate = NSDate()
let units: NSCalendarUnit = [.Hour, .Day, .Month, .Year, .WeekOfYear]
let components = NSCalendar.currentCalendar().components(units, fromDate: date , toDate: todayDate, options: NSCalendarOptions.MatchFirst )
let year = components.year
let month = components.month
let day = components.day
let hour = components.hour
let weeks = components.weekOfYear
// if `date` is before "now" (i.e. in the past) then the components will be positive
if components.year > 0 {
return NSString.init(format: "%d years ago", year);
} else if components.month > 0 {
return NSString.init(format: "%d months ago", month);
} else if components.weekOfYear > 0 {
return NSString.init(format: "%d weeks ago", weeks);
} else if (components.day > 0) {
if components.day > 1 {
return NSString.init(format: "%d days ago", day);
} else {
return "Yesterday";
}
} else {
return NSString.init(format: "%d hours ago", hour);
}
}
FOR: SWIFT 3
Here's a Swift 3 version, for past dates, that handles all units and singular or plural in the returned String.
Example Use:
let oneWeekAgo = Calendar.current.date(byAdding: .weekOfYear, value: -1, to: Date())!
print(relativePast(for: oneWeekAgo)) // output: "1 week ago"
I based it on a riff off of Saurabh Yadav's. Thanks.
func relativePast(for date : Date) -> String {
let units = Set<Calendar.Component>([.year, .month, .day, .hour, .minute, .second, .weekOfYear])
let components = Calendar.current.dateComponents(units, from: date, to: Date())
if components.year! > 0 {
return "\(components.year!) " + (components.year! > 1 ? "years ago" : "year ago")
} else if components.month! > 0 {
return "\(components.month!) " + (components.month! > 1 ? "months ago" : "month ago")
} else if components.weekOfYear! > 0 {
return "\(components.weekOfYear!) " + (components.weekOfYear! > 1 ? "weeks ago" : "week ago")
} else if (components.day! > 0) {
return (components.day! > 1 ? "\(components.day!) days ago" : "Yesterday")
} else if components.hour! > 0 {
return "\(components.hour!) " + (components.hour! > 1 ? "hours ago" : "hour ago")
} else if components.minute! > 0 {
return "\(components.minute!) " + (components.minute! > 1 ? "minutes ago" : "minute ago")
} else {
return "\(components.second!) " + (components.second! > 1 ? "seconds ago" : "second ago")
}
}
To avoid the 24-hour problem mentioned by Budidino to David's answer, I altered it to like this below -
- (NSString *)relativeDateStringForDate:(NSDate *)date
{
NSCalendarUnit units = NSDayCalendarUnit | NSWeekOfYearCalendarUnit |
NSMonthCalendarUnit | NSYearCalendarUnit ;
NSCalendar *cal = [NSCalendar currentCalendar];
NSDateComponents *components1 = [cal components:(NSCalendarUnitEra|NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay) fromDate:[NSDate date]];
NSDate *today = [cal dateFromComponents:components1];
components1 = [cal components:(NSCalendarUnitEra|NSCalendarUnitYear|NSCalendarUnitMonth|NSCalendarUnitDay) fromDate:date];
NSDate *thatdate = [cal dateFromComponents:components1];
// if `date` is before "now" (i.e. in the past) then the components will be positive
NSDateComponents *components = [[NSCalendar currentCalendar] components:units
fromDate:thatdate
toDate:today
options:0];
if (components.year > 0) {
return [NSString stringWithFormat:#"%ld years ago", (long)components.year];
} else if (components.month > 0) {
return [NSString stringWithFormat:#"%ld months ago", (long)components.month];
} else if (components.weekOfYear > 0) {
return [NSString stringWithFormat:#"%ld weeks ago", (long)components.weekOfYear];
} else if (components.day > 0) {
if (components.day > 1) {
return [NSString stringWithFormat:#"%ld days ago", (long)components.day];
} else {
return #"Yesterday";
}
} else {
return #"Today";
}
}
Basically, it creates 2 new dates without time pieces included.Then the comparison is done for "days" difference.
check NSDate-TimeAgo, it also supports multiple languages.
You will need to work out this logic yourself. You will need to determine the number of days in between those two dates.
Here is a relatively naive approach:
+ (NSString *) dateDifference:(NSDate *)date
{
const NSTimeInterval secondsPerDay = 60 * 60 * 24;
NSTimeInterval diff = [date timeIntervalSinceNow] * -1.0;
// if the difference is negative, then the given date/time is in the future
// (because we multiplied by -1.0 to make it easier to follow later)
if (diff < 0)
return #"In the future";
diff /= secondsPerDay; // get the number of days
// if the difference is less than 1, the date occurred today, etc.
if (diff < 1)
return #"Today";
else if (diff < 2)
return #"Yesterday";
else if (diff < 8)
return #"Last week";
else
return [date description]; // use a date formatter if necessary
}
It is naive for a number of reasons:
It doesn't take into account leap days
It assumes there are 86400 seconds in a day (there is such a thing as leap seconds!)
However, this should at least help you head in the right direction. Also, avoid using get in method names. Using get in a method name typically indicates that the caller must provide their own output buffer. Consider NSArray's method, getItems:range:, and NSString's method, getCharacters:range:.
NSString* AgoStringFromTime(NSDate* dateTime)
{
NSDictionary *timeScale = #{#"sec" :#1,
#"min" :#60,
#"hr" :#3600,
#"day" :#86400,
#"week" :#605800,
#"month":#2629743,
#"year" :#31556926};
NSString *scale;
int timeAgo = 0-(int)[dateTime timeIntervalSinceNow];
if (timeAgo < 60) {
scale = #"sec";
} else if (timeAgo < 3600) {
scale = #"min";
} else if (timeAgo < 86400) {
scale = #"hr";
} else if (timeAgo < 605800) {
scale = #"day";
} else if (timeAgo < 2629743) {
scale = #"week";
} else if (timeAgo < 31556926) {
scale = #"month";
} else {
scale = #"year";
}
timeAgo = timeAgo/[[timeScale objectForKey:scale] integerValue];
NSString *s = #"";
if (timeAgo > 1) {
s = #"s";
}
return [NSString stringWithFormat:#"%d %#%#", timeAgo, scale, s];
}
Here is code I created for my use:
+ (NSString*) getTimestampForDate:(NSDate*)date {
NSDate* sourceDate = date;
// Timezone Offset compensation (optional, if your target users are limited to a single time zone.)
NSTimeZone* sourceTimeZone = [NSTimeZone timeZoneWithName:#"America/New_York"];
NSTimeZone* destinationTimeZone = [NSTimeZone systemTimeZone];
NSInteger sourceGMTOffset = [sourceTimeZone secondsFromGMTForDate:sourceDate];
NSInteger destinationGMTOffset = [destinationTimeZone secondsFromGMTForDate:sourceDate];
NSTimeInterval interval = destinationGMTOffset - sourceGMTOffset;
NSDate* destinationDate = [[NSDate alloc] initWithTimeInterval:interval sinceDate:sourceDate];
// Timestamp calculation (based on compensation)
NSCalendar* currentCalendar = [NSCalendar currentCalendar];
NSCalendarUnit unitFlags = NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit | NSHourCalendarUnit | NSMinuteCalendarUnit;
NSDateComponents *differenceComponents = [currentCalendar components:unitFlags fromDate:destinationDate toDate:[NSDate date] options:0];//Use `date` instead of `destinationDate` if you are not using Timezone offset correction
NSInteger yearDifference = [differenceComponents year];
NSInteger monthDifference = [differenceComponents month];
NSInteger dayDifference = [differenceComponents day];
NSInteger hourDifference = [differenceComponents hour];
NSInteger minuteDifference = [differenceComponents minute];
NSString* timestamp;
if (yearDifference == 0
&& monthDifference == 0
&& dayDifference == 0
&& hourDifference == 0
&& minuteDifference <= 2) {
//"Just Now"
timestamp = #"Just Now";
} else if (yearDifference == 0
&& monthDifference == 0
&& dayDifference == 0
&& hourDifference == 0
&& minuteDifference < 60) {
//"13 minutes ago"
timestamp = [NSString stringWithFormat:#"%ld minutes ago", (long)minuteDifference];
} else if (yearDifference == 0
&& monthDifference == 0
&& dayDifference == 0
&& hourDifference == 1) {
//"1 hour ago" EXACT
timestamp = #"1 hour ago";
} else if (yearDifference == 0
&& monthDifference == 0
&& dayDifference == 0
&& hourDifference < 24) {
timestamp = [NSString stringWithFormat:#"%ld hours ago", (long)hourDifference];
} else {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setLocale:[NSLocale currentLocale]];
NSString* strDate, *strDate2 = #"";
if (yearDifference == 0
&& monthDifference == 0
&& dayDifference == 1) {
//"Yesterday at 10:23 AM", "Yesterday at 5:08 PM"
[formatter setDateFormat:#"hh:mm a"];
strDate = [formatter stringFromDate:date];
timestamp = [NSString stringWithFormat:#"Yesterday at %#", strDate];
} else if (yearDifference == 0
&& monthDifference == 0
&& dayDifference < 7) {
//"Tuesday at 7:13 PM"
[formatter setDateFormat:#"EEEE"];
strDate = [formatter stringFromDate:date];
[formatter setDateFormat:#"hh:mm a"];
strDate2 = [formatter stringFromDate:date];
timestamp = [NSString stringWithFormat:#"%# at %#", strDate, strDate2];
} else if (yearDifference == 0) {
//"July 4 at 7:36 AM"
[formatter setDateFormat:#"MMMM d"];
strDate = [formatter stringFromDate:date];
[formatter setDateFormat:#"hh:mm a"];
strDate2 = [formatter stringFromDate:date];
timestamp = [NSString stringWithFormat:#"%# at %#", strDate, strDate2];
} else {
//"March 24 2010 at 4:50 AM"
[formatter setDateFormat:#"d MMMM yyyy"];
strDate = [formatter stringFromDate:date];
[formatter setDateFormat:#"hh:mm a"];
strDate2 = [formatter stringFromDate:date];
timestamp = [NSString stringWithFormat:#"%# at %#", strDate, strDate2];
}
}
return timestamp;
}
This is just a copy of a previous answer but it returns Just now if it is less than five seconds.
func relativePast(for date : Date) -> String {
let units = Set<Calendar.Component>([.year, .month, .day, .hour, .minute, .second, .weekOfYear])
let components = Calendar.current.dateComponents(units, from: date, to: Date())
if components.year! > 0 {
return "\(components.year!) " + (components.year! > 1 ? "years ago" : "year ago")
} else if components.month! > 0 {
return "\(components.month!) " + (components.month! > 1 ? "months ago" : "month ago")
} else if components.weekOfYear! > 0 {
return "\(components.weekOfYear!) " + (components.weekOfYear! > 1 ? "weeks ago" : "week ago")
} else if (components.day! > 0) {
return (components.day! > 1 ? "\(components.day!) days ago" : "Yesterday")
} else if components.hour! > 0 {
return "\(components.hour!) " + (components.hour! > 1 ? "hours ago" : "hour ago")
} else if components.minute! > 0 {
return "\(components.minute!) " + (components.minute! > 1 ? "minutes ago" : "minute ago")
} else {
return "\(components.second!) " + (components.second! > 5 ? "seconds ago" : "Just Now".replacingOccurrences(of: "0", with: "")
}
}
The problem with doesRelativeDateFormatting is that it's pretty much limited to Yesterday, Today, Tomorrow. If you're looking for something more thorough, then take a look at the answers here.
Complate Code If Futures Dates
NSCalendarUnit units = NSCalendarUnitDay | NSCalendarUnitWeekOfYear |
NSCalendarUnitMonth | NSCalendarUnitYear;
NSDateComponents *components = [[NSCalendar currentCalendar] components:units fromDate:date toDate:[NSDate date] options:0];
if (components.year < 0) {
return [NSString stringWithFormat:#"%ld years from now", labs((long)components.year)];
} else if (components.month < 0) {
return [NSString stringWithFormat:#"%ld months from now", labs((long)components.month)];
} else if (components.weekOfYear < 0) {
return [NSString stringWithFormat:#"%ld weeks from now", labs((long)components.weekOfYear)];
} else if (components.day < 0) {
if (components.day < 1) {
return [NSString stringWithFormat:#"%ld days from now", labs((long)components.day)];
} else {
return #"Tomorrow";
}
}
else if (components.year > 0) {
return [NSString stringWithFormat:#"%ld years ago", (long)components.year];
} else if (components.month > 0) {
return [NSString stringWithFormat:#"%ld months ago", (long)components.month];
} else if (components.weekOfYear > 0) {
return [NSString stringWithFormat:#"%ld weeks ago", (long)components.weekOfYear];
} else if (components.day > 0) {
if (components.day > 1) {
return [NSString stringWithFormat:#"%ld days ago", (long)components.day];
} else {
return #"Yesterday";
}
} else {
return #"Today";
}
I have attached demo here please find on this link. TimestampAgo-Demo
Thanks to n00bprogrammer
Edit :- I made changes in Sourcetimezone with [NSTimeZone systemTimeZone] because due to static time zone , issue occure in GMT or UTC format. (second goes in minus) and change deprecated Methods.
Here is my solution in Swift 2 that avoid 24-hour problem by comparing two dates with a zero time.
extension NSDate {
private func dateWithZeroTime(date: NSDate) -> NSDate? {
let calendar = NSCalendar.currentCalendar()
let units: NSCalendarUnit = [.Day, .WeekOfYear, .Month, .Year]
let components = calendar.components(units, fromDate: date)
return calendar.dateFromComponents(components)
}
private func thisDay() -> NSDate? {
return self.dateWithZeroTime(self)
}
private func today() -> NSDate? {
return self.dateWithZeroTime(NSDate())
}
var relativeFormat: String? {
let today = self.today()
let thisDay = self.thisDay()
let formatter = NSDateFormatter()
formatter.dateStyle = NSDateFormatterStyle.LongStyle
let dateString = formatter.stringFromDate(self)
if nil != thisDay && nil != today {
let units: NSCalendarUnit = [.Day, .WeekOfYear, .Month, .Year]
let components = NSCalendar.currentCalendar().components(units, fromDate: thisDay!, toDate: today!, options: [])
if (components.year > 0) {
return components.year == 1 ? "A year ago, \(dateString)" : "\(components.year) years ago, \(dateString)"
} else if (components.month > 0) {
return components.month == 1 ? "A month ago, \(dateString)" : "\(components.month) months ago, \(dateString)"
} else if (components.weekOfYear > 0) {
return components.weekOfYear == 1 ? "A week ago, \(dateString)" : "\(components.weekOfYear) weeks ago, \(dateString)"
} else if (components.day > 0) {
return components.day == 1 ? "Yesterday, \(dateString)" : "\(self.dayOfTheWeek()), \(dateString)"
} else {
return "Today"
}
}
return nil
}
func dayOfTheWeek() -> String {
let weekdays = [
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday"
]
let calendar: NSCalendar = NSCalendar.currentCalendar()
let components: NSDateComponents = calendar.components(.Weekday, fromDate: self)
return weekdays[components.weekday - 1]
}
}
The Swift 5 solution:
public extension Date {
private func dateWithZeroTime(_ date: Date) -> Date? {
let calendar = Calendar.current
let units: Set<Calendar.Component> = Set( [.day, .weekOfYear, .month, .year])
let components = calendar.dateComponents(units, from: date)
return calendar.date(from: components)
}
private func thisDay() -> Date? {
return self.dateWithZeroTime(self)
}
private func today() -> Date? {
return self.dateWithZeroTime(Date())
}
var relativeFormat: String? {
let formatter = DateFormatter()
formatter.dateStyle = DateFormatter.Style.long
let dateString = formatter.string(from: self)
if let thisDay = self.thisDay(),
let today = self.today() {
let units: Set<Calendar.Component> = Set([.day, .weekOfYear, .month, .year])
let components = Calendar.current.dateComponents(units, from: thisDay, to: today)
if let year = components.year,
year > 0 {
return year == 1 ? "A year ago, \(dateString)" : "\(year) years ago, \(dateString)"
} else if let month = components.month,
month > 0 {
return month == 1 ? "A month ago, \(dateString)" : "\(month) months ago, \(dateString)"
} else if let weekOfYear = components.weekOfYear,
weekOfYear > 0 {
return weekOfYear == 1 ? "A week ago, \(dateString)" : "\(weekOfYear) weeks ago, \(dateString)"
} else if let day = components.day,
day > 0 {
return day == 1 ? "Yesterday, \(dateString)" : dayOfWeekWithDateString(dateString)
} else {
return "Today"
}
}
return nil
}
func dayOfTheWeek() -> String? {
let weekdays = [
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday"
]
let calendar = Calendar.current
let components: DateComponents = calendar.dateComponents(Set([.weekday]), from: self)
guard let weekday = components.weekday else { return nil }
return weekdays[weekday - 1]
}
func dayOfWeekWithDateString(_ dateString: String) -> String {
if let dayOfWeek = dayOfTheWeek() {
return "\(dayOfWeek), \(dateString)"
} else {
return dateString
}
}
}
let formatter = DateComponentsFormatter()
formatter.unitsStyle = .full
let now = NSDate()
let dateMakerFormatter = DateFormatter()
dateMakerFormatter.dateFormat = "yyyy/MM/dd HH:mm:ss z"
let dateString = "2017-03-13 10:38:54 +0000"
let stPatricksDay = dateMakerFormatter.date(from: dateString)!
let calendar = NSCalendar.current
let components = calendar.dateComponents([.hour, .minute,.weekOfMonth,.day,.year,.month,.second], from: stPatricksDay, to: now as Date)
if components.year! > 0 {
formatter.allowedUnits = .year
} else if components.month! > 0 {
formatter.allowedUnits = .month
} else if components.weekOfMonth! > 0 {
formatter.allowedUnits = .weekOfMonth
} else if components.day! > 0 {
formatter.allowedUnits = .day
} else if components.hour! > 0 {
formatter.allowedUnits = .hour
} else if components.minute! > 0 {
formatter.allowedUnits = .minute
} else {
formatter.allowedUnits = .second
}
let formatString = NSLocalizedString("%# ago", comment: "Used to say how much time has passed. e.g. '2 hours ago'")
let timeString = formatter.string(from: components)
String(format: formatString, timeString!)
To format the given "sourceDate" as "5:56 pm" for today, "yesterday" for any time yesterday, "January 16" for any day in the same year and "January 16, 2014". I am posting my own method.
sourceDate = //some date that you need to take into consideration
NSDateComponents *components = [[NSCalendar currentCalendar] components:NSCalendarUnitMinute | NSCalendarUnitHour | NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate:[NSDate date]];
NSDateComponents *sourceDateComponents = [[NSCalendar currentCalendar] components:NSCalendarUnitMinute | NSCalendarUnitHour | NSCalendarUnitDay | NSCalendarUnitMonth | NSCalendarUnitYear fromDate: sourceDate];
NSString* timestamp;
NSDateFormatter *formatSourceDate = [NSDateFormatter new];
[formatSourceDate setAMSymbol:#"AM"];
[formatSourceDate setPMSymbol:#"PM"];
//same day - time in h:mm am/pm
if (components.day == sourceDateComponents.day) {
NSLogInfo(#"time");
[formatSourceDate setDateFormat:#"h:mm a"];
timestamp = [NSString stringWithFormat:#"%#",[formatSourceDate stringFromDate:date]];
return timestamp;
}
else if (components.day - sourceDateComponents.day == 1) {
//yesterday
timestamp = NSLocalizedString(#"Yesterday", nil);
return timestamp;
}
if (components.year == sourceDateComponents.year) {
//september 29, 5:56 pm
[formatSourceDate setDateFormat:#"MMMM d"];
timestamp = [NSString stringWithFormat:#"%#",[formatSourceDate stringFromDate:date]];
return timestamp;
}
[formatSourceDate setDateFormat:#"MMMM d year"];
timestamp = [NSString stringWithFormat:#"%#",[formatSourceDate stringFromDate:date]];
return timestamp;
NSLogInfo(#"Timestamp : %#",timestamp);

Resources