Guide: Friendly Date Ranges

:triangular_flag_on_post: Remember to use Read-Search-Ask if you get stuck. Try to pair program :busts_in_silhouette: and write your own code :pencil:

:checkered_flag: Problem Explanation:

Create a program that will take two dates and convert them into a more easy to understand date such as January 1st, 2017. It will also check the difference between them, and handles cases with no difference, more than a day, more than a month, more than a year, and more than a month and less than a year respectively.

Relevant Links

:speech_balloon: Hint: 1

Split the string into an array where you get “YYYY”, “MM”, “DD”.

try to solve the problem now

:speech_balloon: Hint: 2

You need to handle the case for “st”, “nd”, and “th”. Note that 13 is “th” not “rd”.

try to solve the problem now

:speech_balloon: Hint: 3

If you are using Date() to create instances of dates to work with, then use UTC time to avoid errors due to time zone difference between servers.

try to solve the problem now

Spoiler Alert!

687474703a2f2f7777772e796f75726472756d2e636f6d2f796f75726472756d2f696d616765732f323030372f31302f31302f7265645f7761726e696e675f7369676e5f322e676966.gif

Solution ahead!

:beginner: Basic Code Solution:

function makeFriendlyDates(str) {

  var months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];

  // Convert a YYYY-MM-DD string into a date object.
  function convertDate(str) {
    // Split the dates to work independently.
    var dateStr = str.split('-');

    // Force the dates into Universal time to avoid issues due to timezones.
    return (new Date(Date.UTC(dateStr[0], dateStr[1] - 1, dateStr[2])));

  }

  // Handles the case of the day's endings.
  function dateEnding(val) {
    switch (val) {
      case 1:
      case 21:
      case 31:
        return val + 'st';
      case 2:
      case 22:
        return val + 'nd';
      case 3:
      case 23:
        return val + 'rd';
      default:
        return val + 'th';
    }
  }

  // Checks for the real difference in months to avoid errors
  function monthDiff(date1, date2) {
    var month2 = date2.getUTCFullYear() * 12 + date2.getUTCMonth();
    var month1 = date1.getUTCFullYear() * 12 + date1.getUTCMonth();
    return month2 - month1;
  }

  //day diff
  function dayDiff(date1, date2) {
    if(date2.getUTCMonth() === date1.getUTCMonth()){
      return date1.getUTCDate()-date2.getUTCDate();
    }
    return 0;
  }

  // Get's the right month string.
  function getMonth(date) {
    return months[date.getUTCMonth()];
  }

  function displayDate() {

    // Handles same day
    if (date2.getTime() - date1.getTime() === 0) {
      return [getMonth(date1) + ' ' + dateEnding(date1.getUTCDate()) + ', ' + date1.getUTCFullYear()];
    }

    // Handles same month
    if (date1.getUTCMonth() === date2.getUTCMonth() && date1.getUTCFullYear() === date2.getUTCFullYear()) {
      return [getMonth(date1) + ' ' + dateEnding(date1.getUTCDate()), dateEnding(date2.getUTCDate())];
    }

    // Handles more than a month of difference, but less than 12 months and different year
    if (monthDiff(date1, date2) < 12 && date1.getUTCFullYear() !== date2.getUTCFullYear() ) {
      return [getMonth(date1) + ' ' + dateEnding(date1.getUTCDate()), getMonth(date2) + ' ' + dateEnding(date2.getUTCDate())];
    }

    // Handles same month but different year
    if (monthDiff(date1, date2) <= 12 && dayDiff(date1, date2)>0) {
      return [getMonth(date1) + ' ' + dateEnding(date1.getUTCDate())+', '+date1.getUTCFullYear(), getMonth(date2) + ' ' + dateEnding(date2.getUTCDate())];
    }

    // Handles more than a month of difference, but less than 12 months and same year
    if (monthDiff(date1, date2) < 12) {
      return [getMonth(date1) + ' ' + dateEnding(date1.getUTCDate())+', '+date1.getUTCFullYear(), getMonth(date2) + ' ' + dateEnding(date2.getUTCDate())];
    }

    // Handles cases with more than 12 months apart.
    return [getMonth(date1) + ' ' + dateEnding(date1.getUTCDate()) + ', ' + date1.getUTCFullYear(), getMonth(date2) + ' ' + dateEnding(date2.getUTCDate()) + ', ' + date2.getUTCFullYear()];
  }

  var date1 = convertDate(str[0]);
  var date2 = convertDate(str[1]);

  return displayDate();

}

