From dfe9b40d3d75febb79e8657550f8aceac5ba01e4 Mon Sep 17 00:00:00 2001 From: Enzo Bom Date: Mon, 2 Feb 2026 21:33:33 -0300 Subject: [PATCH 1/4] Replace LocalDateTime Task attributes by Instant --- .../ts/taskscheduler/business/TaskService.java | 5 +++-- .../ts/taskscheduler/business/dto/TaskDTO.java | 10 ++++------ .../ts/taskscheduler/controller/TaskController.java | 3 ++- .../ts/taskscheduler/infrastructure/entity/Task.java | 7 ++++--- .../infrastructure/repository/TaskRepository.java | 4 ++-- 5 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/javanauta/ts/taskscheduler/business/TaskService.java b/src/main/java/com/javanauta/ts/taskscheduler/business/TaskService.java index c03cf73..d030e2b 100644 --- a/src/main/java/com/javanauta/ts/taskscheduler/business/TaskService.java +++ b/src/main/java/com/javanauta/ts/taskscheduler/business/TaskService.java @@ -11,6 +11,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import java.time.Instant; import java.time.LocalDateTime; import java.util.List; @@ -25,7 +26,7 @@ public class TaskService { public TaskDTO saveTask(String token, TaskDTO taskDTO) { taskDTO.setUserEmail(jwtUtil.extractUsername(token.substring(7))); - taskDTO.setCreationDateTime(LocalDateTime.now()); + taskDTO.setCreationDateTime(Instant.now()); taskDTO.setNotificationStatusEnum(NotificationStatusEnum.PENDING); Task task = taskConverter.toTask(taskDTO); @@ -33,7 +34,7 @@ public TaskDTO saveTask(String token, TaskDTO taskDTO) { return taskConverter.toTaskDTO(taskRepository.save(task)); } - public List findTaskByTimePeriod(LocalDateTime initialDateTime, LocalDateTime finalDateTime) { + public List findTaskByTimePeriod(Instant initialDateTime, Instant finalDateTime) { return taskConverter.toTaskDTOList(taskRepository.findByDueDateTimeBetween(initialDateTime, finalDateTime)); } diff --git a/src/main/java/com/javanauta/ts/taskscheduler/business/dto/TaskDTO.java b/src/main/java/com/javanauta/ts/taskscheduler/business/dto/TaskDTO.java index b306202..c5b5afa 100644 --- a/src/main/java/com/javanauta/ts/taskscheduler/business/dto/TaskDTO.java +++ b/src/main/java/com/javanauta/ts/taskscheduler/business/dto/TaskDTO.java @@ -4,6 +4,7 @@ import com.javanauta.ts.taskscheduler.infrastructure.enums.NotificationStatusEnum; import lombok.*; +import java.time.Instant; import java.time.LocalDateTime; @Getter @@ -16,12 +17,9 @@ public class TaskDTO { private String id; private String name; private String description; - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy HH:mm:ss") - private LocalDateTime creationDateTime; - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy HH:mm:ss") - private LocalDateTime dueDateTime; + private Instant creationDateTime; + private Instant dueDateTime; private String userEmail; - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy HH:mm:ss") - private LocalDateTime modificationDateTime; + private Instant modificationDateTime; private NotificationStatusEnum notificationStatusEnum; } diff --git a/src/main/java/com/javanauta/ts/taskscheduler/controller/TaskController.java b/src/main/java/com/javanauta/ts/taskscheduler/controller/TaskController.java index 0032a1f..3eb38a8 100644 --- a/src/main/java/com/javanauta/ts/taskscheduler/controller/TaskController.java +++ b/src/main/java/com/javanauta/ts/taskscheduler/controller/TaskController.java @@ -8,6 +8,7 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import java.time.Instant; import java.time.LocalDateTime; import java.util.List; @@ -24,7 +25,7 @@ public ResponseEntity saveTask(@RequestBody TaskDTO taskDTO, @RequestHe } @GetMapping("/events") - public ResponseEntity> findTaskListByPeriod(@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime initialDateTime, @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime finalDateTime) { + public ResponseEntity> findTaskListByPeriod(@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) Instant initialDateTime, @RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) Instant finalDateTime) { return ResponseEntity.ok(taskService.findTaskByTimePeriod(initialDateTime, finalDateTime)); } diff --git a/src/main/java/com/javanauta/ts/taskscheduler/infrastructure/entity/Task.java b/src/main/java/com/javanauta/ts/taskscheduler/infrastructure/entity/Task.java index a814029..d1aacd2 100644 --- a/src/main/java/com/javanauta/ts/taskscheduler/infrastructure/entity/Task.java +++ b/src/main/java/com/javanauta/ts/taskscheduler/infrastructure/entity/Task.java @@ -5,6 +5,7 @@ import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.core.mapping.Document; +import java.time.Instant; import java.time.LocalDateTime; @Setter @@ -19,9 +20,9 @@ public class Task { private String id; private String name; private String description; - private LocalDateTime creationDateTime; - private LocalDateTime dueDateTime; + private Instant creationDateTime; + private Instant dueDateTime; private String userEmail; - private LocalDateTime modificationDateTime; + private Instant modificationDateTime; private NotificationStatusEnum notificationStatusEnum; } diff --git a/src/main/java/com/javanauta/ts/taskscheduler/infrastructure/repository/TaskRepository.java b/src/main/java/com/javanauta/ts/taskscheduler/infrastructure/repository/TaskRepository.java index c912480..fa2cb72 100644 --- a/src/main/java/com/javanauta/ts/taskscheduler/infrastructure/repository/TaskRepository.java +++ b/src/main/java/com/javanauta/ts/taskscheduler/infrastructure/repository/TaskRepository.java @@ -4,11 +4,11 @@ import org.springframework.data.mongodb.repository.MongoRepository; import org.springframework.stereotype.Repository; -import java.time.LocalDateTime; +import java.time.Instant; import java.util.List; @Repository public interface TaskRepository extends MongoRepository { - List findByDueDateTimeBetween(LocalDateTime initialDateTime, LocalDateTime finalDateTime); + List findByDueDateTimeBetween(Instant initialDateTime, Instant finalDateTime); List findByUserEmail(String userEmail); } From 72a7cdb40d644d7532a79b7dcb755c8a53fc6014 Mon Sep 17 00:00:00 2001 From: Enzo Bom Date: Tue, 3 Feb 2026 14:35:51 -0300 Subject: [PATCH 2/4] Add timeZoneId attribute to task, make it always be handled as UTC in backend and adjust logic accordingly --- .../ts/taskscheduler/business/TaskService.java | 12 +++++++++++- .../ts/taskscheduler/business/dto/TaskDTO.java | 3 +-- .../controller/GlobalExceptionHandler.java | 6 ++++++ .../ts/taskscheduler/infrastructure/entity/Task.java | 1 + .../exception/InvalidTimeZoneException.java | 10 ++++++++++ 5 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/javanauta/ts/taskscheduler/infrastructure/exception/InvalidTimeZoneException.java diff --git a/src/main/java/com/javanauta/ts/taskscheduler/business/TaskService.java b/src/main/java/com/javanauta/ts/taskscheduler/business/TaskService.java index d030e2b..d64c3f8 100644 --- a/src/main/java/com/javanauta/ts/taskscheduler/business/TaskService.java +++ b/src/main/java/com/javanauta/ts/taskscheduler/business/TaskService.java @@ -5,6 +5,7 @@ import com.javanauta.ts.taskscheduler.business.mapper.TaskUpdateConverter; import com.javanauta.ts.taskscheduler.infrastructure.entity.Task; import com.javanauta.ts.taskscheduler.infrastructure.enums.NotificationStatusEnum; +import com.javanauta.ts.taskscheduler.infrastructure.exception.InvalidTimeZoneException; import com.javanauta.ts.taskscheduler.infrastructure.exception.ResourceNotFoundException; import com.javanauta.ts.taskscheduler.infrastructure.repository.TaskRepository; import com.javanauta.ts.taskscheduler.infrastructure.security.JwtUtil; @@ -12,7 +13,7 @@ import org.springframework.stereotype.Service; import java.time.Instant; -import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.List; @Service @@ -28,12 +29,19 @@ public TaskDTO saveTask(String token, TaskDTO taskDTO) { taskDTO.setUserEmail(jwtUtil.extractUsername(token.substring(7))); taskDTO.setCreationDateTime(Instant.now()); taskDTO.setNotificationStatusEnum(NotificationStatusEnum.PENDING); + validateTimeZoneId(taskDTO.getTimeZoneId()); Task task = taskConverter.toTask(taskDTO); return taskConverter.toTaskDTO(taskRepository.save(task)); } + private void validateTimeZoneId(String timeZoneId) { + if (!ZoneId.getAvailableZoneIds().contains(timeZoneId)) { + throw new InvalidTimeZoneException("Invalid time zone ID: " + timeZoneId); + } + } + public List findTaskByTimePeriod(Instant initialDateTime, Instant finalDateTime) { return taskConverter.toTaskDTOList(taskRepository.findByDueDateTimeBetween(initialDateTime, finalDateTime)); } @@ -61,6 +69,8 @@ public TaskDTO updateTaskById (TaskDTO taskDTO, String id) { Task task = taskRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException(String.format("Task with ID '%s' not found.", id))); taskUpdateConverter.updateTasks(taskDTO, task); + validateTimeZoneId(task.getTimeZoneId()); + return taskConverter.toTaskDTO(taskRepository.save(task)); } } diff --git a/src/main/java/com/javanauta/ts/taskscheduler/business/dto/TaskDTO.java b/src/main/java/com/javanauta/ts/taskscheduler/business/dto/TaskDTO.java index c5b5afa..a41ea4e 100644 --- a/src/main/java/com/javanauta/ts/taskscheduler/business/dto/TaskDTO.java +++ b/src/main/java/com/javanauta/ts/taskscheduler/business/dto/TaskDTO.java @@ -1,11 +1,9 @@ package com.javanauta.ts.taskscheduler.business.dto; -import com.fasterxml.jackson.annotation.JsonFormat; import com.javanauta.ts.taskscheduler.infrastructure.enums.NotificationStatusEnum; import lombok.*; import java.time.Instant; -import java.time.LocalDateTime; @Getter @Setter @@ -22,4 +20,5 @@ public class TaskDTO { private String userEmail; private Instant modificationDateTime; private NotificationStatusEnum notificationStatusEnum; + private String timeZoneId; } diff --git a/src/main/java/com/javanauta/ts/taskscheduler/controller/GlobalExceptionHandler.java b/src/main/java/com/javanauta/ts/taskscheduler/controller/GlobalExceptionHandler.java index 4db8fac..9d8693a 100644 --- a/src/main/java/com/javanauta/ts/taskscheduler/controller/GlobalExceptionHandler.java +++ b/src/main/java/com/javanauta/ts/taskscheduler/controller/GlobalExceptionHandler.java @@ -1,5 +1,6 @@ package com.javanauta.ts.taskscheduler.controller; +import com.javanauta.ts.taskscheduler.infrastructure.exception.InvalidTimeZoneException; import com.javanauta.ts.taskscheduler.infrastructure.exception.ResourceNotFoundException; import com.javanauta.ts.taskscheduler.infrastructure.exception.UnauthorizedException; import org.springframework.http.HttpStatus; @@ -19,4 +20,9 @@ public ResponseEntity handlerResourceNotFoundException(ResourceNotFoundE public ResponseEntity handlerUnauthorizedException(UnauthorizedException ex) { return new ResponseEntity<>(ex.getMessage(), HttpStatus.UNAUTHORIZED); } + + @ExceptionHandler(InvalidTimeZoneException.class) + public ResponseEntity handlerInvalidTimeZoneId(InvalidTimeZoneException ex) { + return new ResponseEntity<>(ex.getMessage(), HttpStatus.BAD_REQUEST); + } } diff --git a/src/main/java/com/javanauta/ts/taskscheduler/infrastructure/entity/Task.java b/src/main/java/com/javanauta/ts/taskscheduler/infrastructure/entity/Task.java index d1aacd2..bf15246 100644 --- a/src/main/java/com/javanauta/ts/taskscheduler/infrastructure/entity/Task.java +++ b/src/main/java/com/javanauta/ts/taskscheduler/infrastructure/entity/Task.java @@ -25,4 +25,5 @@ public class Task { private String userEmail; private Instant modificationDateTime; private NotificationStatusEnum notificationStatusEnum; + private String timeZoneId; } diff --git a/src/main/java/com/javanauta/ts/taskscheduler/infrastructure/exception/InvalidTimeZoneException.java b/src/main/java/com/javanauta/ts/taskscheduler/infrastructure/exception/InvalidTimeZoneException.java new file mode 100644 index 0000000..067eab1 --- /dev/null +++ b/src/main/java/com/javanauta/ts/taskscheduler/infrastructure/exception/InvalidTimeZoneException.java @@ -0,0 +1,10 @@ +package com.javanauta.ts.taskscheduler.infrastructure.exception; + +public class InvalidTimeZoneException extends RuntimeException { + public InvalidTimeZoneException(String message) { + super(message); + } + public InvalidTimeZoneException(String message, Throwable throwable) { + super(message, throwable); + } +} From 6852e01ee9182ad06141d2ee6d4f70f2eaebfafc Mon Sep 17 00:00:00 2001 From: Enzo Bom Date: Tue, 3 Feb 2026 14:44:57 -0300 Subject: [PATCH 3/4] Update Task's modificationDateTime when task status is modified or task is updated --- .../com/javanauta/ts/taskscheduler/business/TaskService.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/javanauta/ts/taskscheduler/business/TaskService.java b/src/main/java/com/javanauta/ts/taskscheduler/business/TaskService.java index d64c3f8..e65a433 100644 --- a/src/main/java/com/javanauta/ts/taskscheduler/business/TaskService.java +++ b/src/main/java/com/javanauta/ts/taskscheduler/business/TaskService.java @@ -61,6 +61,7 @@ public void deleteTaskById(String id) { public TaskDTO modifyTaskStatusById (NotificationStatusEnum notificationStatusEnum, String id) { Task task = taskRepository.findById(id).orElseThrow(() -> new ResourceNotFoundException(String.format("Task with ID '%s' not found.", id))); task.setNotificationStatusEnum(notificationStatusEnum); + task.setModificationDateTime(Instant.now()); return taskConverter.toTaskDTO(taskRepository.save(task)); } @@ -70,6 +71,7 @@ public TaskDTO updateTaskById (TaskDTO taskDTO, String id) { taskUpdateConverter.updateTasks(taskDTO, task); validateTimeZoneId(task.getTimeZoneId()); + task.setModificationDateTime(Instant.now()); return taskConverter.toTaskDTO(taskRepository.save(task)); } From 563660d449686fb00b0478adae0f9612e602fe04 Mon Sep 17 00:00:00 2001 From: Enzo Bom Date: Tue, 3 Feb 2026 15:10:57 -0300 Subject: [PATCH 4/4] Rename Task's dueDateTime --- .../com/javanauta/ts/taskscheduler/business/TaskService.java | 2 +- .../com/javanauta/ts/taskscheduler/business/dto/TaskDTO.java | 2 +- .../javanauta/ts/taskscheduler/infrastructure/entity/Task.java | 2 +- .../taskscheduler/infrastructure/repository/TaskRepository.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/javanauta/ts/taskscheduler/business/TaskService.java b/src/main/java/com/javanauta/ts/taskscheduler/business/TaskService.java index e65a433..06ac2e3 100644 --- a/src/main/java/com/javanauta/ts/taskscheduler/business/TaskService.java +++ b/src/main/java/com/javanauta/ts/taskscheduler/business/TaskService.java @@ -43,7 +43,7 @@ private void validateTimeZoneId(String timeZoneId) { } public List findTaskByTimePeriod(Instant initialDateTime, Instant finalDateTime) { - return taskConverter.toTaskDTOList(taskRepository.findByDueDateTimeBetween(initialDateTime, finalDateTime)); + return taskConverter.toTaskDTOList(taskRepository.findByScheduledDateTimeBetween(initialDateTime, finalDateTime)); } public List findTaskByUserEmail(String token) { diff --git a/src/main/java/com/javanauta/ts/taskscheduler/business/dto/TaskDTO.java b/src/main/java/com/javanauta/ts/taskscheduler/business/dto/TaskDTO.java index a41ea4e..9504214 100644 --- a/src/main/java/com/javanauta/ts/taskscheduler/business/dto/TaskDTO.java +++ b/src/main/java/com/javanauta/ts/taskscheduler/business/dto/TaskDTO.java @@ -16,7 +16,7 @@ public class TaskDTO { private String name; private String description; private Instant creationDateTime; - private Instant dueDateTime; + private Instant scheduledDateTime; private String userEmail; private Instant modificationDateTime; private NotificationStatusEnum notificationStatusEnum; diff --git a/src/main/java/com/javanauta/ts/taskscheduler/infrastructure/entity/Task.java b/src/main/java/com/javanauta/ts/taskscheduler/infrastructure/entity/Task.java index bf15246..ef0afeb 100644 --- a/src/main/java/com/javanauta/ts/taskscheduler/infrastructure/entity/Task.java +++ b/src/main/java/com/javanauta/ts/taskscheduler/infrastructure/entity/Task.java @@ -21,7 +21,7 @@ public class Task { private String name; private String description; private Instant creationDateTime; - private Instant dueDateTime; + private Instant scheduledDateTime; private String userEmail; private Instant modificationDateTime; private NotificationStatusEnum notificationStatusEnum; diff --git a/src/main/java/com/javanauta/ts/taskscheduler/infrastructure/repository/TaskRepository.java b/src/main/java/com/javanauta/ts/taskscheduler/infrastructure/repository/TaskRepository.java index fa2cb72..13ea63b 100644 --- a/src/main/java/com/javanauta/ts/taskscheduler/infrastructure/repository/TaskRepository.java +++ b/src/main/java/com/javanauta/ts/taskscheduler/infrastructure/repository/TaskRepository.java @@ -9,6 +9,6 @@ @Repository public interface TaskRepository extends MongoRepository { - List findByDueDateTimeBetween(Instant initialDateTime, Instant finalDateTime); + List findByScheduledDateTimeBetween(Instant initialDateTime, Instant finalDateTime); List findByUserEmail(String userEmail); }