How to get Monday 00:00 of current week? Swift 3 - ios

I'm trying to return Monday 00:00 from my date. This is my code:
func getMonday(myDate: Date) -> Date {
let cal = Calendar.current
let comps = cal.dateComponents([.weekOfYear, .yearForWeekOfYear], from: myDate)
let beginningOfWeek = cal.date(from: comps)!
return beginningOfWeek
}
My problem is that it does not return Monday 00:00 , but Saturday 22:00.
Example:
let monday1 = getMonday(myDate: date) //date is: 2016-10-04 17:00:00
print(monday1) //Prints: 2016-10-01 22:00:00 (Saturday)
My question is:
How to return Monday 00:00 from myDate?
Thank you very much.

Your code returns the first day in the given week, that may be
a Sunday or Monday (or perhaps some other day), depending on your locale.
If you want Monday considered to be the first weekday then set
cal.firstWeekday = 2
If you want the Monday of the given week, independent of what the
start of the week is, then set comps.weekday = 2:
func getMonday(myDate: Date) -> Date {
let cal = Calendar.current
var comps = cal.dateComponents([.weekOfYear, .yearForWeekOfYear], from: myDate)
comps.weekday = 2 // Monday
let mondayInWeek = cal.date(from: comps)!
return mondayInWeek
}
Note that printing a Date always uses the GMT time zone,
you'll need a date formatter to print the result according to your local time zone. Example:
let df = DateFormatter()
df.dateFormat = "yyyy-MM-dd HH:mm"
let now = Date()
print(df.string(from: now)) // 2016-10-02 20:16
let monday1 = getMonday(myDate: now)
print(df.string(from: monday1)) // 2016-09-26 00:00

Related

Converting the string to date giving different format [duplicate]

This question already has answers here:
Getting date from [NSDate date] off by a few hours
(3 answers)
Closed 3 years ago.
Converting from string to date and date to string time format is changing the original data.
Tried with dateComponents as well by giving the hour and minute
var calendar = Calendar.current
let components = calendar.dateComponents([.year, .month, .day, .hour], from: calFrom)
calendar.timeZone = .current
// Specify date components
var dateComponents:DateComponents = calendar.dateComponents([.year, .month, .day, .hour], from: Date())
dateComponents.year = components.year
dateComponents.month = components.month
dateComponents.day = components.day
dateComponents.hour = 08//Cutomised hour
dateComponents.minute = 34//Cutomised Minutes
// Create date from components
let someDateTime = calendar.date(from: dateComponents)
print(someDateTime!)
Actual Output:
2019-04-02 03:04:00 +0000
Expected Output:
2019-04-02 08:34:00 +0000
I tried with below code as well. Converting the date to String and manually appending the hour and minutes to the string and converting back to the date.
let calFrom = Date()
let formatter = DateFormatter()
formatter.dateFormat = "dd/MM/yyyy"
var calFromDate = formatter.string(from: calFrom)
calFromDate = calFromDate + " 09" + ":30"
print(calFromDate)
//Output 02/04/2019 09:30
formatter.dateFormat = "dd/MM/yyyy hh:mm"
formatter.locale = Locale.current// set locale to reliable US_POSIX
let date1 = formatter.date(from: calFromDate)
print(date1!)
Actual Output:
2019-04-02 04:00:00 +0000
Expected Output:
02/04/2019 09:30
How to get the exact time that has given in the output?
Date used to update the hour and minute components has UTC timezone so calendar should also have the same timeZone as below,
calendar.timeZone = TimeZone(abbreviation: "UTC")!

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)

Date from week of year returning date not in week

I have come across a rather strange "bug". When getting a date for a week of a year using this method:
let dates = NSMutableArray()
let cal = NSCalendar.currentCalendar()
cal.firstWeekday = 2
let formatter = NSDateFormatter()
formatter.dateFormat = "ww YYYY"
formatter.calendar = cal
let date = formatter.dateFromString(week as String)
println(date)
The string week is 52 2014, so the expected date would be Monday December 22th, but instead it returns Saturday December 20th, at 23:00. First of all, I thought I'd handled the first day of week by setting the firstWeekday-option of the calendar, but no luck. In addition, the date returned isn't even in week 52.
Just to double check I ran cal.components(NSCalendarUnit.WeekOfYearCalendarUnit, fromDate: date!).weekOfYear to double check I'm not an idiot, and no sir, the week for the date produced is 51, the week before the desired week.
Any idea how I can reach the expected result?
Any idea how I can reach the expected result?
What actually is your desired result? Do you want to know the first day of the week or the first day in the last day? Than you could tray this:
let now = NSDate()
var startDate: NSDate? = nil
var duration: NSTimeInterval = 0
let cal = NSCalendar.currentCalendar()
cal.firstWeekday = 2
cal.rangeOfUnit(.WeekCalendarUnit, startDate: &startDate, interval: &duration, forDate: now);
let endDate = startDate?.dateByAddingTimeInterval(duration)
print(startDate)
print(endDate)
it prints
"Optional(2014-12-21 23:00:00 +0000)"
"Optional(2014-12-28 23:00:00 +0000)"
the endDate is the first second that is not in the week anymore.
Note that the offset of 1 hour results from the fact that it is printed in UTC time, that is actually GMT winter time. Indeed these dates are 2014-12-22 00:00:00 and 2014-12-29 00:00:00 in my time zone (GMT+1)
or simply
let components = NSDateComponents()
components.weekOfYear = 52
components.weekday = 2
components.year = 2014
let cal = NSCalendar.currentCalendar()
let day = cal.dateFromComponents(components)
This code adapted to respect user's calendar:
let cal = NSCalendar.currentCalendar()
let components = NSDateComponents()
components.weekOfYear = 52
components.weekday = cal.firstWeekday
components.year = 2014
Changing the firstWeekday from 1 to 2 won't change the date, it will change just the First weekday from Sunday to Monday.
You can do it like this:
func dateFromWeekOfYear(year:Int, weekOfYear:Int, weekday:Int) -> NSDate {
return NSCalendar.currentCalendar().dateWithEra(1, yearForWeekOfYear: year, weekOfYear: weekOfYear, weekday: weekday, hour: 0, minute: 0, second: 0, nanosecond: 0)!
}
let date1 = dateFromWeekOfYear(2014, 52, 1) // Dec 21, 2014, 12:00 AM
let date2 = dateFromWeekOfYear(2014, 52, 2) // Dec 22, 2014, 12:00 AM
let date3 = dateFromWeekOfYear(2014, 52, 3) // Dec 23, 2014, 12:00 AM
If dealing with a string and you want to set he Stand Alone local day of week you can do it like this:
let myDate = "2 52 2014"
let cal = NSCalendar.currentCalendar()
let formatter = NSDateFormatter()
formatter.dateFormat = "c ww Y"
formatter.calendar = cal
if let date1 = formatter.dateFromString(myDate) {
date1 // "Dec 22, 2014, 12:00 AM"
}
If you need further reference you can use this:

Resources