Last updated on

Características de tiempo nativas de JavaScript y HTML5


Recientemente, colaboré con una empresa en un prototipo que necesitaba mostrar datos de sensores en gráficos, lo que involucró algunas conversiones de tiempo, pero solo en los siguientes formatos comunes:

  1. Comunicarse con la API de la plataforma IoT de Chunghwa Telecom, que requiere datos en formato ISO8601 UTC.
  2. El formato de la hora local del navegador.
  3. El formato de cadena del selector de fecha integrado del navegador.

Debido a que no involucra otras zonas horarias, no usé ningún paquete y usé directamente JavaScript nativo para manejarlo, y organizaré los conceptos que aprendí esta vez.

Timestamp

Para manejar el tiempo, primero necesitas entender el concepto de timestamp. En el mundo de la información, el método de registrar el tiempo establece el punto de partida en el 1 de enero de 1970, 0:00:00:000.

Incluso si no has escrito JavaScript, puedes intentar esto: abre el navegador Chrome, presiona Fn + F12 (en Windows, solo presiona F12), haz clic en consola, luego en la consola que aparece, escribe o pega Date.now(), presiona Enter, y puedes obtener cuántos milisegundos han pasado desde 1970 hasta ahora (1625798004061 milisegundos). Luego puedes decirle a otros que has escrito JavaScript y no es Hello World (solo bromeo).

timestamp

Ten en cuenta que este timestamp usa hora UTC (misma zona horaria que el Observatorio de Greenwich), por lo que no importa en qué zona horaria estés, obtendrás el mismo timestamp. Tu teléfono sabrá que estás en Taipei a través de GPS o posicionamiento de red según tu zona horaria, y luego calculará la hora correcta de Taipei agregando 8 horas al timestamp, mostrándolo en tu teléfono.

Zona horaria y tipos

Las personas con experiencia en programación conocen la importancia de los tipos. Este proyecto mío usa tres tipos de conversiones: número, objeto y cadena.

El Date.now() mencionado anteriormente devuelve un tipo número. Si quieres convertirlo a un tipo objeto Date, puedes usar new Date(1625798004061), que se mostrará en la zona horaria del navegador, dándote Fri Jul 09 2021 10:33:24 GMT+0800 (Taipei Standard Time).

new Date() sin parámetros puede obtener directamente el objeto Date actual. Puedes intentar poner diferentes parámetros de formato en él, pero todos devolverán un objeto Date. Por ejemplo, puedes poner una cadena en este formato: new Date('2021-07-09'), o un número en este formato: new Date(2021,7,9).

Si usas JSX (o HTML5) <input type="date" onChange={this.onChange} />, que es el selector de fecha integrado del navegador, el valor que obtienes o las props que le das serán una cadena de tiempo en la zona horaria del navegador, por ejemplo: '2021-07-09'. Hablaremos de esto más adelante.

Para este proyecto en el que he estado trabajando, la API de Chunghwa Telecom requiere formato ISO8601 UTC, que es un tipo de cadena, así: 2021-07-09T02:33:24Z. Ten en cuenta que esta zona horaria es la misma que el timestamp, hora del Observatorio de Greenwich, no la zona horaria donde está tu teléfono.

Conversiones

Con los conceptos de tipos y zonas horarias anteriores, podemos practicar algunas conversiones comunes. Cosas a tener en cuenta:

  1. Para los métodos que practicaremos a continuación, ten en cuenta que deben llamarse en objetos Date. Si descubres que no puedes usarlos, probablemente los estés llamando en un número o cadena.
    • .setHours()
    • .toISOString()
    • .getDate() y .setDate()
    • .toLocaleDateString();
    • .getFullYear()
    • .getMonth()
  2. Los valores de retorno de estos métodos a menudo no son objetos Date, por lo que necesitas convertirlos de vuelta a objetos Date usando new Date() para continuar usando estos métodos.

Práctica 1: Conversión de formato ISO8601 UTC

El objetivo es convertir un objeto Date al formato requerido por Chunghwa Telecom para que pueda usarse para enviar solicitudes a Chunghwa Telecom.

    const today = new Date();
    const requestTime = new Date(today.setHours(0, 0, 0)).toISOString().slice(0, 19) + 'Z';
    console.log('Formato requerido por Chunghwa Telecom:',requestTime)
    //Formato requerido por Chunghwa Telecom: 2021-07-09T02:33:24Z

Debido a que today es un objeto Date, podemos usar .setHours(0,0,0) para establecer la hora Fri Jul 09 2021 10:33:24 GMT+0800 (Taipei Standard Time) al inicio del día Fri Jul 09 2021 0:0:0 GMT+0800 (Taipei Standard Time).

Aquí, .setHours() no devuelve un objeto Date, sino un número, por lo que necesitamos volver a ponerlo en new Date() para convertirlo de vuelta a un objeto Date antes de poder usar el siguiente método .toISOString().

Después de .toISOString(), está muy cerca del formato que Chunghwa Telecom quiere. Es una cadena de zona horaria UTC. Usa .slice() para eliminar la parte después del punto decimal y agrega Z, y tienes el formato correcto: 2021-07-09T02:33:24Z.

Práctica 2: Cálculos de fecha