// test here
makeFriendlyDates(['2016-07-01', '2016-07-04']);

:rocket: Run Code

Code Explanation:

  • The function convertDate() converts a string in the format YYYY-MM-DD to a date object.
    • split() the dates on - to work independently.
    • Force the dates into universal time to avoid timezone issues.
  • The function dateEnding() handles day’s ending i.e., appending st, nd, rd or th.
  • The function monthDiff() checks the real difference in month to avoid errors.
  • The function dayDiff() checks the real difference in day to avoid errors.
  • The function getMonth() returns month string for particular date.
  • The function displayDate() displays the date correctly. Following provisions are made:
    • Handles same day.
    • Handles same month.
    • Handles more than a month of difference, but less than 12 months and different year.
    • Handles same month but different year.
    • Handles more than a month of difference, but less than 12 months and same year.
    • Handles cases with more than 12 months apart.

Relevant Links

:clipboard: NOTES FOR CONTRIBUTIONS:

  • :warning: DO NOT add solutions that are similar to any existing solutions. If you think it is similar but better, then try to merge (or replace) the existing similar solution.
  • Add an explanation of your solution.
  • Categorize the solution in one of the following categories — Basic, Intermediate and Advanced. :traffic_light:
  • Please add your username only if you have added any relevant main contents. (:warning: DO NOT remove any existing usernames)

See :point_right: Wiki Challenge Solution Template for reference.

You did not check for currentYear case.

1 Like

Explanation::: Read comments …

function makeFriendlyDates(arr) {
  // Split date strings into Array
  var first = arr[0].split("-"),
      second = arr[1].split("-"),
  // Convert string values into integer numbers
      firstYear = parseInt(first[0]),
      firstMonth = parseInt(first[1]),
      firstDay = parseInt(first[2]),
      secondYear = parseInt(second[0]),
      secondMonth = parseInt(second[1]),
      secondDay = parseInt(second[2]);
  // Set Month Names
  var months = {1:"January",2:"February",3:"March",4:"April",5:"May",6:"June",7:"July",8:"August",
                9:"September",10:"October",11:"November",12:"December"};
  
  // Check ordinal Dates
  function ordinalDates(day){
    switch (day){
      case 1:
      case 21:
      case 31:
         return day + "st";
      case 2:
      case 22:
         return day + "nd";
      case 3:
      case 23:
         return day + "rd";
      default:
         return day + "th";
    }
  }  
  var friendlyFirst = "" + months[firstMonth] + " " + ordinalDates(firstDay);
  var friendlySecond = "" + months[secondMonth] + " " + ordinalDates(secondDay);
  
  switch (true) {
  //Check same Dates
    case (arr[0] === arr[1]): 
      return [friendlyFirst + ", " + firstYear];
  //Check If the range ends in the same month that it begins, do not display the ending year or month.
    case ((firstYear === secondYear) && (firstMonth === secondMonth)):
      return [friendlyFirst,"" + ordinalDates(secondDay)];
  //Check if the date range ends in less than a year from when it begins, do not display the ending year.  
    case ((firstYear === secondYear) && (firstMonth != secondMonth)):
      return [friendlyFirst + ", " + firstYear, friendlySecond];
  /*Check if the date range ends in less than a year from when it begins and 
    check if the range ends in the same month that it begins.*/
    case ((secondYear - firstYear == 1) && (firstMonth === secondMonth) && (firstDay > secondDay)):
      return [friendlyFirst + ", " + firstYear, friendlySecond];
  //Check if the date range ends within one year, the year should not be displayed at the beginning of the friendly range.
    case ((secondYear - firstYear == 1) && (firstMonth > secondMonth)):
      return [friendlyFirst, friendlySecond];
    default:
      return [friendlyFirst + ", " + firstYear, friendlySecond + ", " + secondYear];
  };
}

I’m gonna leave this here for others running the current challenges (not the beta push), cause I’m still pissed off that this NFP has refused to fix the bugs in these challenges, having known since JULY 2016 of these issues. Hours of trying to debug YOUR issue. blah…

// To Err is code, to Arr is pirate. So work like a captain, but code like a pirate!
// walk-through with Stephen Mayeux
//DON'T EVER DO THIS FOR REALSY, USE A LIBRARY - http://momentjs.com/
// THIS CHALLENGE IS BUGGY AS ALL GET OUT, AND YOU'VE KNOWN OF THE ISSUES SINCE JULY 2016

