javascript
exercises
exercises.js⚡javascript
/**
* 15.2 Date Formatting & Calculations - Exercises
*
* Practice formatting dates and performing date calculations.
*/
/**
* Exercise 1: Custom Date Formatter
*
* Create a flexible date formatter that supports various format tokens.
*
* Tokens:
* - YYYY: 4-digit year
* - YY: 2-digit year
* - MMMM: Full month name
* - MMM: Abbreviated month name
* - MM: 2-digit month
* - M: 1-2 digit month
* - DD: 2-digit day
* - D: 1-2 digit day
* - dddd: Full weekday name
* - ddd: Abbreviated weekday name
* - HH: 2-digit hour (24-hour)
* - hh: 2-digit hour (12-hour)
* - mm: 2-digit minutes
* - ss: 2-digit seconds
* - A: AM/PM
* - a: am/pm
*
* @param {Date} date - The date to format
* @param {string} format - The format string
* @returns {string} - Formatted date
*/
function formatDate(date, format) {
// Your code here
}
// console.log(formatDate(new Date("2024-12-25T14:30:00"), "YYYY-MM-DD"));
// "2024-12-25"
// console.log(formatDate(new Date("2024-12-25T14:30:00"), "dddd, MMMM D, YYYY"));
// "Wednesday, December 25, 2024"
// console.log(formatDate(new Date("2024-12-25T14:30:00"), "MM/DD/YY"));
// "12/25/24"
// console.log(formatDate(new Date("2024-12-25T14:30:00"), "hh:mm A"));
// "02:30 PM"
// console.log(formatDate(new Date("2024-12-25T14:30:00"), "ddd, MMM D at h:mm a"));
// "Wed, Dec 25 at 2:30 pm"
/**
* Exercise 2: Date Duration Calculator
*
* Create a function that calculates the exact duration between two dates.
*
* @param {Date} startDate - Start date
* @param {Date} endDate - End date
* @returns {object} - { years, months, days, hours, minutes, seconds, totalDays }
*/
function calculateDuration(startDate, endDate) {
// Your code here
}
// console.log(calculateDuration(
// new Date("2020-03-15T10:30:00"),
// new Date("2024-06-20T14:45:30")
// ));
// {
// years: 4,
// months: 3,
// days: 5,
// hours: 4,
// minutes: 15,
// seconds: 30,
// totalDays: 1558
// }
/**
* Exercise 3: Date Arithmetic Functions
*
* Create a set of functions for date arithmetic.
*/
function add(date, amount, unit) {
// unit: 'years', 'months', 'weeks', 'days', 'hours', 'minutes', 'seconds'
// Your code here
}
function subtract(date, amount, unit) {
// Your code here
}
function startOf(date, unit) {
// unit: 'year', 'month', 'week', 'day', 'hour', 'minute'
// Your code here
}
function endOf(date, unit) {
// Your code here
}
// const date = new Date("2024-06-15T10:30:45");
// console.log(add(date, 1, 'months').toISOString()); // 2024-07-15T...
// console.log(subtract(date, 2, 'weeks').toISOString()); // 2024-06-01T...
// console.log(startOf(date, 'month').toISOString()); // 2024-06-01T00:00:00
// console.log(endOf(date, 'day').toISOString()); // 2024-06-15T23:59:59.999
/**
* Exercise 4: Relative Time Formatter
*
* Create a comprehensive relative time formatter.
*
* @param {Date} date - The date to format
* @param {Date} baseDate - The reference date (default: now)
* @param {object} options - Options { style: 'long' | 'short' | 'narrow' }
* @returns {string} - Relative time string
*/
function relativeTime(date, baseDate = new Date(), options = {}) {
// Your code here
}
// console.log(relativeTime(new Date(Date.now() - 30000)));
// "30 seconds ago" or "just now"
// console.log(relativeTime(new Date(Date.now() - 3600000)));
// "1 hour ago"
// console.log(relativeTime(new Date(Date.now() + 86400000)));
// "in 1 day" or "tomorrow"
// console.log(relativeTime(new Date("2024-01-01"), new Date("2024-06-15")));
// "5 months ago"
/**
* Exercise 5: Business Day Calculator
*
* Create functions for working with business days.
* Allow for custom holidays.
*/
function isBusinessDay(date, holidays = []) {
// Your code here
}
function addBusinessDays(date, days, holidays = []) {
// Your code here
}
function getBusinessDaysBetween(startDate, endDate, holidays = []) {
// Your code here
}
function getNextBusinessDay(date, holidays = []) {
// Your code here
}
// const holidays = [
// new Date("2024-12-25"), // Christmas
// new Date("2024-01-01"), // New Year
// ];
// console.log(isBusinessDay(new Date("2024-12-25"), holidays)); // false
// console.log(addBusinessDays(new Date("2024-12-23"), 3, holidays).toDateString());
// Should skip Christmas and weekends
/**
* Exercise 6: Date Range Functions
*
* Create functions for working with date ranges.
*/
function eachDayOfInterval(start, end) {
// Return array of all dates between start and end
// Your code here
}
function eachWeekOfInterval(start, end) {
// Return array of week start dates
// Your code here
}
function eachMonthOfInterval(start, end) {
// Return array of month start dates
// Your code here
}
function isWithinInterval(date, { start, end }) {
// Your code here
}
function areIntervalsOverlapping(intervalA, intervalB) {
// Your code here
}
// console.log(eachDayOfInterval(
// new Date("2024-01-01"),
// new Date("2024-01-05")
// ));
// Array of 5 dates
// console.log(areIntervalsOverlapping(
// { start: new Date("2024-01-01"), end: new Date("2024-01-15") },
// { start: new Date("2024-01-10"), end: new Date("2024-01-20") }
// ));
// true
/**
* Exercise 7: Calendar Generator
*
* Create a function that generates calendar data for a month.
*
* @param {number} year - The year
* @param {number} month - The month (1-12)
* @returns {object} - Calendar data
*/
function generateCalendar(year, month) {
// Your code here
// Return: {
// year, month, monthName,
// daysInMonth, firstDayOfWeek (0-6),
// weeks: [
// [{ date, isCurrentMonth, isToday, isWeekend }, ...]
// ]
// }
}
// console.log(generateCalendar(2024, 12));
// {
// year: 2024,
// month: 12,
// monthName: "December",
// daysInMonth: 31,
// firstDayOfWeek: 0,
// weeks: [...]
// }
/**
* Exercise 8: Time Zone Converter
*
* Create functions for working with time zones.
*/
function getTimeInTimezone(date, timezone) {
// Return formatted time in specified timezone
// Your code here
}
function convertTimezone(date, fromTimezone, toTimezone) {
// Convert date from one timezone to another
// Your code here
}
function getTimezoneOffset(timezone, date = new Date()) {
// Get offset in hours for a timezone
// Your code here
}
// console.log(getTimeInTimezone(new Date(), "America/New_York"));
// "10:30 AM EST"
// console.log(getTimeInTimezone(new Date(), "Asia/Tokyo"));
// "12:30 AM JST"
/**
* Exercise 9: Date Parser
*
* Create a function that parses various date string formats.
*
* @param {string} dateString - The date string to parse
* @param {string} format - Optional format hint
* @returns {Date|null} - Parsed date or null if invalid
*/
function parseDate(dateString, format = null) {
// Your code here
// Should handle:
// - ISO format: "2024-12-25T10:30:00Z"
// - US format: "12/25/2024"
// - European format: "25/12/2024"
// - Written: "December 25, 2024"
// - Short: "Dec 25, 2024"
// - Custom format if provided
}
// console.log(parseDate("2024-12-25"));
// console.log(parseDate("12/25/2024"));
// console.log(parseDate("25/12/2024", "DD/MM/YYYY"));
// console.log(parseDate("December 25, 2024"));
/**
* Exercise 10: Date/Time Utilities Object
*
* Create a comprehensive date utilities object.
*/
const DateUtils = {
// Comparison
isSame(date1, date2, unit) {
// unit: 'year', 'month', 'day', 'hour', 'minute'
// Your code here
},
isBefore(date1, date2) {
// Your code here
},
isAfter(date1, date2) {
// Your code here
},
// Checks
isToday(date) {
// Your code here
},
isTomorrow(date) {
// Your code here
},
isYesterday(date) {
// Your code here
},
isWeekend(date) {
// Your code here
},
isLeapYear(year) {
// Your code here
},
// Getters
getDaysInMonth(year, month) {
// Your code here
},
getQuarter(date) {
// Your code here
},
getWeekNumber(date) {
// Your code here
},
// Formatting
format(date, formatStr) {
// Your code here
},
formatDistance(date1, date2) {
// Your code here
},
};
// console.log(DateUtils.isSame(new Date("2024-06-15"), new Date("2024-06-20"), 'month'));
// true
// console.log(DateUtils.isToday(new Date()));
// true
// console.log(DateUtils.getQuarter(new Date("2024-08-15")));
// 3
// console.log(DateUtils.formatDistance(
// new Date("2024-01-01"),
// new Date("2024-06-15")
// ));
// "5 months"
// ============================================
// SOLUTIONS
// ============================================
/*
// Solution 1
function formatDate(date, format) {
const months = ['January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December'];
const days = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
const hours12 = date.getHours() % 12 || 12;
const ampm = date.getHours() >= 12 ? 'PM' : 'AM';
const tokens = {
'YYYY': date.getFullYear(),
'YY': String(date.getFullYear()).slice(-2),
'MMMM': months[date.getMonth()],
'MMM': months[date.getMonth()].slice(0, 3),
'MM': String(date.getMonth() + 1).padStart(2, '0'),
'M': date.getMonth() + 1,
'DD': String(date.getDate()).padStart(2, '0'),
'D': date.getDate(),
'dddd': days[date.getDay()],
'ddd': days[date.getDay()].slice(0, 3),
'HH': String(date.getHours()).padStart(2, '0'),
'H': date.getHours(),
'hh': String(hours12).padStart(2, '0'),
'h': hours12,
'mm': String(date.getMinutes()).padStart(2, '0'),
'm': date.getMinutes(),
'ss': String(date.getSeconds()).padStart(2, '0'),
's': date.getSeconds(),
'A': ampm,
'a': ampm.toLowerCase()
};
// Sort by length descending to match longer tokens first
const pattern = Object.keys(tokens)
.sort((a, b) => b.length - a.length)
.join('|');
return format.replace(new RegExp(pattern, 'g'), match => tokens[match]);
}
// Solution 2
function calculateDuration(startDate, endDate) {
let start = new Date(startDate);
let end = new Date(endDate);
if (start > end) [start, end] = [end, start];
const totalMs = end - start;
const totalDays = Math.floor(totalMs / (1000 * 60 * 60 * 24));
let years = end.getFullYear() - start.getFullYear();
let months = end.getMonth() - start.getMonth();
let days = end.getDate() - start.getDate();
let hours = end.getHours() - start.getHours();
let minutes = end.getMinutes() - start.getMinutes();
let seconds = end.getSeconds() - start.getSeconds();
if (seconds < 0) { seconds += 60; minutes--; }
if (minutes < 0) { minutes += 60; hours--; }
if (hours < 0) { hours += 24; days--; }
if (days < 0) {
const prevMonth = new Date(end.getFullYear(), end.getMonth(), 0);
days += prevMonth.getDate();
months--;
}
if (months < 0) { months += 12; years--; }
return { years, months, days, hours, minutes, seconds, totalDays };
}
// Solution 3
function add(date, amount, unit) {
const result = new Date(date);
switch (unit) {
case 'years': result.setFullYear(result.getFullYear() + amount); break;
case 'months': result.setMonth(result.getMonth() + amount); break;
case 'weeks': result.setDate(result.getDate() + amount * 7); break;
case 'days': result.setDate(result.getDate() + amount); break;
case 'hours': result.setHours(result.getHours() + amount); break;
case 'minutes': result.setMinutes(result.getMinutes() + amount); break;
case 'seconds': result.setSeconds(result.getSeconds() + amount); break;
}
return result;
}
function subtract(date, amount, unit) {
return add(date, -amount, unit);
}
function startOf(date, unit) {
const result = new Date(date);
switch (unit) {
case 'year':
result.setMonth(0);
case 'month':
result.setDate(1);
case 'day':
result.setHours(0);
case 'hour':
result.setMinutes(0);
case 'minute':
result.setSeconds(0, 0);
break;
case 'week':
result.setDate(result.getDate() - result.getDay());
result.setHours(0, 0, 0, 0);
break;
}
return result;
}
function endOf(date, unit) {
const result = new Date(date);
switch (unit) {
case 'year':
result.setMonth(11);
result.setDate(31);
result.setHours(23, 59, 59, 999);
break;
case 'month':
result.setMonth(result.getMonth() + 1, 0);
result.setHours(23, 59, 59, 999);
break;
case 'day':
result.setHours(23, 59, 59, 999);
break;
case 'hour':
result.setMinutes(59, 59, 999);
break;
case 'minute':
result.setSeconds(59, 999);
break;
case 'week':
result.setDate(result.getDate() + (6 - result.getDay()));
result.setHours(23, 59, 59, 999);
break;
}
return result;
}
// Solution 4
function relativeTime(date, baseDate = new Date(), options = {}) {
const rtf = new Intl.RelativeTimeFormat('en', {
numeric: 'auto',
style: options.style || 'long'
});
const diff = date - baseDate;
const absDiff = Math.abs(diff);
const sign = diff < 0 ? -1 : 1;
const seconds = absDiff / 1000;
const minutes = seconds / 60;
const hours = minutes / 60;
const days = hours / 24;
const weeks = days / 7;
const months = days / 30;
const years = days / 365;
if (seconds < 60) return rtf.format(Math.round(seconds) * sign, 'second');
if (minutes < 60) return rtf.format(Math.round(minutes) * sign, 'minute');
if (hours < 24) return rtf.format(Math.round(hours) * sign, 'hour');
if (days < 7) return rtf.format(Math.round(days) * sign, 'day');
if (weeks < 4) return rtf.format(Math.round(weeks) * sign, 'week');
if (months < 12) return rtf.format(Math.round(months) * sign, 'month');
return rtf.format(Math.round(years) * sign, 'year');
}
// Solution 5
function isBusinessDay(date, holidays = []) {
const day = date.getDay();
if (day === 0 || day === 6) return false;
const dateStr = date.toDateString();
return !holidays.some(h => h.toDateString() === dateStr);
}
function addBusinessDays(date, days, holidays = []) {
const result = new Date(date);
let added = 0;
const direction = days >= 0 ? 1 : -1;
days = Math.abs(days);
while (added < days) {
result.setDate(result.getDate() + direction);
if (isBusinessDay(result, holidays)) {
added++;
}
}
return result;
}
function getBusinessDaysBetween(startDate, endDate, holidays = []) {
let count = 0;
const current = new Date(startDate);
while (current < endDate) {
current.setDate(current.getDate() + 1);
if (isBusinessDay(current, holidays)) {
count++;
}
}
return count;
}
function getNextBusinessDay(date, holidays = []) {
const result = new Date(date);
do {
result.setDate(result.getDate() + 1);
} while (!isBusinessDay(result, holidays));
return result;
}
// Solution 6
function eachDayOfInterval(start, end) {
const days = [];
const current = new Date(start);
while (current <= end) {
days.push(new Date(current));
current.setDate(current.getDate() + 1);
}
return days;
}
function eachWeekOfInterval(start, end) {
const weeks = [];
const current = new Date(start);
current.setDate(current.getDate() - current.getDay()); // Start of week
while (current <= end) {
weeks.push(new Date(current));
current.setDate(current.getDate() + 7);
}
return weeks;
}
function eachMonthOfInterval(start, end) {
const months = [];
const current = new Date(start.getFullYear(), start.getMonth(), 1);
while (current <= end) {
months.push(new Date(current));
current.setMonth(current.getMonth() + 1);
}
return months;
}
function isWithinInterval(date, { start, end }) {
return date >= start && date <= end;
}
function areIntervalsOverlapping(intervalA, intervalB) {
return intervalA.start <= intervalB.end && intervalB.start <= intervalA.end;
}
// Solution 7
function generateCalendar(year, month) {
const monthNames = ['January', 'February', 'March', 'April', 'May', 'June',
'July', 'August', 'September', 'October', 'November', 'December'];
const firstDay = new Date(year, month - 1, 1);
const lastDay = new Date(year, month, 0);
const today = new Date();
today.setHours(0, 0, 0, 0);
const weeks = [];
let week = [];
// Previous month days
const prevMonthEnd = new Date(year, month - 1, 0);
for (let i = 0; i < firstDay.getDay(); i++) {
const day = prevMonthEnd.getDate() - firstDay.getDay() + i + 1;
const date = new Date(year, month - 2, day);
week.push({
date,
day,
isCurrentMonth: false,
isToday: date.getTime() === today.getTime(),
isWeekend: date.getDay() === 0 || date.getDay() === 6
});
}
// Current month days
for (let day = 1; day <= lastDay.getDate(); day++) {
const date = new Date(year, month - 1, day);
week.push({
date,
day,
isCurrentMonth: true,
isToday: date.getTime() === today.getTime(),
isWeekend: date.getDay() === 0 || date.getDay() === 6
});
if (week.length === 7) {
weeks.push(week);
week = [];
}
}
// Next month days
let nextDay = 1;
while (week.length > 0 && week.length < 7) {
const date = new Date(year, month, nextDay);
week.push({
date,
day: nextDay++,
isCurrentMonth: false,
isToday: false,
isWeekend: date.getDay() === 0 || date.getDay() === 6
});
}
if (week.length) weeks.push(week);
return {
year,
month,
monthName: monthNames[month - 1],
daysInMonth: lastDay.getDate(),
firstDayOfWeek: firstDay.getDay(),
weeks
};
}
// Solution 8
function getTimeInTimezone(date, timezone) {
return date.toLocaleTimeString('en-US', {
timeZone: timezone,
hour: '2-digit',
minute: '2-digit',
timeZoneName: 'short'
});
}
function convertTimezone(date, fromTimezone, toTimezone) {
// Create formatter for each timezone
const fromFormatter = new Intl.DateTimeFormat('en-US', {
timeZone: fromTimezone,
year: 'numeric', month: '2-digit', day: '2-digit',
hour: '2-digit', minute: '2-digit', second: '2-digit',
hour12: false
});
// This is a simplified conversion - for accurate conversion,
// you'd need to parse and reconstruct the date
return date.toLocaleString('en-US', { timeZone: toTimezone });
}
function getTimezoneOffset(timezone, date = new Date()) {
const utcDate = new Date(date.toLocaleString('en-US', { timeZone: 'UTC' }));
const tzDate = new Date(date.toLocaleString('en-US', { timeZone: timezone }));
return (tzDate - utcDate) / (1000 * 60 * 60);
}
// Solution 9
function parseDate(dateString, format = null) {
if (!dateString) return null;
// Try ISO format first
const isoDate = new Date(dateString);
if (!isNaN(isoDate.getTime()) && dateString.includes('-')) {
return isoDate;
}
// US format: MM/DD/YYYY
const usMatch = dateString.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/);
if (usMatch && format !== 'DD/MM/YYYY') {
return new Date(usMatch[3], usMatch[1] - 1, usMatch[2]);
}
// European format: DD/MM/YYYY
if (format === 'DD/MM/YYYY') {
const euMatch = dateString.match(/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/);
if (euMatch) {
return new Date(euMatch[3], euMatch[2] - 1, euMatch[1]);
}
}
// Written formats
const writtenDate = new Date(dateString);
if (!isNaN(writtenDate.getTime())) {
return writtenDate;
}
return null;
}
// Solution 10
const DateUtils = {
isSame(date1, date2, unit) {
switch (unit) {
case 'year': return date1.getFullYear() === date2.getFullYear();
case 'month': return this.isSame(date1, date2, 'year') &&
date1.getMonth() === date2.getMonth();
case 'day': return this.isSame(date1, date2, 'month') &&
date1.getDate() === date2.getDate();
case 'hour': return this.isSame(date1, date2, 'day') &&
date1.getHours() === date2.getHours();
case 'minute': return this.isSame(date1, date2, 'hour') &&
date1.getMinutes() === date2.getMinutes();
default: return date1.getTime() === date2.getTime();
}
},
isBefore(date1, date2) {
return date1.getTime() < date2.getTime();
},
isAfter(date1, date2) {
return date1.getTime() > date2.getTime();
},
isToday(date) {
return this.isSame(date, new Date(), 'day');
},
isTomorrow(date) {
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
return this.isSame(date, tomorrow, 'day');
},
isYesterday(date) {
const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
return this.isSame(date, yesterday, 'day');
},
isWeekend(date) {
const day = date.getDay();
return day === 0 || day === 6;
},
isLeapYear(year) {
return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);
},
getDaysInMonth(year, month) {
return new Date(year, month, 0).getDate();
},
getQuarter(date) {
return Math.floor(date.getMonth() / 3) + 1;
},
getWeekNumber(date) {
const d = new Date(date);
d.setHours(0, 0, 0, 0);
d.setDate(d.getDate() + 4 - (d.getDay() || 7));
const yearStart = new Date(d.getFullYear(), 0, 1);
return Math.ceil((((d - yearStart) / 86400000) + 1) / 7);
},
format(date, formatStr) {
return formatDate(date, formatStr); // Uses solution 1
},
formatDistance(date1, date2) {
const diff = Math.abs(date2 - date1);
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
if (days < 1) return 'less than a day';
if (days === 1) return '1 day';
if (days < 30) return `${days} days`;
const months = Math.floor(days / 30);
if (months === 1) return '1 month';
if (months < 12) return `${months} months`;
const years = Math.floor(days / 365);
if (years === 1) return '1 year';
return `${years} years`;
}
};
*/