Otra razón importante para usar objetos Date para el tiempo es que es conveniente para los cálculos. Por ejemplo, si escribo código para calcular la fecha de ayer, la situación es diferente si hoy resulta ser el 1 de julio o el 2 de julio, y necesitaría considerarlos por separado, lo cual es molesto. Pero usar objetos Date y sus métodos lo hace muy conveniente.

    const firstDay = new Date('2021-07-01');
    const oneDaysBeforeFirstDay = new Date(firstDay.setDate(firstDay.getDate()-1));
    console.log(oneDaysBeforeFirstDay);
    //Wed Jun 30 2021 08:00:00 GMT+0800 (Taipei Standard Time)

Al calcular el día antes del 1 de julio, .getDate() obtiene el número 1 para el 1 de julio. Después de que restamos 1, se convierte en 0, y .setDate() sabrá inteligentemente que 0 es el último día del mes anterior, por lo que después de restar directamente 1 del 1 de julio, la fecha calculada es el 30 de junio.

Aquí, recuerda que .setDate() también devuelve un tipo número de timestamp, y necesita pasar por new Date() para convertir a un objeto Date.

Práctica 3: Convertir objetos Date a cadenas

.toLocaleDateString() puede convertir una fecha a un formato de cadena como 2017/7/9, pero si quieres usarlo para un selector de fecha HTML5, no funcionará directamente porque necesita un formato de cadena como 2017-07-09. Solo pude pensar en hacer esta conversión manualmente. Si alguien tiene otros métodos, por favor házmelo saber.

El método es usar .getFullYear(), .getMonth(), y .getDate() por separado para obtener el año, mes y día como números, luego usar .toString() para convertir los números a cadenas. También necesitas verificar si el número obtenido originalmente es de un dígito o dos dígitos. Si es de un dígito, necesitas agregar una cadena 0 al frente.

Además, para .getMonth(), necesitas prestar especial atención a un detalle. Como las reglas de índice de matriz, si el número que obtienes es 6, eso representa julio; si el número es 11, eso representa diciembre. Por lo tanto, necesitas agregar 1 al número que obtienes para convertir correctamente.

  //Convertir objeto de tiempo a formato compatible con selector de fecha
  formatDate = (dateOriginal) => {
    //Obtener año
    const year = dateOriginal.getFullYear().toString();
    //Obtener mes, los números de mes en JS comienzan desde 0, por lo que se necesita +1
    //Si solo tiene un dígito, agregar 0 al frente
    const month =
      (dateOriginal.getMonth() + 1).toString().length === 1 ?
        '0' + (dateOriginal.getMonth() + 1).toString()
        :
        (dateOriginal.getMonth() + 1).toString();
    //Obtener día
    //Si solo tiene un dígito, agregar 0 al frente
    const day = dateOriginal.getDate().toString().length === 1 ?
      '0' + dateOriginal.getDate().toString()
      :
      dateOriginal.getDate().toString()
    //Combinar año, mes, día
    const dateFormatted = year + '-' + month + '-' + day;
    return dateFormatted
  }
  const today = new Date();
  const dateForInput = formatDate(today);
  console.log('Hoy es',dateForInput);
  //Hoy es 2021-07-09

Selector de fecha

Después de convertir el formato usando la función de ejemplo personalizada anterior, podemos usarlo para establecer el selector de fecha compatible con HTML5. Por ejemplo, si hoy es el 16 de julio, limita las fechas que este selector puede seleccionar desde el 28 de junio hasta ayer (props max y min). El código del selector de fecha es el siguiente:

<input
  className="mv2 ba"
  type="date"
  placeholder="yyyy-mm-dd"
  value={this.state.selectedDate}
  onChange={this.onChange}
  max={this.formatDate(yesterday)}
  min={'2021-06-28'}
/>        
Selector de fecha

Sin embargo, la UI de este selector de fecha es proporcionada por el navegador. Aunque iOS Safari proporciona una UI, Safari de escritorio no, por lo que en Mac, lo que aparecerá será una interfaz donde necesitas escribir con el teclado. Si no eres como yo, solo haciendo temporalmente un prototipo, puedes encontrar algunos paquetes existentes para usar, lo que facilitará controlar la interfaz en diferentes versiones de navegadores.

Conversiones para otras zonas horarias

Si tus necesidades involucran conversiones de otras zonas horarias, puedes consultar este artículo: Usar JavaScript nativo para calcular tiempos en varias zonas horarias. También hay algunos paquetes poderosos y útiles disponibles, como el famoso Moment.js. Si te preocupa que Moment.js ya no se mantiene, puedes usar el más nuevo luxon.

Pensamientos

El manejo de tiempo de HTML5 y JavaScript nativo parece un poco tedioso, pero conceptualmente no es demasiado complejo. Podría ser bastante adecuado para que los principiantes de JavaScript practiquen, y también puedes aprender que el proceso de programación a menudo requiere conversiones entre diferentes tipos y formatos requeridos. Si alguien tiene diferentes enfoques, no dudes en compartir.

Actualización

Respecto al formatDate() mencionado anteriormente, alguien en Front-End Developers Taiwan proporcionó una buena alternativa. Puedes ir a revisarlo. Mi enfoque personal favorito es el siguiente:

const formatDate = (dateOriginal) => `${dateOriginal.toLocaleString('en', {year: 'numeric'})}-${dateOriginal.toLocaleString('en', {month: '2-digit'})}-${dateOriginal.toLocaleString('en', {day: '2-digit'})}`