CakeDevelopment
CakeDevelopment

Reputation: 49

Time Counter Command giving NaN in discord.js

I am trying to make a command that counts the time between 2 commands. Example: !start (save the current time) - !end (saves the current time and get the difference between 2 commands). The export format of the latest time wants to be an embed that contains something like this: "Your time: 1:45:32 (hours:minutes:seconds)". Here is my code:

    bot.on("message", (message) => {


        var startDate;

        if (message.content == "!start") {
            startDate = new Date();

            console.log(new Date(startDate).getTime())

            message.reply("works");
        }

        if (message.content == "!end") {
            let endDate = new Date();

            
            console.log(new Date(endDate).getTime())


            let result = new Date(startDate).getTime() - new Date(endDate).getTime();


            message.reply(result)
        }
    });

The message that this code sends is "NaN"

Upvotes: 1

Views: 734

Answers (1)

Lauren Yim
Lauren Yim

Reputation: 14088

The bot is responding with NaN (which stands for Not a Number) probably because startDate is undefined, which means:

  • new Date(startDate) is an invalid date
  • new Date(startDate).getTime() is NaN
  • new Date(startDate).getTime() - new Date(endDate).getTime() is NaN (NaN - anything is always NaN)
  • result, which is NaN, gets coerced to a string and the message 'NaN' is sent.

What most likely happened is that a user sent !end before !start, so startDate is uninitialised.

To fix this, check if startDate is defined before continuing with the !end command:

if (message.content == "!end") {
    if (!startDate) return message.reply('Use !start first')

    // rest of code...
}

For more information on NaN, see NaN on MDN.


Some other things:

  • You don't need to use new Date(startDate) or new Date(endDate); startDate and endDate are already Dates.
  • Let's say someone sends !start and then !end. What happens if someone sends !end again? The bot would respond with the time since the !start to the last !end, which is probably unintended. You might want to set startDate to undefined after the !end command to reset it.
  • This command works across channels (someone can send !start in one channel and !end in another). If you don't want this, you could try storing the dates in something like a Map keyed by the channel IDs. (EDIT: it would probably be better to keep the timers unique per user, not per channel.)

Here's how I would implement it:

/**
 * A map from user IDs to start timestamps
 * @type Map<string, number>
 */
const startTimestamps = new Map()

/**
 * Pads a number to 2 digits.
 * @param {number} value
 * @returns {string}
 */
const pad2Digits = value => String(value).padStart(2, '0')

bot.on('message', async message => {
  try {
    if (message.content === '!start') {
      // Sets the start time. This overrides any existing timers
      // Date.now() is equivalent to new Date().getTime()
      startTimestamps.set(message.author.id, Date.now())
      await message.reply('Timer started.')
    } else if (message.content === '!end') {
      if (startTimestamps.has(message.author.id)) {
        // The user has an existing timer to stop
        // Calculate the timer result
        const ms = Date.now() - startTimestamps.get(message.author.id)
        const totalSecs = Math.floor(ms / 1000)
        const totalMins = Math.floor(totalSecs / 60)
        const hrs = Math.floor(totalMins / 60)
        const mins = totalMins % 60
        const secs = totalSecs % 60
        // Reply with result
        await message.reply(`Your time: ${hrs}:${pad2Digits(mins)}:${pad2Digits(secs)}`)
        // Remove timestamp from map
        startTimestamps.delete(message.author.id)
      } else {
        // The user does not have an existing timer
        await message.reply('You need to use `!start` first!')
      }
    }
  } catch (error) {
    console.error(error)
  }
})

Upvotes: 4

Related Questions