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 buen puerto. Aunque no sea la tarea más emocionante o motivadora para los desarrolladores, migrar código heredado a nuevas versiones de librerías exige determinación y la experiencia adecuada. Joda-Time a java.time es una de esas migraciones que requiere una planificación y ejecución meticulosas.
Si tu proyecto Java comenzó antes de Java SE 8 y utiliza procesamiento de fecha/hora, probablemente usó Joda-Time, una excelente librería y el estándar de facto para manejar funciones de fecha y hora antes de SE 8. Si tu proyecto todavía usa Joda-Time pero te gustaría migrar a java.time, sigue leyendo.
El lanzamiento de Java SE 8 incluyó una nueva y mejorada API estándar de fecha y hora, 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 terminologías han cambiado. Por eso, migrar de Joda-Time a java.time requiere prestar mucha atención a cada línea de código que cambies. Esto puede llevar mucho tiempo y casi te hará desear que existiera una forma más fácil y automatizada de migrar.
Existe 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) que tú definas. Dedica tu tiempo a definir recetas reutilizables en lugar de realizar tareas de migración repetitivas. La automatización no solo transformará tu código heredado de Joda-Time, sino que también ayudará a los equipos a seguir las directrices directamente en el IDE mientras escriben código nuevo.
Para ayudarte a empezar, hemos creado un libro de recetas público de Sensei llamado Estandarización en java.time (JSR-310), que incluye recetas para migrar de Joda-Time a java.time de una forma menos dolorosa. Este es un conjunto de recetas en crecimiento que seguiremos ampliando para ofrecer mayor cobertura con más recetas.
Aquí tienes un ejemplo de una migración que podría ayudarte a ver cómo Sensei hace que migrar código heredado sea sencillo.

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 algunas trampas ocultas al migrar una sola línea de código de Joda-Time a java.time. Luego veremos una de nuestras recetas de Sensei de nuestro libro de recetas de Estandarización en 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 DateTime de Joda-Time a partir de 7 argumentos enteros que representan los valores de los campos de DateTime.
¿Cómo migramos esto a un equivalente en java.time?
El javadoc en Joda-Time para este constructor dice:
Construye una instancia a partir de valores de campo de fecha y hora usando ISOChronology en la zona horaria predeterminada.
Al principio, podríamos asumir que existe una clase DateTime en java.time, pero no es así. Si buscas en Google "migrar de joda time a java time", lo más probable es que encuentres la entrada del blog de Stephen Colebourne Convertir de Joda-Time a java.time.
Esto te da un buen comienzo y nos orienta hacia el uso de java.time.ZonedDateTime o java.time.OffsetDateTime. Aquí surge nuestra primera pregunta: ¿cuál debo usar? Probablemente ZonedDateTime, basándonos en los comentarios de Stephen.
Al consultar 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 cuenta con un constructor que acepta un objeto y realiza la conversión de tipo. java.time solo dispone de métodos de fábrica, por lo que la conversión queda en manos del usuario, aunque se proporciona un método parse() para cadenas de texto.
Por lo tanto, debe existir un método de fábrica estático; al buscar entre los métodos estáticos, encontramos uno que parece bastante adecuado, pero no es exactamente igual.
Tiene 7 parámetros de tipo int, al igual que nuestro constructor original de Joda-Time DateTime; sin embargo, si no prestas atención, pasarás por alto un detalle importante. El séptimo parámetro ya no representa milisegundos, sino que espera nanosegundos. Esto se debe a que java.time tiene una mayor precisión que Joda-Time y mide los instantes a nivel de nanosegundos. Un detalle importante que podrías haber pasado por alto fácilmente. Además, este método requiere un ZoneId, lo que te hace preguntarte por qué antes no lo necesitabas y por qué ahora sí.
Recordando el javadoc de nuestro constructor original, que mencionaba que utilizaría la zona horaria predeterminada, ¿quizás exista una forma de obtener el ZoneId predeterminado?
El javadoc de ZoneId no nos indica ningún constructor, pero al observar los métodos estáticos, vemos que podemos usar systemDefault()
Ahora que hemos resuelto lo del ZoneId, ¿qué debemos hacer con nuestra conversión de milisegundos a nanosegundos? Quizás podamos usar 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 tenemos también un problema de conversión que resolver. ¿Quizás podríamos intentar algo sencillo? ¿Una multiplicación?
Esto funcionará, pero parece un poco fuera de lugar. Si aún no te has dado cuenta, hemos dedicado una cantidad considerable de tiempo y esfuerzo a migrar una sola línea de código. Pero, como puedes imaginar, tenemos muchas ediciones similares que hacer manualmente y la situación no mejora.
Sin embargo, si analizamos un poco más a fondo 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, se puede lograr usando el método with(TemporalField field, long newValue), utilizando ChronoField.MILLI_OF_SECOND como TemporalField.
Y el javadoc 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 al 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 contenga todos los valores originales, además de los milisegundos.
Al ver nuestro resultado final, parece que solo hemos cambiado una línea de código; ¡realmente no refleja el esfuerzo que supuso investigar solo una migración!
Crea una receta para migrar de forma más rápida y sencilla
Sensei nos ofrece una forma de compartir esta información, obtenida con tanto esfuerzo, con otros desarrolladores. Al crear una receta que capture todos estos requisitos, permitirás que los usuarios de Sensei realicen esta migración con un solo clic.
Una receta de Sensei consta de 3 secciones principales:
- Metadatos
- Búsqueda
- Correcciones disponibles
Echemos un vistazo a una receta de Sensei (que también puede verse como una receta YAML) que nos ayudará a migrar esta llamada a su equivalente en 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 dicha 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:
- Buscando una instanceCreation, es decir, el uso de un constructor. Nota: existen muchos otros objetivos de búsqueda disponibles
- El constructor debe tener 7 argumentos, esto se especifica mediante la propiedad argCount
- args 1-7 deben ser de tipo int
- Estamos buscando constructores del tipo org.joda.time.DateTime
Sección de correcciones disponibles
La sección availableFixes puede especificar una o más correcciones que se pueden aplicar al elemento de código coincidente. Cada corrección puede tener múltiples acciones y, en nuestro caso, tenemos una única corrección que realiza 2 acciones.
- El nombre de la corrección se muestra al usuario en el menú "Quickfixes" y describe lo que sucederá si el usuario aplica esta corrección rápida.
- La lista de acciones muestra qué acciones realizará esta corrección rápida.
- La acción rewrite reescribirá el elemento de código utilizando una plantilla mustache. Puede hacer uso de variables y funciones de reemplazo 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 se declare con el tipo especificado por type
Uso de la receta para realizar transformaciones de código
Una vez escrita y habilitada nuestra receta, esta analiza nuestro código y resalta los segmentos a los que se puede aplicar.
En la captura de pantalla a continuación, podemos ver que el constructor de destino ha sido marcado por Sensei. Al pasar el cursor sobre el constructor marcado, se nos muestra la descripción corta de la receta y la opción de corrección rápida Migrate to java.time.ZonedDateTime

