Skip to content

Phase 4: Replace Joda-Time with java.time#609

Open
devin-ai-integration[bot] wants to merge 1 commit into
devin/1779366147-phase3-spring-boot-3.4-gradle-8from
devin/1779366484-phase4-joda-to-java-time
Open

Phase 4: Replace Joda-Time with java.time#609
devin-ai-integration[bot] wants to merge 1 commit into
devin/1779366147-phase3-spring-boot-3.4-gradle-8from
devin/1779366484-phase4-joda-to-java-time

Conversation

@devin-ai-integration
Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration Bot commented May 21, 2026

Summary

Complete removal of Joda-Time dependency, replacing all org.joda.time.DateTime usage with java.time.OffsetDateTime across 17 files.

Changes by category:

Domain entities:

  • Article.java, Comment.java: DateTimeOffsetDateTime, new DateTime()OffsetDateTime.now(ZoneOffset.UTC)

DTOs:

  • ArticleData.java, CommentData.java: DateTimeOffsetDateTime fields

Infrastructure:

  • DateTimeHandler.java: MyBatis TypeHandler rewritten for OffsetDateTime (using Timestamp.from(instant) / timestamp.toInstant().atOffset(UTC))
  • DateTimeCursor.java: cursor serialization using Instant.toEpochMilli() / Instant.ofEpochMilli()
  • CommentReadService.java: updated mapper type parameter

Application/GraphQL layer:

  • JacksonCustomizations.java: replaced Joda serializer with DateTimeFormatter.ISO_OFFSET_DATE_TIME
  • ArticleQueryService.java, CommentQueryService.java: CursorPageParameter<OffsetDateTime>
  • ArticleDatafetcher.java, CommentDatafetcher.java: replaced ISODateTimeFormat with DateTimeFormatter

Tests:

  • TestHelper.java, ArticleApiTest.java, ArticlesApiTest.java, ArticleQueryServiceTest.java: updated to use OffsetDateTime

build.gradle:

  • Removed joda-time:joda-time:2.10.13 dependency

All 68 tests pass with ./gradlew test.

Review & Testing Checklist for Human

  • Run ./gradlew test - all 68 tests should pass
  • Start the app with ./gradlew bootRun and verify REST responses serialize dates in ISO-8601 format (e.g. 2026-05-21T12:30:00Z)
  • Test GraphQL endpoint /graphql - verify createdAt/updatedAt fields in article and comment queries return ISO-8601 strings
  • Verify no remaining Joda-Time references: grep -r "org.joda" src/ should return nothing

Notes

Phase 4 of 4. Builds on Phase 3 (PR #608). This completes the full migration from Java 11 + Spring Boot 2.6.3 to Java 17 + Spring Boot 3.4.1 with modern dependencies.

Link to Devin session: https://app.devin.ai/sessions/e28d39518c344b59883ff9b9b60cdc8b
Requested by: @luisblascocog


Devin Review

Status Commit
⚪ Not started

Run Devin Review

💡 Connect your GitHub account to enable automatic code reviews.

Open in Devin Review (Staging)
Open in Devin Review

Replace all org.joda.time.DateTime usage with java.time.OffsetDateTime:
- Article.java, Comment.java: OffsetDateTime fields, ZoneOffset.UTC
- ArticleData.java, CommentData.java: OffsetDateTime fields
- DateTimeHandler.java: MyBatis TypeHandler rewritten for OffsetDateTime
- DateTimeCursor.java: cursor serialization using Instant.toEpochMilli
- JacksonCustomizations.java: custom serializer using DateTimeFormatter.ISO_OFFSET_DATE_TIME
- ArticleQueryService.java, CommentQueryService.java: CursorPageParameter<OffsetDateTime>
- ArticleDatafetcher.java, CommentDatafetcher.java: java.time formatting
- CommentReadService.java: updated mapper type parameter
- Test files: updated to use OffsetDateTime.now(ZoneOffset.UTC)

build.gradle:
- Removed joda-time:joda-time:2.10.13 dependency

All 68 tests pass.

Co-Authored-By: Luis Blasco <luis.blasco@cognition.ai>
@devin-ai-integration
Copy link
Copy Markdown
Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Copy link
Copy Markdown
Author

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 potential issue.

View 3 additional findings in Devin Review.

Open in Devin Review

.body(comment.getBody())
.updatedAt(ISODateTimeFormat.dateTime().withZoneUTC().print(comment.getCreatedAt()))
.createdAt(ISODateTimeFormat.dateTime().withZoneUTC().print(comment.getCreatedAt()))
.updatedAt(comment.getCreatedAt().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME))
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 buildCommentResult uses getCreatedAt() for updatedAt instead of getUpdatedAt()

In CommentDatafetcher.buildCommentResult, the .updatedAt(...) field is populated using comment.getCreatedAt() instead of comment.getUpdatedAt(). This means the GraphQL Comment.updatedAt response field will always return the creation timestamp rather than the update timestamp. This was a pre-existing bug from the old Joda-Time code, but since the PR rewrote these lines during the migration, it carried the bug forward.

Current MyBatis mapping masks the bug

Currently in src/main/resources/mapper/TransferData.xml:35-36, both fields are mapped from the same DB column (commentCreatedAt), so the values happen to be identical. However, the comments table does have a separate updated_at column (src/main/resources/db/migration/V1__create_tables.sql:48), so the bug will surface if the query and mapping are ever fixed to use the correct column.

Suggested change
.updatedAt(comment.getCreatedAt().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME))
.updatedAt(comment.getUpdatedAt().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME))
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@devin-ai-integration
Copy link
Copy Markdown
Author

End-to-End Test Results

Booted the app locally (./gradlew bootRun, Spring Boot 3.4.1 on Java 17) and tested 8 scenarios via curl against localhost:8080.

All 8 tests passed
# Test Result Key Evidence
1 User Registration + JWT ✅ passed HTTP 201, HS512 JWT with 3 segments
2 Login + Token Validation (JJWT 0.12.6 round-trip) ✅ passed Login 200, GET /user with token 200
3 Create Article + ISO-8601 Dates (REST) ✅ passed createdAt: 2026-05-21T12:59:14.573Z
4 GET Article + Date Persistence Round-trip ✅ passed Dates survive SQLite write→read, recent & with TZ
5 GraphQL Article Query + Dates ✅ passed ISO-8601 in articles query, no errors
6 Comment Creation + GraphQL Comment Dates ✅ passed REST 201, GraphQL ISO-8601 dates
7 Spring Security 6 Access Control ✅ passed Public=200, Protected (no token)=401
8 Jakarta Validation ✅ passed HTTP 422 with field-level errors
Unit tests: 68 passed, 0 failures, 0 errors
BUILD SUCCESSFUL
Total tests: 68
Total failures: 0
Total errors: 0
Key date serialization evidence

REST response (POST /articles):

"createdAt": "2026-05-21T12:59:14.573Z",
"updatedAt": "2026-05-21T12:59:14.573Z"

GraphQL response ({ articles(first:1) { edges { node { createdAt updatedAt } } } }):

"createdAt": "2026-05-21T12:59:14.573Z",
"updatedAt": "2026-05-21T12:59:14.573Z"

REST comment (POST /articles/test-article/comments):

"createdAt": "2026-05-21T12:59:42.942Z",
"updatedAt": "2026-05-21T12:59:42.942Z"

Note: Pre-existing bug preserved during migration — CommentDatafetcher.buildCommentResult uses getCreatedAt() for both updatedAt and createdAt in GraphQL (as flagged by Devin Review).

Devin session

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants