0

I really want to know how much more structure you can do for a task like this in a class cause right now I'm in shock that my answer wasn't enough for this company's interview question. They literally asked me to take a date and see if it is between today and 5 days prior. So let me know how much more needs to be done here.

The date in question is passed to a function. That function takes today's date and then you compare the date in question from 5 days prior to today's date. If the date in question is ascending from the 5-days-prior and then if the date is descending from today's date the function returns true. So someone let me know where I went wrong here.

func compareDate(thisDate:Date,aheadOfDate:Date, beforeThisDate:Date) -> Bool{
    if( thisDate < aheadOfDate){
        return false
    }
    if(thisDate > beforeThisDate){
        return false
    }
    return true
}

func daysFromToday(days:Int) -> Date{

    let calendar = Calendar.current
    let newDate = calendar.date(byAdding: .day, value: days, to:  Date.init())
    return newDate!
}

let todaysDate = Date.init()
let fiveDaysAgo = daysFromToday(days:-5)

print(compareDate(thisDate: daysFromToday(days: 1), aheadOfDate: fiveDaysAgo, beforeThisDate: todaysDate))
rmaddy
  • 314,917
  • 42
  • 532
  • 579
Esko918
  • 1,397
  • 1
  • 12
  • 25
  • 6
    I'm voting to close this question as off-topic because this question belongs to [codereview.stackexchange.com](https://codereview.stackexchange.com) – Dávid Pásztor Jan 08 '18 at 23:13
  • 1
    First you should use Date() instead of Date.init() as you don't need to specify init. Secondly although they don't say as you are comparing dates you probably need to take time out of the equation (i.e. make sure all dates are the start of the day). After that there are multiple ways to compare a date. The question isn't the best because 1) They also don't say if it should be a simple solution or more generic which would allow it to be parameterised (i.e. enter the range -days to +days) 2) The don't say if the comparison is inclusive or exclusive – Upholder Of Truth Jan 08 '18 at 23:30
  • 1
    @UpholderOfTruth for calendrical calculations not time sensitive you should use noon instead of startOfDay. Take a look at the comments at this answer https://stackoverflow.com/a/48158011/2303865 – Leo Dabus Jan 08 '18 at 23:40
  • @LeoDabus that depends if you are checking if something is on a particular day then it's the start not noon. So if you want to know if something is up to 5 days ago you use the start of that day not noon because 9am is still on that day 5 days ago. – Upholder Of Truth Jan 08 '18 at 23:41
  • @UpholderOfTruth Look at the comments first – Leo Dabus Jan 08 '18 at 23:42
  • @LeoDabus ignore my last comment I was being stupid and thinking of a specific situation I recently encountered. However in my original comment I said 'start of the day'. I didn't specify what that actually meant as circumstances can change it. You are correct that for a lot of general usage noon is good. However if I said was something up to 1 day ago (i.e. today or yesterday) I could still consider 9am yesterday to be included in that but 23:59 on the day before excluded. We are talking about things being on a day not a specific time period. As I said the question they asked isn't clear. – Upholder Of Truth Jan 08 '18 at 23:47
  • @LeoDabus I just re-watched that video (it's been a while) and noon is used when you 'don't care' about when you are in the day but in the case of checking if you are in a date range you probably want to go from the start of the day at the beginning of the range (so 5 days ago) to the end of the date at the end of the range (end of today). In that video there is a section about calculating the start of the day (and therefore the end) and it's much more complex than it would appear. – Upholder Of Truth Jan 09 '18 at 00:06
  • I am talking about the number of days between two dates not time sensitive. You should always use noon. – Leo Dabus Jan 09 '18 at 00:45
  • @LeoDabus yep perfectly true as long as you do the same with both dates the one you are checking and the one you check against. I was thinking more if you want to change the date you are checking (I admit there isn't really a reason not to just proposing a different solution) – Upholder Of Truth Jan 09 '18 at 07:27
  • @LeoDabus all this is two but what you are really doing is comparing two points of time that are at the same position in the day. So you can really use anything as long as it's definitely in the day all the time. So both noon and the start of the day (note not midnight or the startOfDay function but the calculated start of the day see that WWDC video for the explanation) – Upholder Of Truth Jan 09 '18 at 07:51

4 Answers4

1

It would be simpler to subtract the given date from today's date and see if the difference in days is between 0 and 5. Also, IDK if they care, but the compareDate function could instead be a one-line boolean interpretation.

sudo
  • 5,604
  • 5
  • 40
  • 78
  • Yes but then you could only do one day instead of being able to check the given date against any date – Esko918 Jan 08 '18 at 23:15
  • @Esko918 If you want to compare to something besides the current date, you can easily have your function take another date param instead of the current date and see if the difference is between 0 and 5. The point is you don't have to create a third date just to do the comparison. – sudo Jan 09 '18 at 02:00
1

First function

I would restructure into an extension on Date:

extension Date {
    func isBetween(_ first: Date, and second: Date) -> Bool {
        return first < self && self < second
    }
}

Then I would replace this a < b && b < c pattern with a range expression a...c ~= b, which creates a date range, and checks if self is in it:

extension Date {
    func isBetween(_ first: Date, and second: Date) -> Bool {
        return first...second ~= self
    }
}

Second function

I would also restructure into an extension on Date, and I would inline calendar and newDate because they add visual clutter, without providing any new information. I would also replace Date.init() with just Date().

extension Date {
    func offset(days: Int) -> Date {
        return Calendar.current.date(byAdding: .day, value: days, to:  self)
    }
}

Usage

In the final snippet, I would use these functions as such:

let now = Date()
let fiveDaysAgo = now.offset(days: -5)

print(now.offset(days: +1).isBetween(fiveDaysAgo, and: now))
Alexander
  • 59,041
  • 12
  • 98
  • 151
1

I think what the current answers are missing is that if you want to compare dates you need to take the time out of the equation (which is not the same as not caring about the time). So for that interview question what you are really saying is 'is the date I am checking >= the start of the day 5 days ago and before the start of tomorrow'. After all if you are checking it at 10pm today then 9am 5 days ago would still be ok but not if you are including time in all the checks.

So before doing any checking you need to calculate the start of day for 5 days ago and tomorrow which is done something like this:

    let now = Date()

    let cal = Calendar.current
    var start = cal.startOfDay(for: Date())
    let inYesterday = cal.date(byAdding: .day, value: -5, to: now)!
    start = cal.startOfDay(for: inYesterday)

    var end = cal.startOfDay(for: Date())
    let inTomorrow = cal.date(byAdding: .day, value: 1, to: now)!
    end = cal.startOfDay(for: inTomorrow)

(This is more complex than you would think because you have to account for different time zones and formats and things like summer time.)

Then depending on how you want to use it you could do something like this:

extension Date {
    func isBetween(date: Date, andDaysAgo daysAgo: Int) -> Bool {
        let cal = Calendar.current
        var start = cal.startOfDay(for: date)
        let inYesterday = cal.date(byAdding: .day, value: -daysAgo, to: date)!
        start = cal.startOfDay(for: inYesterday)

        var end = cal.startOfDay(for: date)
        let inTomorrow = cal.date(byAdding: .day, value: 1, to: date)!
        end = cal.startOfDay(for: inTomorrow)

        return start..<end ~= self
    }
}

Which you would call like this:

var checkDate = Date(timeIntervalSinceNow: 3600 * 24 * 0)
print (checkDate.isBetween(date: Date(), andDaysAgo: 5) // prints true
checkDate = Date(timeIntervalSinceNow: 3600 * 24 * -5)
print (checkDate.isBetween(date: Date(), andDaysAgo: 5) // prints true
checkDate = Date(timeIntervalSinceNow: 3600 * 24 * 1)
print (checkDate.isBetween(date: Date(), andDaysAgo: 5) // prints false
checkDate = Date(timeIntervalSinceNow: 3600 * 24 * -6)
print (checkDate.isBetween(date: Date(), andDaysAgo: 5) // prints flase

(these are just quick hacked examples)

EDIT

As was pointed out to me (quite rightly) if you are comparing dates and don't care about time then you can use noon as your reference point (basically it's always fixed and unaffected by things like daylight saving changes). So this is one way to do that:

func isBetweenAlt(date: Date, andDaysAgo daysAgo: Int) -> Bool {
    let cal = Calendar.current
    let startCheck = cal.date(bySettingHour: 12, minute: 0, second: 0, of: cal.date(byAdding: .day, value: -daysAgo, to: date)!)!
    let endCheck = cal.date(bySettingHour: 12, minute: 0, second: 0, of: date)!
    let checkDate = cal.date(bySettingHour: 12, minute: 0, second: 0, of: self)!

    return startCheck...endCheck ~= checkDate
}

Also in the spirit of learning here are a couple of other ways to do it:

func isBetweenAlt2(date: Date, andDaysAgo daysAgo: Int) -> Bool {
    let cal = Calendar.current
    let startCheck = cal.date(byAdding: .day, value: -(daysAgo + 1), to: date)! // You have to offset by one day as the test will be > not >=
    let endCheck = cal.date(byAdding: .day, value: 1, to: date)! // You have to offset by one day as the test will be < not <=

    return cal.compare(startCheck, to: self, toGranularity: .day) == .orderedAscending && cal.compare(endCheck, to: self, toGranularity: .day) == .orderedDescending
}

func isBetweenAlt3(date: Date, andDaysAgo daysAgo: Int) -> Bool {
    let cal = Calendar.current
    let startCheck = cal.ordinality(of: .day, in: .era, for: cal.date(byAdding: .day, value: -daysAgo, to: date)!)!
    let endCheck = cal.ordinality(of: .day, in: .era, for: date)!
    let check = cal.ordinality(of: .day, in: .era, for: self)!

    return startCheck...endCheck ~= check
}

All of them do the same job (I think).

EDIT

With reference back to the original question and these kind of tests in interviews. Often there are multiple solutions to a problem that may all be equally valid and the idea of the interview question is not necessarily to get the answer they think is correct but to show that you have thought about the issues that affect the problem. Particularly with a junior developer role it can be less important that an answer is complete in the time but more important that the person understood the issue, the challenges involved and how to go about solving it.

Upholder Of Truth
  • 4,643
  • 2
  • 13
  • 23
0

I made your code a bit shorter and a bit more "swift-like"

func compare(date thisDate: Date, aheadOf aheadOfDate: Date, before beforeDate: Date) -> Bool {
    return (thisDate > aheadOfDate) && (thisDate < beforeDate)
}

func fromToday(days: Double) -> Date {
    let today = Date()
    return Date(timeIntervalSince1970: today.timeIntervalSince1970 + (days * 86400.0)) //Todays date in seconds + seconds in a day times desired number of days
}

let today = Date()
let fiveDaysAgo = fromToday(days: -5)
let oneDayFromToday = fromToday(days: 1)

print(compare(date: oneDayFromToday, aheadOf: fiveDaysAgo, before: today))
Victor Apeland
  • 110
  • 1
  • 10