loading...

Daily Challenge #252 - Everybody hates Mondays

thepracticaldev profile image dev.to staff ・2 min read

Nobody likes Mondays! You spent the weekend partying and hanging out with friends, and then Monday comes, and you have to get up early, put on some business clothes and go to work. So how many of these horrible days does someone have to endure? Well, let's find out.

Create a method to find the number of Mondays given a person's birthday and the current date. Do not worry about holidays/vacation, sick leave etc. Assume a person has a job and goes to work throughout a year if they are of working age. To keep things simple, assume someone starts working when they are 22 years old and retires when he/she is 78. That's right, Mondays don't count as bad days if you are at school/university or a pensioner! And although not having to work on the weekends is a rather recent fad (look it up!) assume that Mondays where and will always be bad days at any era.

Examples

Mondays.count(LocalDate.of(1995, 4, 3), LocalDate.of(2017, 4, 3))
=> 1 Monday

Mondays.count(LocalDate.of(1995, 4, 2), LocalDate.of(2018, 4, 2))
=> 53 Mondays

Tests

Mondays.count(LocalDate.of(1700, 9, 20), LocalDate.of(1762, 9, 26))
Mondays.count(LocalDate.of(-300, 12, 20), LocalDate.of(-258, 12, 22))
Mondays.count(LocalDate.of(1000000, 2, 25), LocalDate.of(1000022, 12, 28))


This challenge comes from shadowmanos on CodeWars. Thank you to CodeWars, who has licensed redistribution of this challenge under the 2-Clause BSD License!

Want to propose a challenge idea for a future post? Email yo+challenge@dev.to with your suggestions!

Posted on by:

thepracticaldev profile

dev.to staff

@thepracticaldev

The hardworking team behind dev.to ❤️

Discussion

pic
Editor guide
 

Python implementation

from datetime import datetime, timedelta


class Mondays():

    @staticmethod
    def count(birth: datetime, current_date: datetime):
        # For a more accurate deltatime in years use dateutil library with relativedelta instead, it supports leap years
        end_working = birth + timedelta(days=78 * 365)
        start_working = birth + timedelta(days=22 * 365)
        if current_date > end_working:
            current_date = birth + end_working
        first_monday = start_working + timedelta(days=(7 - start_working.weekday()) % 7)
        last_monday = current_date - timedelta(days=current_date.weekday())
        days_between_mondays = (last_monday - first_monday).days
        if days_between_mondays >= 7:
            return int(days_between_mondays / 7)
        return 1 if not current_date.weekday() or not start_working.weekday() else 0


try:
    mondays = Mondays.count(datetime(1995, 4, 3), datetime(2017, 4, 3))
    print (mondays)
except ValueError as ex:
    print (ex)

try:
    mondays = Mondays.count(datetime(1995, 4, 2), datetime(2018, 4, 2))
    print (mondays)
except ValueError as ex:
    print (ex)

try:
    mondays = Mondays.count(datetime(1700, 9, 20), datetime(1762, 9, 26))
    print (mondays)
except ValueError as ex:
    print (ex)

try:
    mondays = Mondays.count(datetime(-300, 12, 20), datetime(-258, 12, 22))
    print (mondays)
except ValueError as ex:
    print (ex)

try:
    mondays = Mondays.count(datetime(1000000, 2, 25), datetime(1000022, 12, 28))
    print (mondays)
except ValueError as ex:
    print (ex)
 

Rust, likely a better way to do this.

use std::convert::TryInto;
use chrono::prelude::*; // 0.4.11

pub struct Mondays {}

impl Mondays {
    pub fn count(birth: chrono::NaiveDate, curr_date: chrono::NaiveDate) -> i64 {
        // start work at age 22
        let start_work = birth + chrono::Duration::days(22 * 365);
        // retire at age 78
        let retire = birth + chrono::Duration::days(78 * 365);
        // check if they have retired or not
        let end_work = if curr_date > retire { retire } else { curr_date };
        // check days from monday, if 0 set to 7
        let start_from_monday = match start_work.weekday().num_days_from_monday() {
            0 => 7,
            x => x,
        };
        // get first monday
        let first_monday = start_work + chrono::Duration::days((7 - start_from_monday).try_into().unwrap());
        // get last monday
        let last_monday = end_work - chrono::Duration::days((end_work.weekday().num_days_from_monday()).try_into().unwrap());
        // count days between mondays
        let between_mondays = last_monday.signed_duration_since(first_monday).num_days();
        // if more than 7 days, div by 7
        if between_mondays > 7 {
            between_mondays / 7
        } else if between_mondays >= 0 {
           1 
        } else {
            0
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn test_1() {
        assert_eq!(Mondays::count(chrono::NaiveDate::from_ymd(1995, 4, 3), chrono::NaiveDate::from_ymd(2017, 4, 3)), 1);
    }
    #[test]
    fn test_2() {
        assert_eq!(Mondays::count(chrono::NaiveDate::from_ymd(1995, 4, 2), chrono::NaiveDate::from_ymd(2018, 4, 2)), 53);
    }
    #[test]
    fn test_3() {
        assert_eq!(Mondays::count(chrono::NaiveDate::from_ymd(1995, 4, 2), chrono::NaiveDate::from_ymd(2017, 3, 2)), 0);
    }
}

Rust Playground
GitHub Gist

 

JavaScript version without outside packages. It takes me such a long time. Still doubt if it can pass all the tests, while CodeWar ask for a Java version.

const oneWeek = 7 * 24 * 3600 * 1000
const oneDay = 24 * 3600 * 1000
const getWorkDate =  ([year, month, day]) => [year+22, month, day]
const getRetireDate = ([year, month, day]) => [year+78, month, day]
const getDate = ([year, month, day]) => new Date(year, month-1, day)
const formatMonday = number => `${number} Monday${number == 0 || number == 1 ? '' : 's'}`

const cmpDate = (date1, date2) => {
    for (let i=0;i<3;i++) {
        if(date1[i]>date2[i]) return 'large'
        if(date1[i]<date2[i]) return 'less'
    }
    return 'equal'
}



const countMonday = (birthDate, currentDate) => {
    let count = 0
    const workDate =  getWorkDate(birthDate)
    const retireDate = getRetireDate(birthDate)
    if(cmpDate(currentDate, workDate) == 'less') return formatMonday(count)
    const endDate = cmpDate(currentDate, retireDate) != 'less' ? retireDate : currentDate

    const workWeekDay = getDate(workDate).getDay()
    const endWeekDay = getDate(endDate).getDay()
    console.log(workWeekDay, endWeekDay)

    const duration = getDate(endDate).getTime() - getDate(workDate).getTime() + oneDay
    const weekCount = parseInt(duration/oneWeek)
    console.log(duration, oneWeek, duration % oneWeek)
    if (duration % oneWeek != 0 && (
        endWeekDay < workWeekDay || endWeekDay == 1
    )) {
        count = weekCount + 1
    } else {
        count = weekCount
    }
    return formatMonday(count)
}

birth = [1995, 4, 3]
current = [2017, 4, 3]
console.log(countMonday(birth, current))

birth = [1995, 4, 2]
current = [2018, 4, 2]
console.log(countMonday(birth, current))