javascript

exercises

exercises.js
/**
 * 15.1 Date Fundamentals - Exercises
 *
 * Practice creating, manipulating, and working with JavaScript Date objects.
 */

/**
 * Exercise 1: Date Creator
 *
 * Create a function that creates a Date from various input formats.
 *
 * @param {string|number|Date|object} input - Date input
 *   - string: ISO format or readable format
 *   - number: milliseconds since epoch
 *   - Date: clone the date
 *   - object: { year, month, day, hour?, minute?, second?, ms? }
 *     Note: month should be 1-12 (human-readable), not 0-11
 * @returns {Date|null} - Date object or null if invalid
 */
function createDate(input) {
  // Your code here
}

// console.log(createDate("2024-12-25"));
// console.log(createDate(1735084800000));
// console.log(createDate(new Date()));
// console.log(createDate({ year: 2024, month: 12, day: 25 }));
// console.log(createDate({ year: 2024, month: 12, day: 25, hour: 10, minute: 30 }));
// console.log(createDate("invalid"));  // null

/**
 * Exercise 2: Date Components
 *
 * Create a function that extracts all components from a date.
 *
 * @param {Date} date - The date to analyze
 * @returns {object} - All date components
 */
function getDateComponents(date) {
  // Your code here
  // Return: {
  //   year, month (1-12), day, dayOfWeek (0-6),
  //   dayName, monthName, hour, minute, second, ms,
  //   timestamp, isLeapYear, daysInMonth, weekOfYear
  // }
}

// console.log(getDateComponents(new Date("2024-12-25T10:30:45.123")));
// {
//   year: 2024,
//   month: 12,
//   day: 25,
//   dayOfWeek: 3,
//   dayName: "Wednesday",
//   monthName: "December",
//   hour: 10,
//   minute: 30,
//   second: 45,
//   ms: 123,
//   timestamp: 1735123845123,
//   isLeapYear: true,
//   daysInMonth: 31,
//   weekOfYear: 52
// }

/**
 * Exercise 3: Date Validator
 *
 * Create a function that validates if a date is valid and within a range.
 *
 * @param {any} input - The value to validate
 * @param {object} options - Validation options
 * @returns {object} - { isValid, date, errors }
 */
function validateDate(input, options = {}) {
  // Your code here
  // Options can include:
  // - minDate: Date - minimum allowed date
  // - maxDate: Date - maximum allowed date
  // - allowFuture: boolean - allow future dates
  // - allowPast: boolean - allow past dates
}

// console.log(validateDate("2024-12-25"));
// { isValid: true, date: Date, errors: [] }

// console.log(validateDate("invalid"));
// { isValid: false, date: null, errors: ["Invalid date format"] }

// console.log(validateDate("2020-01-01", { allowPast: false }));
// { isValid: false, date: Date, errors: ["Date cannot be in the past"] }

/**
 * Exercise 4: Day/Month Boundaries
 *
 * Create functions to get the start and end of various time periods.
 */
function startOf(date, unit) {
  // Your code here
  // unit: 'day', 'week', 'month', 'year'
}

function endOf(date, unit) {
  // Your code here
  // unit: 'day', 'week', 'month', 'year'
}

// const date = new Date("2024-06-15T14:30:00");
// console.log(startOf(date, 'day'));    // 2024-06-15T00:00:00.000
// console.log(endOf(date, 'day'));      // 2024-06-15T23:59:59.999
// console.log(startOf(date, 'week'));   // 2024-06-09T00:00:00.000 (Sunday)
// console.log(startOf(date, 'month'));  // 2024-06-01T00:00:00.000
// console.log(endOf(date, 'month'));    // 2024-06-30T23:59:59.999
// console.log(startOf(date, 'year'));   // 2024-01-01T00:00:00.000
// console.log(endOf(date, 'year'));     // 2024-12-31T23:59:59.999

/**
 * Exercise 5: Date Cloning and Modification
 *
 * Create a function that clones a date and applies modifications.
 *
 * @param {Date} date - The original date
 * @param {object} changes - Changes to apply
 * @returns {Date} - New date with changes applied
 */