Después de seleccionar la corrección rápida Migrate to java.time.ZonedDateTime, el código se transforma de acuerdo con las acciones que especificamos en la receta.

Una migración única y prácticas de codificación uniformes en todos los equipos, con Sensei
Como podemos ver en nuestro ejemplo anterior, la migración de una sola línea de código puede implicar conocimientos difíciles de adquirir. Sensei puede convertir ese conocimiento en recetas o manuales prácticos que se pueden compartir entre los equipos. Puede planificar un sprint de migración único o adoptar el enfoque de realizar transformaciones instantáneas incrementales a java.time a medida que encuentre código de Joda-Time. Puede habilitar o deshabilitar recetas para realizar migraciones en etapas o pasos lógicos, e incluso ampliar o reducir el alcance de los archivos analizados por Sensei: la flexibilidad que hace que las migraciones de código sean menos dolorosas.
La migración de bibliotecas es solo un ejemplo de las muchas formas en que se puede utilizar Sensei para estandarizar sus proyectos. Siempre puede estar atento a los antipatrones o a ciertas transformaciones manuales de código que encuentre con frecuencia en las solicitudes de extracción (pull requests) o mientras programa. Si tiene un conjunto de pautas de codificación que los desarrolladores suelen pasar por alto, puede convertirlas en recetas, lo que permitirá a los desarrolladores aplicar transformaciones de código aprobadas con total confianza.
Si tiene alguna pregunta, ¡nos encantaría saber de usted! Únase a nosotros en Slack en: sensei-scw.slack.com
Controle el desarrollo impulsado por IA antes de su lanzamiento
Mida el riesgo asistido por IA, aplique políticas de codificación segura en el momento de la confirmación y acelere la entrega segura en todo su ciclo de vida de desarrollo de software (SDLC).
Explorar más blogs
Acceda a contenido especializado sobre programación segura, gobernanza de IA y gestión de riesgos de software.

Una cultura de seguridad: cómo Sage creó su programa de defensores
Descubre cómo Sage reforzó su seguridad con un enfoque flexible y centrado en las relaciones, logrando formar a más de 200 expertos en seguridad y alcanzar una reducción de riesgos cuantificable.

Kamer van Koophandel: Seguridad a escala impulsada por los desarrolladores
La Cámara de Comercio (Kamer van Koophandel) comparte cómo integró la programación segura en el desarrollo diario mediante certificaciones basadas en roles, evaluaciones comparativas de puntuación de confianza y una cultura de responsabilidad compartida en seguridad.

Cómo un «Juego de Códigos» guía a IAG Group hacia un futuro de programación más seguro
IAG Group es la entidad detrás de muchas de las principales compañías de seguros en la región de Asia-Pacífico, que suscribe pólizas para millones de clientes por un valor aproximado de 11.400 millones de dólares australianos en primas anuales.

Mes de la Concienciación sobre Ciberseguridad 2026: Comprender la seguridad de la IA es fundamental para la defensa empresarial
Hemos llegado a un punto en el que la mayoría de las principales empresas de IA han pedido públicamente una ralentización global en el desarrollo de la IA para que las medidas de seguridad puedan ponerse al día, pero la verdad es que ya se han materializado graves problemas de seguridad. Esto deja a innumerables líderes de seguridad y equipos ejecutivos en una situación urgente, a medida que los costes monetarios y de riesgo de la implementación de la IA siguen acumulándose.

Habilitador 8: Branding de su programa
Un programa de codificación segura necesita algo más que un buen contenido para tener éxito. El Habilitador 8 muestra cómo el branding transforma la participación en una experiencia codiciada basada en el estatus.
.avif)
Cada empleado está ahora en la primera línea de la ciberseguridad de la IA
El panorama tecnológico empresarial está cambiando a una velocidad que pocos previeron hasta que fue demasiado tarde. Hemos cruzado oficialmente el umbral del código escrito por humanos y la asistencia básica de copilotos hacia la era del Ciclo de Vida de Desarrollo Agéntico (ADLC). Si bien los agentes de IA autónomos prometen una eficiencia sin precedentes en múltiples funciones, también traen consigo una clase completamente nueva de riesgos regulatorios y de seguridad.
Proteja el desarrollo basado en IA antes de su lanzamiento
Identifique riesgos de desarrollo, aplique políticas y prevenga vulnerabilidades en todo el ciclo de vida de su software.