Migración de Joda-Time a java.time
Migrar código (léase código heredado) no es divertido. Requiere una enorme cantidad de planificación y esfuerzo para llevarlo a cabo. Aunque no es el trabajo más emocionante o motivador para los desarrolladores, se requiere determinación y la experiencia adecuada para migrar el código heredado a las nuevas versiones de las bibliotecas. Joda-Time a java.time es una de esas migraciones que requiere una planificación y ejecución meticulosas.
Si su proyecto Java comenzó su vida antes de Java SE 8, y utiliza el procesamiento de fecha/hora, entonces probablemente utilizó Joda-Time - una excelente biblioteca y un estándar de facto para manejar las funciones de fecha y hora antes de SE 8. Si su proyecto todavía utiliza Joda-Time pero le gustaría migrar a java.time entonces siga leyendo.
El lanzamiento de Java SE 8 incluyó una nueva y mejorada API de fecha y hora estándar comúnmente conocida como java.time (JSR-310). El proyecto Joda-Time recomienda ahora migrar a java.time (JSR-310).
Aunque java.time (JSR-310) se inspiró en gran medida en Joda-Time, no es compatible con versiones anteriores y los conceptos y la terminología han cambiado. Por eso, la migración de Joda-Time a java.time requiere una cuidadosa atención a cada línea de código que se cambie. Esto puede llevar mucho tiempo y casi te haría desear que hubiera una forma más fácil y automatizada de migrar.
Hay una forma mejor de migrar y la hemos creado usando Sensei - un plugin de IntelliJ para realizar automáticamente transformaciones de código según las recetas (reglas) definidas por usted. Dedique su tiempo a definir recetas reutilizables, en lugar de realizar tareas de migración repetitivas. La automatización no sólo transformará su código heredado de Joda-Time, sino que también ayudará a los equipos a seguir las directrices allí mismo, en el IDE, mientras escriben el nuevo código.
Para ayudarte a tener una ventaja, hemos creado un libro de cocina público Sensei Standardization on java.time (JSR-310) que incluye recetas para migrar de Joda-Time a java.time de una manera menos dolorosa. Se trata de un conjunto creciente de recetas que seguiremos ampliando para añadir más cobertura con más recetas.
Este es un ejemplo de una migración de muestra que puede ayudarle a ver cómo Sensei facilita la migración de código heredado.
De la migración manual repetitiva a las transformaciones de código automatizadas
Veamos un ejemplo de creación de un nuevo DateTime que demuestra unas cuantas trampas ocultas al migrar una sola línea de código de Joda-Time a java.time. A continuación, veremos una de nuestras recetas de Sensei de nuestro libro de cocina Standardization on java.time (JSR-310) y mostraremos cómo captura toda esta información, para que esta misma migración pueda ser reutilizada una y otra vez por cualquier desarrollador.
En este ejemplo, estamos construyendo un Joda-Time DateTime a partir de 7 argumentos int que representan valores de los campos DateTime.
¿Cómo podemos migrar esto a un equivalente de java.time?
El javadoc en Joda-Time para este constructor dice:
Construye una instancia a partir de los valores de los campos datetime utilizando ISOChronology en la zona horaria por defecto.
Al principio, podríamos suponer que hay una clase DateTime en java.time, pero no la hay. Si buscas en Google "migrar de joda time a java time" es muy probable que encuentres la entrada del blog de Stephen Colebourne Converting from Joda-Time to java.time.
Esto le da un buen comienzo, y nos apunta en la dirección de usar java.time.ZonedDateTime o java.time.OffsetDateTime. Aquí está nuestra primera pregunta, ¿cuál utilizo? Probablemente ZonedDateTime basado en los comentarios de Stephen.
Buscando en el javadoc de ZonedDateTime, no vemos ningún constructor. Volviendo a la entrada del blog de Stephen leemos más abajo:
Construcción. Joda-Time tiene un constructor que acepta un objeto y realiza la conversión de tipo. java.time sólo tiene métodos de fábrica, por lo que la conversión es un problema del usuario, aunque se proporciona un método parse() para las cadenas.
Así que debe haber un método estático de fábrica, buscando en los métodos estáticos encontramos uno que se parece bastante, pero no es exactamente igual.
Tiene 7 parámetros int como nuestro constructor Joda-Time DateTime original, sin embargo si no prestas atención te perderás un detalle importante. El 7º parámetro ya no representa milisegundos, en su lugar espera nanosegundos, esto se debe a que java.time ha aumentado la precisión respecto a Joda-Time y mide los instantes al nano-segundo. Un detalle importante que podría haber pasado fácilmente por alto. Además, este método espera un ZoneId, por lo que hace que te preguntes por qué no lo necesitabas antes y por qué lo necesitas ahora.
Recordando el javadoc de nuestro constructor original que mencionaba que usaría la zona horaria por defecto, ¿quizás haya una forma de obtener el ZoneId por defecto?
El javadoc de ZoneId no nos habla de ningún constructor listado, pero mirando los métodos estáticos vemos que podemos usar systemDefault()
Ahora que hemos resuelto el ZoneId, ¿qué debemos hacer con nuestra conversión de milisegundos a nanoSegundos? Tal vez podamos utilizar java.util.concurrent.TimeUnit para realizar la conversión.
Este método devuelve un long, y nuestro método espera un int, así que ahora también tenemos un problema de conversión que resolver. Tal vez podríamos intentar algo sencillo. ¿Una multiplicación?
Esto funcionará pero parece un poco fuera de lugar. Por si no lo has notado ya, hemos invertido una cantidad considerable de tiempo y esfuerzo para migrar una sola línea de código. Pero como puedes imaginar, tenemos muchas ediciones de este tipo que hacer a mano y no mejora.
Sin embargo, si miramos un poco más a la API de java.time, podemos descubrir una solución que parece un poco más fluida.
Aunque ZonedDateTime no tiene una forma obvia de establecer los milisegundos, puede hacerse mediante el método with(TemporalField field, long newValue), utilizando ChronoField.MILLI_OF_SECOND como TemporalField.
Y la documentación de java menciona que realizará la conversión a nanosegundos por nosotros:
Cuando este campo se utiliza para establecer un valor, debe comportarse de la misma manera que establecer NANO_OF_SECOND con el valor multiplicado por 1.000.000.
Así que podemos simplemente especificar 0 para nuestros nanosegundos en el método de fábrica, y luego usar el método with para crear un ZonedDateTime que tenga todos los valores originales así como los milisegundos.
Mirando nuestro resultado final, parece que sólo hemos cambiado una línea de código, ¡no muestra realmente el esfuerzo que ha supuesto investigar una sola migración!
Cree una receta para migrar más rápida y fácilmente
Sensei nos ofrece una forma de compartir con otros desarrolladores esta información que tanto nos ha costado conseguir. Al crear una receta que recoge todos estos requisitos, permitirá a los usuarios de Sensei realizar esta migración con un clic del ratón.
Una receta de Sensei consta de 3 secciones principales:
- Metadatos
- Buscar en
- Arreglos disponibles
Veamos una receta de Sensei (también puede verse como una receta YAML) que nos ayudará a migrar esta llamada a su equivalente java.time.
DateTime foo = new DateTime(year, monthOfYear, dayOfMonth, hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond);
Sección de metadatos
La sección de metadatos contiene información sobre la receta y cómo debe utilizarse.
Sección de búsqueda
La sección de búsqueda de una receta de Sensei especifica a qué elementos de código debe aplicarse esta receta.
search:
instanceCreation:
args:
1:
type: int
2:
type: int
3:
type: int
4:
type: int
5:
type: int
6:
type: int
7:
type: int
argCount: 7
type: org.joda.time.DateTime
En esta sección de búsqueda vemos que estamos:
- Buscar una instanceCreation, es decir, un uso de un Constructor. Nota: hay muchos otros objetivos de búsqueda disponibles
- El constructor debe tener 7 argumentos, esto se especifica con la propiedad argCount
- los argumentos 1-7 deben ser de tipo int
- Buscamos constructores del tipo org.joda.time.DateTime
Sección de arreglos disponibles
La sección availableFixes puede especificar una o más correcciones que pueden aplicarse al elemento de código correspondiente. Cada fix puede tener múltiples acciones, y en nuestro caso tenemos un único fix, que realiza 2 acciones.
- El nombre de la corrección se muestra al usuario en el menú "Quickfixes", y describe lo que ocurrirá si el usuario aplica esta quickfix
- La lista de acciones muestra las acciones que realizará este quickfix
- La acción de reescritura reescribirá el elemento de código utilizando una plantilla mustache. Puede hacer uso de variables y funciones de sustitución de cadenas.
- La acción modifyAssignedVariable comprobará si este constructor se está utilizando para asignar el valor a una variable. Si es así, esta acción modificará la variable para que sea declarada como el tipo especificado por type
Uso de la receta para la transformación del código
Con nuestra receta escrita y habilitada, escanea nuestro código y destaca los segmentos a los que se puede aplicar.
En la captura de pantalla de abajo podemos ver que el constructor de destino ha sido marcado por Sensei. Al pasar el ratón por encima del constructor marcado nos muestra la receta shortDescription y la opción Quickfix Migrate to java.time.ZonedDateTime
Después de seleccionar el quickfix Migrate to java.time.ZonedDateTime, el código se transforma según las acciones que especificamos en la receta.
Una migración única y prácticas de codificación uniformes en todos los equipos, con Sensei
Podemos ver en nuestro ejemplo anterior que la migración de una sola línea de código puede implicar un conocimiento duramente ganado. Sensei puede convertir ese conocimiento en recetas o libros de cocina procesables que pueden ser compartidos dentro de los equipos. Puedes planificar un sprint de migración único o adoptar el enfoque de hacer transformaciones incrementales instantáneas a java.time a medida que te encuentres con código Joda-Time. Puede habilitar/deshabilitar recetas como una forma de hacer migraciones en etapas o pasos lógicos e incluso, ampliar o reducir el alcance de los archivos escaneados por Sensei - la flexibilidad que hace que las migraciones de código sean menos dolorosas.
La migración de librerías es sólo un ejemplo de las muchas maneras en que Sensei puede utilizarse para estandarizar tus proyectos. Siempre puedes estar atento a los antipatrones o a ciertas transformaciones manuales de código que encuentres con frecuencia en pull requests o mientras codificas tú mismo. Si tienes un conjunto de directrices de codificación que los desarrolladores no siguen a menudo, puedes convertir las directrices en recetas, permitiendo a los desarrolladores aplicar transformaciones de código aprobadas con confianza.
Si tienes alguna pregunta, nos encantaría que nos la hicieras llegar. Únase a nosotros en Slack en: sensei-scw.slack.com
Cameron es un desarrollador de software senior en Secure Code Warrior. Tiene más de 15 años de experiencia en la entrega de software y es un apasionado de la productividad de los desarrolladores y contribuye activamente al software de código abierto.
Secure Code Warrior está a disposición de su organización para ayudarle a proteger el código a lo largo de todo el ciclo de vida de desarrollo de software y crear una cultura en la que la ciberseguridad sea una prioridad. Tanto si es director de AppSec, desarrollador, CISO o cualquier persona implicada en la seguridad, podemos ayudar a su organización a reducir los riesgos asociados a un código inseguro.
Reservar una demostraciónCameron es un desarrollador de software senior en Secure Code Warrior. Tiene más de 15 años de experiencia en la entrega de software y es un apasionado de la productividad de los desarrolladores y contribuye activamente al software de código abierto.
Migrar código (léase código heredado) no es divertido. Requiere una enorme cantidad de planificación y esfuerzo para llevarlo a cabo. Aunque no es el trabajo más emocionante o motivador para los desarrolladores, se requiere determinación y la experiencia adecuada para migrar el código heredado a las nuevas versiones de las bibliotecas. Joda-Time a java.time es una de esas migraciones que requiere una planificación y ejecución meticulosas.
Si su proyecto Java comenzó su vida antes de Java SE 8, y utiliza el procesamiento de fecha/hora, entonces probablemente utilizó Joda-Time - una excelente biblioteca y un estándar de facto para manejar las funciones de fecha y hora antes de SE 8. Si su proyecto todavía utiliza Joda-Time pero le gustaría migrar a java.time entonces siga leyendo.
El lanzamiento de Java SE 8 incluyó una nueva y mejorada API de fecha y hora estándar comúnmente conocida como java.time (JSR-310). El proyecto Joda-Time recomienda ahora migrar a java.time (JSR-310).
Aunque java.time (JSR-310) se inspiró en gran medida en Joda-Time, no es compatible con versiones anteriores y los conceptos y la terminología han cambiado. Por eso, la migración de Joda-Time a java.time requiere una cuidadosa atención a cada línea de código que se cambie. Esto puede llevar mucho tiempo y casi te haría desear que hubiera una forma más fácil y automatizada de migrar.
Hay una forma mejor de migrar y la hemos creado usando Sensei - un plugin de IntelliJ para realizar automáticamente transformaciones de código según las recetas (reglas) definidas por usted. Dedique su tiempo a definir recetas reutilizables, en lugar de realizar tareas de migración repetitivas. La automatización no sólo transformará su código heredado de Joda-Time, sino que también ayudará a los equipos a seguir las directrices allí mismo, en el IDE, mientras escriben el nuevo código.
Para ayudarte a tener una ventaja, hemos creado un libro de cocina público Sensei Standardization on java.time (JSR-310) que incluye recetas para migrar de Joda-Time a java.time de una manera menos dolorosa. Se trata de un conjunto creciente de recetas que seguiremos ampliando para añadir más cobertura con más recetas.
Este es un ejemplo de una migración de muestra que puede ayudarle a ver cómo Sensei facilita la migración de código heredado.
De la migración manual repetitiva a las transformaciones de código automatizadas
Veamos un ejemplo de creación de un nuevo DateTime que demuestra unas cuantas trampas ocultas al migrar una sola línea de código de Joda-Time a java.time. A continuación, veremos una de nuestras recetas de Sensei de nuestro libro de cocina Standardization on java.time (JSR-310) y mostraremos cómo captura toda esta información, para que esta misma migración pueda ser reutilizada una y otra vez por cualquier desarrollador.
En este ejemplo, estamos construyendo un Joda-Time DateTime a partir de 7 argumentos int que representan valores de los campos DateTime.
¿Cómo podemos migrar esto a un equivalente de java.time?
El javadoc en Joda-Time para este constructor dice:
Construye una instancia a partir de los valores de los campos datetime utilizando ISOChronology en la zona horaria por defecto.
Al principio, podríamos suponer que hay una clase DateTime en java.time, pero no la hay. Si buscas en Google "migrar de joda time a java time" es muy probable que encuentres la entrada del blog de Stephen Colebourne Converting from Joda-Time to java.time.
Esto le da un buen comienzo, y nos apunta en la dirección de usar java.time.ZonedDateTime o java.time.OffsetDateTime. Aquí está nuestra primera pregunta, ¿cuál utilizo? Probablemente ZonedDateTime basado en los comentarios de Stephen.
Buscando en el javadoc de ZonedDateTime, no vemos ningún constructor. Volviendo a la entrada del blog de Stephen leemos más abajo:
Construcción. Joda-Time tiene un constructor que acepta un objeto y realiza la conversión de tipo. java.time sólo tiene métodos de fábrica, por lo que la conversión es un problema del usuario, aunque se proporciona un método parse() para las cadenas.
Así que debe haber un método estático de fábrica, buscando en los métodos estáticos encontramos uno que se parece bastante, pero no es exactamente igual.
Tiene 7 parámetros int como nuestro constructor Joda-Time DateTime original, sin embargo si no prestas atención te perderás un detalle importante. El 7º parámetro ya no representa milisegundos, en su lugar espera nanosegundos, esto se debe a que java.time ha aumentado la precisión respecto a Joda-Time y mide los instantes al nano-segundo. Un detalle importante que podría haber pasado fácilmente por alto. Además, este método espera un ZoneId, por lo que hace que te preguntes por qué no lo necesitabas antes y por qué lo necesitas ahora.
Recordando el javadoc de nuestro constructor original que mencionaba que usaría la zona horaria por defecto, ¿quizás haya una forma de obtener el ZoneId por defecto?
El javadoc de ZoneId no nos habla de ningún constructor listado, pero mirando los métodos estáticos vemos que podemos usar systemDefault()
Ahora que hemos resuelto el ZoneId, ¿qué debemos hacer con nuestra conversión de milisegundos a nanoSegundos? Tal vez podamos utilizar java.util.concurrent.TimeUnit para realizar la conversión.
Este método devuelve un long, y nuestro método espera un int, así que ahora también tenemos un problema de conversión que resolver. Tal vez podríamos intentar algo sencillo. ¿Una multiplicación?
Esto funcionará pero parece un poco fuera de lugar. Por si no lo has notado ya, hemos invertido una cantidad considerable de tiempo y esfuerzo para migrar una sola línea de código. Pero como puedes imaginar, tenemos muchas ediciones de este tipo que hacer a mano y no mejora.
Sin embargo, si miramos un poco más a la API de java.time, podemos descubrir una solución que parece un poco más fluida.
Aunque ZonedDateTime no tiene una forma obvia de establecer los milisegundos, puede hacerse mediante el método with(TemporalField field, long newValue), utilizando ChronoField.MILLI_OF_SECOND como TemporalField.
Y la documentación de java menciona que realizará la conversión a nanosegundos por nosotros:
Cuando este campo se utiliza para establecer un valor, debe comportarse de la misma manera que establecer NANO_OF_SECOND con el valor multiplicado por 1.000.000.
Así que podemos simplemente especificar 0 para nuestros nanosegundos en el método de fábrica, y luego usar el método with para crear un ZonedDateTime que tenga todos los valores originales así como los milisegundos.
Mirando nuestro resultado final, parece que sólo hemos cambiado una línea de código, ¡no muestra realmente el esfuerzo que ha supuesto investigar una sola migración!
Cree una receta para migrar más rápida y fácilmente
Sensei nos ofrece una forma de compartir con otros desarrolladores esta información que tanto nos ha costado conseguir. Al crear una receta que recoge todos estos requisitos, permitirá a los usuarios de Sensei realizar esta migración con un clic del ratón.
Una receta de Sensei consta de 3 secciones principales:
- Metadatos
- Buscar en
- Arreglos disponibles
Veamos una receta de Sensei (también puede verse como una receta YAML) que nos ayudará a migrar esta llamada a su equivalente java.time.
DateTime foo = new DateTime(year, monthOfYear, dayOfMonth, hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond);
Sección de metadatos
La sección de metadatos contiene información sobre la receta y cómo debe utilizarse.
Sección de búsqueda
La sección de búsqueda de una receta de Sensei especifica a qué elementos de código debe aplicarse esta receta.
search:
instanceCreation:
args:
1:
type: int
2:
type: int
3:
type: int
4:
type: int
5:
type: int
6:
type: int
7:
type: int
argCount: 7
type: org.joda.time.DateTime
En esta sección de búsqueda vemos que estamos:
- Buscar una instanceCreation, es decir, un uso de un Constructor. Nota: hay muchos otros objetivos de búsqueda disponibles
- El constructor debe tener 7 argumentos, esto se especifica con la propiedad argCount
- los argumentos 1-7 deben ser de tipo int
- Buscamos constructores del tipo org.joda.time.DateTime
Sección de arreglos disponibles
La sección availableFixes puede especificar una o más correcciones que pueden aplicarse al elemento de código correspondiente. Cada fix puede tener múltiples acciones, y en nuestro caso tenemos un único fix, que realiza 2 acciones.
- El nombre de la corrección se muestra al usuario en el menú "Quickfixes", y describe lo que ocurrirá si el usuario aplica esta quickfix
- La lista de acciones muestra las acciones que realizará este quickfix
- La acción de reescritura reescribirá el elemento de código utilizando una plantilla mustache. Puede hacer uso de variables y funciones de sustitución de cadenas.
- La acción modifyAssignedVariable comprobará si este constructor se está utilizando para asignar el valor a una variable. Si es así, esta acción modificará la variable para que sea declarada como el tipo especificado por type
Uso de la receta para la transformación del código
Con nuestra receta escrita y habilitada, escanea nuestro código y destaca los segmentos a los que se puede aplicar.
En la captura de pantalla de abajo podemos ver que el constructor de destino ha sido marcado por Sensei. Al pasar el ratón por encima del constructor marcado nos muestra la receta shortDescription y la opción Quickfix Migrate to java.time.ZonedDateTime
Después de seleccionar el quickfix Migrate to java.time.ZonedDateTime, el código se transforma según las acciones que especificamos en la receta.
Una migración única y prácticas de codificación uniformes en todos los equipos, con Sensei
Podemos ver en nuestro ejemplo anterior que la migración de una sola línea de código puede implicar un conocimiento duramente ganado. Sensei puede convertir ese conocimiento en recetas o libros de cocina procesables que pueden ser compartidos dentro de los equipos. Puedes planificar un sprint de migración único o adoptar el enfoque de hacer transformaciones incrementales instantáneas a java.time a medida que te encuentres con código Joda-Time. Puede habilitar/deshabilitar recetas como una forma de hacer migraciones en etapas o pasos lógicos e incluso, ampliar o reducir el alcance de los archivos escaneados por Sensei - la flexibilidad que hace que las migraciones de código sean menos dolorosas.
La migración de librerías es sólo un ejemplo de las muchas maneras en que Sensei puede utilizarse para estandarizar tus proyectos. Siempre puedes estar atento a los antipatrones o a ciertas transformaciones manuales de código que encuentres con frecuencia en pull requests o mientras codificas tú mismo. Si tienes un conjunto de directrices de codificación que los desarrolladores no siguen a menudo, puedes convertir las directrices en recetas, permitiendo a los desarrolladores aplicar transformaciones de código aprobadas con confianza.
Si tienes alguna pregunta, nos encantaría que nos la hicieras llegar. Únase a nosotros en Slack en: sensei-scw.slack.com
Migrar código (léase código heredado) no es divertido. Requiere una enorme cantidad de planificación y esfuerzo para llevarlo a cabo. Aunque no es el trabajo más emocionante o motivador para los desarrolladores, se requiere determinación y la experiencia adecuada para migrar el código heredado a las nuevas versiones de las bibliotecas. Joda-Time a java.time es una de esas migraciones que requiere una planificación y ejecución meticulosas.
Si su proyecto Java comenzó su vida antes de Java SE 8, y utiliza el procesamiento de fecha/hora, entonces probablemente utilizó Joda-Time - una excelente biblioteca y un estándar de facto para manejar las funciones de fecha y hora antes de SE 8. Si su proyecto todavía utiliza Joda-Time pero le gustaría migrar a java.time entonces siga leyendo.
El lanzamiento de Java SE 8 incluyó una nueva y mejorada API de fecha y hora estándar comúnmente conocida como java.time (JSR-310). El proyecto Joda-Time recomienda ahora migrar a java.time (JSR-310).
Aunque java.time (JSR-310) se inspiró en gran medida en Joda-Time, no es compatible con versiones anteriores y los conceptos y la terminología han cambiado. Por eso, la migración de Joda-Time a java.time requiere una cuidadosa atención a cada línea de código que se cambie. Esto puede llevar mucho tiempo y casi te haría desear que hubiera una forma más fácil y automatizada de migrar.
Hay una forma mejor de migrar y la hemos creado usando Sensei - un plugin de IntelliJ para realizar automáticamente transformaciones de código según las recetas (reglas) definidas por usted. Dedique su tiempo a definir recetas reutilizables, en lugar de realizar tareas de migración repetitivas. La automatización no sólo transformará su código heredado de Joda-Time, sino que también ayudará a los equipos a seguir las directrices allí mismo, en el IDE, mientras escriben el nuevo código.
Para ayudarte a tener una ventaja, hemos creado un libro de cocina público Sensei Standardization on java.time (JSR-310) que incluye recetas para migrar de Joda-Time a java.time de una manera menos dolorosa. Se trata de un conjunto creciente de recetas que seguiremos ampliando para añadir más cobertura con más recetas.
Este es un ejemplo de una migración de muestra que puede ayudarle a ver cómo Sensei facilita la migración de código heredado.
De la migración manual repetitiva a las transformaciones de código automatizadas
Veamos un ejemplo de creación de un nuevo DateTime que demuestra unas cuantas trampas ocultas al migrar una sola línea de código de Joda-Time a java.time. A continuación, veremos una de nuestras recetas de Sensei de nuestro libro de cocina Standardization on java.time (JSR-310) y mostraremos cómo captura toda esta información, para que esta misma migración pueda ser reutilizada una y otra vez por cualquier desarrollador.
En este ejemplo, estamos construyendo un Joda-Time DateTime a partir de 7 argumentos int que representan valores de los campos DateTime.
¿Cómo podemos migrar esto a un equivalente de java.time?
El javadoc en Joda-Time para este constructor dice:
Construye una instancia a partir de los valores de los campos datetime utilizando ISOChronology en la zona horaria por defecto.
Al principio, podríamos suponer que hay una clase DateTime en java.time, pero no la hay. Si buscas en Google "migrar de joda time a java time" es muy probable que encuentres la entrada del blog de Stephen Colebourne Converting from Joda-Time to java.time.
Esto le da un buen comienzo, y nos apunta en la dirección de usar java.time.ZonedDateTime o java.time.OffsetDateTime. Aquí está nuestra primera pregunta, ¿cuál utilizo? Probablemente ZonedDateTime basado en los comentarios de Stephen.
Buscando en el javadoc de ZonedDateTime, no vemos ningún constructor. Volviendo a la entrada del blog de Stephen leemos más abajo:
Construcción. Joda-Time tiene un constructor que acepta un objeto y realiza la conversión de tipo. java.time sólo tiene métodos de fábrica, por lo que la conversión es un problema del usuario, aunque se proporciona un método parse() para las cadenas.
Así que debe haber un método estático de fábrica, buscando en los métodos estáticos encontramos uno que se parece bastante, pero no es exactamente igual.
Tiene 7 parámetros int como nuestro constructor Joda-Time DateTime original, sin embargo si no prestas atención te perderás un detalle importante. El 7º parámetro ya no representa milisegundos, en su lugar espera nanosegundos, esto se debe a que java.time ha aumentado la precisión respecto a Joda-Time y mide los instantes al nano-segundo. Un detalle importante que podría haber pasado fácilmente por alto. Además, este método espera un ZoneId, por lo que hace que te preguntes por qué no lo necesitabas antes y por qué lo necesitas ahora.
Recordando el javadoc de nuestro constructor original que mencionaba que usaría la zona horaria por defecto, ¿quizás haya una forma de obtener el ZoneId por defecto?
El javadoc de ZoneId no nos habla de ningún constructor listado, pero mirando los métodos estáticos vemos que podemos usar systemDefault()
Ahora que hemos resuelto el ZoneId, ¿qué debemos hacer con nuestra conversión de milisegundos a nanoSegundos? Tal vez podamos utilizar java.util.concurrent.TimeUnit para realizar la conversión.
Este método devuelve un long, y nuestro método espera un int, así que ahora también tenemos un problema de conversión que resolver. Tal vez podríamos intentar algo sencillo. ¿Una multiplicación?
Esto funcionará pero parece un poco fuera de lugar. Por si no lo has notado ya, hemos invertido una cantidad considerable de tiempo y esfuerzo para migrar una sola línea de código. Pero como puedes imaginar, tenemos muchas ediciones de este tipo que hacer a mano y no mejora.
Sin embargo, si miramos un poco más a la API de java.time, podemos descubrir una solución que parece un poco más fluida.
Aunque ZonedDateTime no tiene una forma obvia de establecer los milisegundos, puede hacerse mediante el método with(TemporalField field, long newValue), utilizando ChronoField.MILLI_OF_SECOND como TemporalField.
Y la documentación de java menciona que realizará la conversión a nanosegundos por nosotros:
Cuando este campo se utiliza para establecer un valor, debe comportarse de la misma manera que establecer NANO_OF_SECOND con el valor multiplicado por 1.000.000.
Así que podemos simplemente especificar 0 para nuestros nanosegundos en el método de fábrica, y luego usar el método with para crear un ZonedDateTime que tenga todos los valores originales así como los milisegundos.
Mirando nuestro resultado final, parece que sólo hemos cambiado una línea de código, ¡no muestra realmente el esfuerzo que ha supuesto investigar una sola migración!
Cree una receta para migrar más rápida y fácilmente
Sensei nos ofrece una forma de compartir con otros desarrolladores esta información que tanto nos ha costado conseguir. Al crear una receta que recoge todos estos requisitos, permitirá a los usuarios de Sensei realizar esta migración con un clic del ratón.
Una receta de Sensei consta de 3 secciones principales:
- Metadatos
- Buscar en
- Arreglos disponibles
Veamos una receta de Sensei (también puede verse como una receta YAML) que nos ayudará a migrar esta llamada a su equivalente java.time.
DateTime foo = new DateTime(year, monthOfYear, dayOfMonth, hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond);
Sección de metadatos
La sección de metadatos contiene información sobre la receta y cómo debe utilizarse.
Sección de búsqueda
La sección de búsqueda de una receta de Sensei especifica a qué elementos de código debe aplicarse esta receta.
search:
instanceCreation:
args:
1:
type: int
2:
type: int
3:
type: int
4:
type: int
5:
type: int
6:
type: int
7:
type: int
argCount: 7
type: org.joda.time.DateTime
En esta sección de búsqueda vemos que estamos:
- Buscar una instanceCreation, es decir, un uso de un Constructor. Nota: hay muchos otros objetivos de búsqueda disponibles
- El constructor debe tener 7 argumentos, esto se especifica con la propiedad argCount
- los argumentos 1-7 deben ser de tipo int
- Buscamos constructores del tipo org.joda.time.DateTime
Sección de arreglos disponibles
La sección availableFixes puede especificar una o más correcciones que pueden aplicarse al elemento de código correspondiente. Cada fix puede tener múltiples acciones, y en nuestro caso tenemos un único fix, que realiza 2 acciones.
- El nombre de la corrección se muestra al usuario en el menú "Quickfixes", y describe lo que ocurrirá si el usuario aplica esta quickfix
- La lista de acciones muestra las acciones que realizará este quickfix
- La acción de reescritura reescribirá el elemento de código utilizando una plantilla mustache. Puede hacer uso de variables y funciones de sustitución de cadenas.
- La acción modifyAssignedVariable comprobará si este constructor se está utilizando para asignar el valor a una variable. Si es así, esta acción modificará la variable para que sea declarada como el tipo especificado por type
Uso de la receta para la transformación del código
Con nuestra receta escrita y habilitada, escanea nuestro código y destaca los segmentos a los que se puede aplicar.
En la captura de pantalla de abajo podemos ver que el constructor de destino ha sido marcado por Sensei. Al pasar el ratón por encima del constructor marcado nos muestra la receta shortDescription y la opción Quickfix Migrate to java.time.ZonedDateTime
Después de seleccionar el quickfix Migrate to java.time.ZonedDateTime, el código se transforma según las acciones que especificamos en la receta.
Una migración única y prácticas de codificación uniformes en todos los equipos, con Sensei
Podemos ver en nuestro ejemplo anterior que la migración de una sola línea de código puede implicar un conocimiento duramente ganado. Sensei puede convertir ese conocimiento en recetas o libros de cocina procesables que pueden ser compartidos dentro de los equipos. Puedes planificar un sprint de migración único o adoptar el enfoque de hacer transformaciones incrementales instantáneas a java.time a medida que te encuentres con código Joda-Time. Puede habilitar/deshabilitar recetas como una forma de hacer migraciones en etapas o pasos lógicos e incluso, ampliar o reducir el alcance de los archivos escaneados por Sensei - la flexibilidad que hace que las migraciones de código sean menos dolorosas.
La migración de librerías es sólo un ejemplo de las muchas maneras en que Sensei puede utilizarse para estandarizar tus proyectos. Siempre puedes estar atento a los antipatrones o a ciertas transformaciones manuales de código que encuentres con frecuencia en pull requests o mientras codificas tú mismo. Si tienes un conjunto de directrices de codificación que los desarrolladores no siguen a menudo, puedes convertir las directrices en recetas, permitiendo a los desarrolladores aplicar transformaciones de código aprobadas con confianza.
Si tienes alguna pregunta, nos encantaría que nos la hicieras llegar. Únase a nosotros en Slack en: sensei-scw.slack.com
Haga clic en el siguiente enlace y descargue el PDF de este recurso.
Secure Code Warrior está a disposición de su organización para ayudarle a proteger el código a lo largo de todo el ciclo de vida de desarrollo de software y crear una cultura en la que la ciberseguridad sea una prioridad. Tanto si es director de AppSec, desarrollador, CISO o cualquier persona implicada en la seguridad, podemos ayudar a su organización a reducir los riesgos asociados a un código inseguro.
Ver el informeReservar una demostraciónCameron es un desarrollador de software senior en Secure Code Warrior. Tiene más de 15 años de experiencia en la entrega de software y es un apasionado de la productividad de los desarrolladores y contribuye activamente al software de código abierto.
Migrar código (léase código heredado) no es divertido. Requiere una enorme cantidad de planificación y esfuerzo para llevarlo a cabo. Aunque no es el trabajo más emocionante o motivador para los desarrolladores, se requiere determinación y la experiencia adecuada para migrar el código heredado a las nuevas versiones de las bibliotecas. Joda-Time a java.time es una de esas migraciones que requiere una planificación y ejecución meticulosas.
Si su proyecto Java comenzó su vida antes de Java SE 8, y utiliza el procesamiento de fecha/hora, entonces probablemente utilizó Joda-Time - una excelente biblioteca y un estándar de facto para manejar las funciones de fecha y hora antes de SE 8. Si su proyecto todavía utiliza Joda-Time pero le gustaría migrar a java.time entonces siga leyendo.
El lanzamiento de Java SE 8 incluyó una nueva y mejorada API de fecha y hora estándar comúnmente conocida como java.time (JSR-310). El proyecto Joda-Time recomienda ahora migrar a java.time (JSR-310).
Aunque java.time (JSR-310) se inspiró en gran medida en Joda-Time, no es compatible con versiones anteriores y los conceptos y la terminología han cambiado. Por eso, la migración de Joda-Time a java.time requiere una cuidadosa atención a cada línea de código que se cambie. Esto puede llevar mucho tiempo y casi te haría desear que hubiera una forma más fácil y automatizada de migrar.
Hay una forma mejor de migrar y la hemos creado usando Sensei - un plugin de IntelliJ para realizar automáticamente transformaciones de código según las recetas (reglas) definidas por usted. Dedique su tiempo a definir recetas reutilizables, en lugar de realizar tareas de migración repetitivas. La automatización no sólo transformará su código heredado de Joda-Time, sino que también ayudará a los equipos a seguir las directrices allí mismo, en el IDE, mientras escriben el nuevo código.
Para ayudarte a tener una ventaja, hemos creado un libro de cocina público Sensei Standardization on java.time (JSR-310) que incluye recetas para migrar de Joda-Time a java.time de una manera menos dolorosa. Se trata de un conjunto creciente de recetas que seguiremos ampliando para añadir más cobertura con más recetas.
Este es un ejemplo de una migración de muestra que puede ayudarle a ver cómo Sensei facilita la migración de código heredado.
De la migración manual repetitiva a las transformaciones de código automatizadas
Veamos un ejemplo de creación de un nuevo DateTime que demuestra unas cuantas trampas ocultas al migrar una sola línea de código de Joda-Time a java.time. A continuación, veremos una de nuestras recetas de Sensei de nuestro libro de cocina Standardization on java.time (JSR-310) y mostraremos cómo captura toda esta información, para que esta misma migración pueda ser reutilizada una y otra vez por cualquier desarrollador.
En este ejemplo, estamos construyendo un Joda-Time DateTime a partir de 7 argumentos int que representan valores de los campos DateTime.
¿Cómo podemos migrar esto a un equivalente de java.time?
El javadoc en Joda-Time para este constructor dice:
Construye una instancia a partir de los valores de los campos datetime utilizando ISOChronology en la zona horaria por defecto.
Al principio, podríamos suponer que hay una clase DateTime en java.time, pero no la hay. Si buscas en Google "migrar de joda time a java time" es muy probable que encuentres la entrada del blog de Stephen Colebourne Converting from Joda-Time to java.time.
Esto le da un buen comienzo, y nos apunta en la dirección de usar java.time.ZonedDateTime o java.time.OffsetDateTime. Aquí está nuestra primera pregunta, ¿cuál utilizo? Probablemente ZonedDateTime basado en los comentarios de Stephen.
Buscando en el javadoc de ZonedDateTime, no vemos ningún constructor. Volviendo a la entrada del blog de Stephen leemos más abajo:
Construcción. Joda-Time tiene un constructor que acepta un objeto y realiza la conversión de tipo. java.time sólo tiene métodos de fábrica, por lo que la conversión es un problema del usuario, aunque se proporciona un método parse() para las cadenas.
Así que debe haber un método estático de fábrica, buscando en los métodos estáticos encontramos uno que se parece bastante, pero no es exactamente igual.
Tiene 7 parámetros int como nuestro constructor Joda-Time DateTime original, sin embargo si no prestas atención te perderás un detalle importante. El 7º parámetro ya no representa milisegundos, en su lugar espera nanosegundos, esto se debe a que java.time ha aumentado la precisión respecto a Joda-Time y mide los instantes al nano-segundo. Un detalle importante que podría haber pasado fácilmente por alto. Además, este método espera un ZoneId, por lo que hace que te preguntes por qué no lo necesitabas antes y por qué lo necesitas ahora.
Recordando el javadoc de nuestro constructor original que mencionaba que usaría la zona horaria por defecto, ¿quizás haya una forma de obtener el ZoneId por defecto?
El javadoc de ZoneId no nos habla de ningún constructor listado, pero mirando los métodos estáticos vemos que podemos usar systemDefault()
Ahora que hemos resuelto el ZoneId, ¿qué debemos hacer con nuestra conversión de milisegundos a nanoSegundos? Tal vez podamos utilizar java.util.concurrent.TimeUnit para realizar la conversión.
Este método devuelve un long, y nuestro método espera un int, así que ahora también tenemos un problema de conversión que resolver. Tal vez podríamos intentar algo sencillo. ¿Una multiplicación?
Esto funcionará pero parece un poco fuera de lugar. Por si no lo has notado ya, hemos invertido una cantidad considerable de tiempo y esfuerzo para migrar una sola línea de código. Pero como puedes imaginar, tenemos muchas ediciones de este tipo que hacer a mano y no mejora.
Sin embargo, si miramos un poco más a la API de java.time, podemos descubrir una solución que parece un poco más fluida.
Aunque ZonedDateTime no tiene una forma obvia de establecer los milisegundos, puede hacerse mediante el método with(TemporalField field, long newValue), utilizando ChronoField.MILLI_OF_SECOND como TemporalField.
Y la documentación de java menciona que realizará la conversión a nanosegundos por nosotros:
Cuando este campo se utiliza para establecer un valor, debe comportarse de la misma manera que establecer NANO_OF_SECOND con el valor multiplicado por 1.000.000.
Así que podemos simplemente especificar 0 para nuestros nanosegundos en el método de fábrica, y luego usar el método with para crear un ZonedDateTime que tenga todos los valores originales así como los milisegundos.
Mirando nuestro resultado final, parece que sólo hemos cambiado una línea de código, ¡no muestra realmente el esfuerzo que ha supuesto investigar una sola migración!
Cree una receta para migrar más rápida y fácilmente
Sensei nos ofrece una forma de compartir con otros desarrolladores esta información que tanto nos ha costado conseguir. Al crear una receta que recoge todos estos requisitos, permitirá a los usuarios de Sensei realizar esta migración con un clic del ratón.
Una receta de Sensei consta de 3 secciones principales:
- Metadatos
- Buscar en
- Arreglos disponibles
Veamos una receta de Sensei (también puede verse como una receta YAML) que nos ayudará a migrar esta llamada a su equivalente java.time.
DateTime foo = new DateTime(year, monthOfYear, dayOfMonth, hourOfDay, minuteOfHour, secondOfMinute, millisOfSecond);
Sección de metadatos
La sección de metadatos contiene información sobre la receta y cómo debe utilizarse.
Sección de búsqueda
La sección de búsqueda de una receta de Sensei especifica a qué elementos de código debe aplicarse esta receta.
search:
instanceCreation:
args:
1:
type: int
2:
type: int
3:
type: int
4:
type: int
5:
type: int
6:
type: int
7:
type: int
argCount: 7
type: org.joda.time.DateTime
En esta sección de búsqueda vemos que estamos:
- Buscar una instanceCreation, es decir, un uso de un Constructor. Nota: hay muchos otros objetivos de búsqueda disponibles
- El constructor debe tener 7 argumentos, esto se especifica con la propiedad argCount
- los argumentos 1-7 deben ser de tipo int
- Buscamos constructores del tipo org.joda.time.DateTime
Sección de arreglos disponibles
La sección availableFixes puede especificar una o más correcciones que pueden aplicarse al elemento de código correspondiente. Cada fix puede tener múltiples acciones, y en nuestro caso tenemos un único fix, que realiza 2 acciones.
- El nombre de la corrección se muestra al usuario en el menú "Quickfixes", y describe lo que ocurrirá si el usuario aplica esta quickfix
- La lista de acciones muestra las acciones que realizará este quickfix
- La acción de reescritura reescribirá el elemento de código utilizando una plantilla mustache. Puede hacer uso de variables y funciones de sustitución de cadenas.
- La acción modifyAssignedVariable comprobará si este constructor se está utilizando para asignar el valor a una variable. Si es así, esta acción modificará la variable para que sea declarada como el tipo especificado por type
Uso de la receta para la transformación del código
Con nuestra receta escrita y habilitada, escanea nuestro código y destaca los segmentos a los que se puede aplicar.
En la captura de pantalla de abajo podemos ver que el constructor de destino ha sido marcado por Sensei. Al pasar el ratón por encima del constructor marcado nos muestra la receta shortDescription y la opción Quickfix Migrate to java.time.ZonedDateTime
Después de seleccionar el quickfix Migrate to java.time.ZonedDateTime, el código se transforma según las acciones que especificamos en la receta.
Una migración única y prácticas de codificación uniformes en todos los equipos, con Sensei
Podemos ver en nuestro ejemplo anterior que la migración de una sola línea de código puede implicar un conocimiento duramente ganado. Sensei puede convertir ese conocimiento en recetas o libros de cocina procesables que pueden ser compartidos dentro de los equipos. Puedes planificar un sprint de migración único o adoptar el enfoque de hacer transformaciones incrementales instantáneas a java.time a medida que te encuentres con código Joda-Time. Puede habilitar/deshabilitar recetas como una forma de hacer migraciones en etapas o pasos lógicos e incluso, ampliar o reducir el alcance de los archivos escaneados por Sensei - la flexibilidad que hace que las migraciones de código sean menos dolorosas.
La migración de librerías es sólo un ejemplo de las muchas maneras en que Sensei puede utilizarse para estandarizar tus proyectos. Siempre puedes estar atento a los antipatrones o a ciertas transformaciones manuales de código que encuentres con frecuencia en pull requests o mientras codificas tú mismo. Si tienes un conjunto de directrices de codificación que los desarrolladores no siguen a menudo, puedes convertir las directrices en recetas, permitiendo a los desarrolladores aplicar transformaciones de código aprobadas con confianza.
Si tienes alguna pregunta, nos encantaría que nos la hicieras llegar. Únase a nosotros en Slack en: sensei-scw.slack.com
Índice
Cameron es un desarrollador de software senior en Secure Code Warrior. Tiene más de 15 años de experiencia en la entrega de software y es un apasionado de la productividad de los desarrolladores y contribuye activamente al software de código abierto.
Secure Code Warrior está a disposición de su organización para ayudarle a proteger el código a lo largo de todo el ciclo de vida de desarrollo de software y crear una cultura en la que la ciberseguridad sea una prioridad. Tanto si es director de AppSec, desarrollador, CISO o cualquier persona implicada en la seguridad, podemos ayudar a su organización a reducir los riesgos asociados a un código inseguro.
Reservar una demostraciónDescargarRecursos para empezar
Evaluación comparativa de las competencias en materia de seguridad: optimización del diseño seguro en la empresa
El movimiento Secure-by-Design es el futuro del desarrollo de software seguro. Conozca los elementos clave que las empresas deben tener en cuenta cuando piensan en una iniciativa Secure-by-Design.
DigitalOcean reduce su deuda de seguridad con Secure Code Warrior
El uso por parte de DigitalOcean de la formación Secure Code Warrior ha reducido significativamente la deuda de seguridad, permitiendo a los equipos centrarse más en la innovación y la productividad. La mejora de la seguridad ha reforzado la calidad de sus productos y su ventaja competitiva. De cara al futuro, SCW Trust Score les ayudará a seguir mejorando las prácticas de seguridad y a continuar impulsando la innovación.
Recursos para empezar
La puntuación de confianza revela el valor de las iniciativas de mejora de la seguridad mediante el diseño
Nuestra investigación ha demostrado que la formación en código seguro funciona. Trust Score, que utiliza un algoritmo basado en más de 20 millones de puntos de datos de aprendizaje procedentes del trabajo de más de 250 000 alumnos en más de 600 organizaciones, revela su eficacia a la hora de reducir las vulnerabilidades y cómo hacer que la iniciativa sea aún más eficaz.
Seguridad reactiva frente a seguridad preventiva: Prevenir es mejor que curar
La idea de introducir la seguridad preventiva en el código y los sistemas heredados al mismo tiempo que en las aplicaciones más recientes puede parecer desalentadora, pero un planteamiento basado en el diseño seguro, aplicado mediante la mejora de las competencias de los desarrolladores, puede aplicar las mejores prácticas de seguridad a esos sistemas. Es la mejor oportunidad que tienen muchas organizaciones de mejorar su seguridad.
Ventajas de la evaluación comparativa de las competencias de seguridad de los desarrolladores
La creciente atención que se presta al código seguro y a los principios del diseño seguro exige que los desarrolladores reciban formación en ciberseguridad desde el principio del proceso de desarrollo de software, con herramientas como Secure Code Warrior's Trust Score, que ayudan a medir y mejorar sus progresos.
Impulsando iniciativas de seguridad por diseño para empresas con éxito significativo
Nuestro último documento de investigación, Benchmarking Security Skills: Streamlining Secure-by-Design in the Enterprise, es el resultado de un análisis profundo de iniciativas reales de Secure-by-Design a nivel empresarial y de la derivación de enfoques de mejores prácticas basados en hallazgos basados en datos.