function modifyDate(date, changes) {
  // Your code here
  // changes can include: year, month, day, hour, minute, second, ms
  // Should not modify the original date
}

// const original = new Date("2024-06-15T10:30:00");
// console.log(modifyDate(original, { year: 2025 }));
// console.log(modifyDate(original, { month: 12, day: 25 }));
// console.log(modifyDate(original, { hour: 0, minute: 0, second: 0 }));
// console.log(original);  // Should be unchanged

/**
 * Exercise 6: Date Comparison Utilities
 *
 * Create functions for comparing dates.
 */
function isSameDay(date1, date2) {
  // Your code here
}

function isSameMonth(date1, date2) {
  // Your code here
}

function isSameYear(date1, date2) {
  // Your code here
}

function isBefore(date1, date2) {
  // Your code here
}

function isAfter(date1, date2) {
  // Your code here
}

function isBetween(date, start, end, inclusive = true) {
  // Your code here
}

// const d1 = new Date("2024-06-15");
// const d2 = new Date("2024-06-15");
// const d3 = new Date("2024-06-20");

// console.log(isSameDay(d1, d2));     // true
// console.log(isSameMonth(d1, d3));   // true
// console.log(isBefore(d1, d3));      // true
// console.log(isBetween(d1, new Date("2024-06-01"), new Date("2024-06-30"))); // true

/**
 * Exercise 7: Week Number Calculator
 *
 * Create a function that calculates the ISO week number.
 * ISO weeks start on Monday, and week 1 is the week containing January 4.
 *
 * @param {Date} date - The date
 * @returns {object} - { week: number, year: number }
 */
function getISOWeek(date) {
  // Your code here
}

// console.log(getISOWeek(new Date("2024-01-01"))); // { week: 1, year: 2024 }
// console.log(getISOWeek(new Date("2024-12-31"))); // { week: 1, year: 2025 }
// console.log(getISOWeek(new Date("2024-06-15"))); // { week: 24, year: 2024 }

/**
 * Exercise 8: Leap Year and Days
 *
 * Create functions for leap year calculations.
 */
function isLeapYear(year) {
  // Your code here
}

function getDaysInMonth(year, month) {
  // month: 1-12
  // Your code here
}

function getDaysInYear(year) {
  // Your code here
}

function getDayOfYear(date) {
  // Returns 1-366
  // Your code here
}

// console.log(isLeapYear(2024));       // true
// console.log(isLeapYear(2023));       // false
// console.log(isLeapYear(2000));       // true
// console.log(isLeapYear(1900));       // false
// console.log(getDaysInMonth(2024, 2)); // 29 (leap year)
// console.log(getDaysInMonth(2023, 2)); // 28
// console.log(getDaysInYear(2024));    // 366
// console.log(getDayOfYear(new Date("2024-12-31"))); // 366

/**
 * Exercise 9: Date Sorting
 *
 * Create a function that sorts an array of dates or objects with dates.
 *
 * @param {Array} items - Array of dates or objects
 * @param {string} order - 'asc' or 'desc'
 * @param {string} key - Property name if items are objects
 * @returns {Array} - Sorted array (new array, doesn't modify original)
 */
function sortByDate(items, order = 'asc', key = null) {
  // Your code here
}

// const dates = [
//   new Date("2024-06-15"),
//   new Date("2024-01-01"),
//   new Date("2024-12-31")
// ];
// console.log(sortByDate(dates, 'asc'));
// console.log(sortByDate(dates, 'desc'));

// const events = [
//   { name: "Event A", date: new Date("2024-06-15") },
//   { name: "Event B", date: new Date("2024-01-01") },
//   { name: "Event C", date: new Date("2024-12-31") }
// ];
// console.log(sortByDate(events, 'asc', 'date'));

/**
 * Exercise 10: Timezone Utilities
 *
 * Create functions for working with timezones.
 */
function getTimezoneInfo() {
  // Return info about current timezone
  // Your code here
}