// start the helper functions to avoid a ton of copypasta throughout this challenge
// our months array

  var months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];

//convert integer to ordinal number

  function makeOrdinals(num) {
    switch (num) {
      case 1:
      case 21:
      case 31:
        return num.toString() + 'st';
      case 2:
      case 22:
        return num.toString() + 'nd';
      case 3:
      case 23:
        return num.toString() + 'rd';
      default:
        return num.toString() + 'th';
    }
  }

// access months numerical number array (*January = [0]) and create ordinal number
  function monthAndDay(d) {
    return months[d.getMonth()] + ' ' + makeOrdinals(d.getDate());
  }

  function fullDate(d) {
    return monthAndDay(d) + ', ' + d.getFullYear();
  }  

	function makeFriendlyDates(arr) {
    //midnight, Jan 1, 1970 - https://www.quora.com/What-is-the-significance-of-January-1-1970
    //return Date.parse(arr[0]); = 1467331200000 miliseconds

// Change all date vars to UTC, and use (-1) on the currentYear var, because FCC hasn't fixed the bugs in this challenge. thanks      
    var currentYear = new Date().getUTCFullYear() - 1;
    var start = new Date(Date.parse(arr[0]));
      start = new Date(start.getUTCFullYear(), start.getUTCMonth(), start.getUTCDate());
    var end = new Date(Date.parse(arr[1]));
      end = new Date(end.getUTCFullYear(), end.getUTCMonth(), end.getUTCDate());
    var yearInMS = 31536000000; // 1 year in miliseconds

    //test variables by uncommenting one line below
    //return currentYear;
    //return start;
    //return end;
    //return yearInMS;

    // if our start and end dates are the same, in miliseconds
    if (end - start === 0) {
      return [fullDate(start)];
    }

    // if we are in the current year and same month
    if (start.getFullYear() === currentYear && start.getMonth() === end.getMonth()) {
      return [monthAndDay(start), makeOrdinals(end.getDate())];
    }

    // if we are in the current year and dates are less than one year apart
    if (start.getFullYear() === currentYear && end - start < yearInMS) {
      return [monthAndDay(start), monthAndDay(end)];
    }

    // if we are NOT in the current year, but less than a year apart
    if (end - start < yearInMS) {
      return [fullDate(start), monthAndDay(end)];
    }

    // if our dates are more than a year apart
    if (end - start >= yearInMS) {
      return [fullDate(start), fullDate(end)];
    }
  }


makeFriendlyDates(['2017-07-01', '2017-07-04']);

Not beautiful, but hey it works. Can’t expect more from such a nasty challenge.

function makeFriendlyDates(arr) {
  var f=['',''];
  var diff=arr.map(e=>(e.replace(/-/g,''))).reduce((a,v)=>v-a);
  var diffMoreThanYear=diff>9999;
  var dates=arr.map(e=>e.split('-').map(v=>parseInt(v)));
  
  //month
  f[0]=f[0].concat(month(dates[0][1])+' ');
  if(dates[0][0]!=dates[1][0]||dates[0][1]!=dates[1][1]){
    f[1]=f[1].concat(month(dates[1][1])+' '); 
  }
  //day
  f=f.map((v,i)=>v.concat(day(dates[i][2])));
  //year
  if(dates[0][0]!=2016||diffMoreThanYear){
    f[0]=f[0].concat(', '+dates[0][0]); 
  }
  if(diffMoreThanYear){
    f[1]=f[1].concat(', '+dates[1][0]); 
  }
  if(diff==0){
    f.pop();
    return f;
  }
  else return f;
}

function month(str){
  switch(str){
    case 1: return 'January';
    case 2: return 'February';
    case 3: return 'March';
    case 4: return 'April';
    case 5: return 'May';
    case 6: return 'June';
    case 7: return 'July';
    case 8: return 'August';
    case 9: return 'September';
    case 10: return 'October';
    case 11: return 'November';
    case 12: return 'December';
  }             
}

function day(n){
  switch(n){
    case 1: return '1st';
    case 2: return '2nd';
    case 3: return '3rd';
  }
  return n+'th';
}

Did this problem get pulled from the course? I can’t find it anywhere.

Nonetheless, I’m glad I stumbled upon this thread as I have just completed the challenge (all I have to do is submit it now) but the code is very ugly when compared to some really elegant solutions presented here.