function toUTC(date) {
  // Convert local date to UTC date string
  // Your code here
}

function fromUTC(utcString) {
  // Convert UTC string to local Date
  // Your code here
}

function convertTimezone(date, fromOffset, toOffset) {
  // Convert between timezone offsets (in hours)
  // Your code here
}

// console.log(getTimezoneInfo());
// { name: "America/New_York", offset: -5, offsetMinutes: -300 }

// console.log(toUTC(new Date()));
// "2024-06-15T18:30:00.000Z"

// console.log(convertTimezone(new Date(), -5, +9));  // EST to JST

// ============================================
// SOLUTIONS
// ============================================

/*
// Solution 1
function createDate(input) {
    if (input === null || input === undefined) {
        return null;
    }
    
    let date;
    
    if (input instanceof Date) {
        date = new Date(input.getTime());
    } else if (typeof input === 'number') {
        date = new Date(input);
    } else if (typeof input === 'string') {
        date = new Date(input);
    } else if (typeof input === 'object') {
        const { year, month, day, hour = 0, minute = 0, second = 0, ms = 0 } = input;
        // Convert month from 1-12 to 0-11
        date = new Date(year, month - 1, day, hour, minute, second, ms);
    } else {
        return null;
    }
    
    return isNaN(date.getTime()) ? null : date;
}

// Solution 2
function getDateComponents(date) {
    const dayNames = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
    const monthNames = ['January', 'February', 'March', 'April', 'May', 'June',
                        'July', 'August', 'September', 'October', 'November', 'December'];
    
    const year = date.getFullYear();
    const month = date.getMonth() + 1;
    
    // Check leap year
    const isLeapYear = (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);
    
    // Days in month
    const daysInMonth = new Date(year, date.getMonth() + 1, 0).getDate();
    
    // Week of year (simple calculation)
    const startOfYear = new Date(year, 0, 1);
    const daysSinceStart = Math.floor((date - startOfYear) / (1000 * 60 * 60 * 24));
    const weekOfYear = Math.ceil((daysSinceStart + startOfYear.getDay() + 1) / 7);
    
    return {
        year,
        month,
        day: date.getDate(),
        dayOfWeek: date.getDay(),
        dayName: dayNames[date.getDay()],
        monthName: monthNames[date.getMonth()],
        hour: date.getHours(),
        minute: date.getMinutes(),
        second: date.getSeconds(),
        ms: date.getMilliseconds(),
        timestamp: date.getTime(),
        isLeapYear,
        daysInMonth,
        weekOfYear
    };
}

// Solution 3
function validateDate(input, options = {}) {
    const errors = [];
    let date = null;
    
    // Try to create date
    if (input instanceof Date) {
        date = input;
    } else {
        date = new Date(input);
    }
    
    // Check if valid
    if (isNaN(date.getTime())) {
        return { isValid: false, date: null, errors: ['Invalid date format'] };
    }
    
    const now = new Date();
    
    // Check minDate
    if (options.minDate && date < options.minDate) {
        errors.push(`Date cannot be before ${options.minDate.toDateString()}`);
    }
    
    // Check maxDate
    if (options.maxDate && date > options.maxDate) {
        errors.push(`Date cannot be after ${options.maxDate.toDateString()}`);
    }
    
    // Check allowFuture
    if (options.allowFuture === false && date > now) {
        errors.push('Date cannot be in the future');
    }
    
    // Check allowPast
    if (options.allowPast === false && date < now) {
        errors.push('Date cannot be in the past');
    }
    
    return {
        isValid: errors.length === 0,
        date,
        errors
    };
}

// Solution 4
function startOf(date, unit) {
    const result = new Date(date);
    
    switch (unit) {
        case 'day':
            result.setHours(0, 0, 0, 0);
            break;
        case 'week':
            result.setHours(0, 0, 0, 0);
            result.setDate(result.getDate() - result.getDay());
            break;
        case 'month':
            result.setDate(1);
            result.setHours(0, 0, 0, 0);
            break;
        case 'year':
            result.setMonth(0, 1);
            result.setHours(0, 0, 0, 0);
            break;
    }
    
    return result;
}

function endOf(date, unit) {
    const result = new Date(date);
    
    switch (unit) {
        case 'day':
            result.setHours(23, 59, 59, 999);
            break;
        case 'week':
            result.setDate(result.getDate() + (6 - result.getDay()));
            result.setHours(23, 59, 59, 999);
            break;
        case 'month':
            result.setMonth(result.getMonth() + 1, 0);
            result.setHours(23, 59, 59, 999);
            break;
        case 'year':
            result.setMonth(11, 31);
            result.setHours(23, 59, 59, 999);
            break;
    }
    
    return result;
}

// Solution 5
function modifyDate(date, changes) {
    const result = new Date(date.getTime());
    
    if (changes.year !== undefined) result.setFullYear(changes.year);
    if (changes.month !== undefined) result.setMonth(changes.month - 1);
    if (changes.day !== undefined) result.setDate(changes.day);
    if (changes.hour !== undefined) result.setHours(changes.hour);
    if (changes.minute !== undefined) result.setMinutes(changes.minute);
    if (changes.second !== undefined) result.setSeconds(changes.second);
    if (changes.ms !== undefined) result.setMilliseconds(changes.ms);
    
    return result;
}

// Solution 6
function isSameDay(date1, date2) {
    return date1.getFullYear() === date2.getFullYear() &&
           date1.getMonth() === date2.getMonth() &&
           date1.getDate() === date2.getDate();
}

function isSameMonth(date1, date2) {
    return date1.getFullYear() === date2.getFullYear() &&
           date1.getMonth() === date2.getMonth();
}

function isSameYear(date1, date2) {
    return date1.getFullYear() === date2.getFullYear();
}

function isBefore(date1, date2) {
    return date1.getTime() < date2.getTime();
}

function isAfter(date1, date2) {
    return date1.getTime() > date2.getTime();
}

function isBetween(date, start, end, inclusive = true) {
    const time = date.getTime();
    const startTime = start.getTime();
    const endTime = end.getTime();
    
    if (inclusive) {
        return time >= startTime && time <= endTime;
    }
    return time > startTime && time < endTime;
}

// Solution 7
function getISOWeek(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);
    const weekNumber = Math.ceil((((d - yearStart) / 86400000) + 1) / 7);
    
    return { week: weekNumber, year: d.getFullYear() };
}

// Solution 8
function isLeapYear(year) {
    return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);
}

function getDaysInMonth(year, month) {
    return new Date(year, month, 0).getDate();
}

function getDaysInYear(year) {
    return isLeapYear(year) ? 366 : 365;
}

function getDayOfYear(date) {
    const start = new Date(date.getFullYear(), 0, 0);
    const diff = date - start;
    const oneDay = 1000 * 60 * 60 * 24;
    return Math.floor(diff / oneDay);
}

// Solution 9
function sortByDate(items, order = 'asc', key = null) {
    const sorted = [...items].sort((a, b) => {
        const dateA = key ? a[key] : a;
        const dateB = key ? b[key] : b;
        return dateA.getTime() - dateB.getTime();
    });
    
    return order === 'desc' ? sorted.reverse() : sorted;
}

// Solution 10
function getTimezoneInfo() {
    const offset = new Date().getTimezoneOffset();
    const offsetHours = -offset / 60;
    
    // Try to get timezone name
    let name;
    try {
        name = Intl.DateTimeFormat().resolvedOptions().timeZone;
    } catch {
        name = 'Unknown';
    }
    
    return {
        name,
        offset: offsetHours,
        offsetMinutes: -offset
    };
}

function toUTC(date) {
    return date.toISOString();
}

function fromUTC(utcString) {
    return new Date(utcString);
}

function convertTimezone(date, fromOffset, toOffset) {
    const utc = date.getTime() + (fromOffset * 60 * 60 * 1000);
    return new Date(utc - (toOffset * 60 * 60 * 1000));
}
*/
Exercises - JavaScript Tutorial | DeepML