Spring hibernate unwanted timezone adjustment - java

I have a simple service that takes a date in as a String (amongst other fields) and saves it to a mySql database. I am using Spring Boot with Hibernate.
If I post JSON with {"expenseDate":"04/02/2017"} the date that is being captured after the jackson mapping is "2017-04-01 19:00:00".
Incidentally, if I look at the row in the database, the date is "2017-04-02" just as I would expect. When I query the service, the date that comes back in the json is correct, but when I dump out the rows from the database, they all return the correct day minus six hours.
I live in the central time zone, so my guess is that the date in the database is UTC and Spring is taking six hours off for being in central time.
Controller:
#PostMapping("/expenses")
public ResponseEntity<Expense> submitExpense(#RequestBody Expense expense) throws BadHttpRequest {
expenseService.saveExpense(expense);
return new ResponseEntity<>(expense, HttpStatus.CREATED);
}
Entity:
#Entity
public class Expense {
#Id
#GeneratedValue
Integer id;
Double cost;
String location;
String expenseType;
String description;
#JsonFormat(pattern = "MM/dd/yyyy")
Date expenseDate;
I tried #JsonFormat(pattern = "MM/dd/yyyy", timezone = "UTC") on the date field but it changed nothing.
I also tried constructing the date from the view using the date parts and passing it as a long down to the service, but the result is the same. clearly I'm missing something excruciatingly vital and, likely, simple.
any thoughts?

You should set the "timezone" to whatever timezone you expect the incoming date to be in.
public class Data {
#JsonFormat(pattern = "MM/dd/yyyy", timezone="GMT-04:00")
Date date;
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
public class Main {
public static void main(String[] args) throws JsonParseException, JsonMappingException, IOException {
ObjectMapper mapper = new ObjectMapper();
Data data = mapper.readValue("{\"date\" : \"04/02/2017\"}", Data.class);
System.out.println(data.date);
}
}
Output
Sun Apr 02 00:00:00 EDT 2017
// I am located in EDT
If I assume the incoming date is in UTC
#JsonFormat(pattern = "MM/dd/yyyy", timezone="UTC")
Date date;
Output
Sat Apr 01 20:00:00 EDT 2017
By default it appears to be considering the date as UTC. In your case if you are in CST you should consider setting Central Time timezone (I believe US/Central).
Note
If your date inputs can come from clients in different timezones that will be another discussion. You will most likely have to define a contract that client enters a date in a particular timezone OR explicitly specify a timezone as additional input OR have your client side (if one exists) do a conversion to a specific timezone that your server-side expects.

There is a database connection url parameter called serverTimezone you may need put.

Related

What could be the reason of auto datetime conversion in java api response

I created one simple model class User. I used Util date here.
class User{
private int id;
private String name;
private Date createdAt;
}
On user post API call, I simply do setCreatedAt(new Date).
The problem is in the response, I am getting createdAt as -5.30 of the actual time. No additional time conversion method is called.
For Example, I hit the POST API user created at 28-10-2021 11:30:00 which I can see in the logs. But when it returns the response to the postman it shows 28-10-2021 06:00:00 time. There is no time conversion method in the code. I checked the return object in the return statement in debug mode even there is showing 28-10-2021 11:30:00.
I wanted to know where is this conversion happening. And how to stop this.
If it's the problem with datetime library, then which one should I use.
Extra information:
* My system timezone is in UTC.
* I am using ubuntu.
* Creating restFull APIs(JaxRs)
EDIT 1:
client and server are on the same machine(UTC timezone). For client, I am using Postman.
URL: [POST] /user
Request Body:
{
"name": "XYZ"
}
Actual Response:
{
"id": 1,
"name": "XYZ",
"createdAt: "28-10-2021 06:00:00"
}
Expected Response:
{
"id": 1,
"name": "XYZ",
"createdAt: "28-10-2021 11:30:00"
}
On user post API call, I simply do setCreatedAt(new Date).
It appears that you have not set the timezone while creating an instance of java.util.Date
By default it will set as UTC irrespective of your system timezone. You can use the https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html instead.
final TIMEZONE = ""; // need to set the timezone here
SimpleDateFormat formatter = new SimpleDateFormat("dd-M-yyyy hh:mm:ss", Locale.ENGLISH);
formatter.setTimeZone(TimeZone.getTimeZone(TIMEZONE));
String dateInString = "28-10-2021 11:30:00";
Date date = formatter.parse(dateInString);
There might be JsonFormat annotations that have timeZone issues. Please check the link for more details on the issue.jackson-data-bind issue Overriding the timezone in ObjectMapper didn't work either. You can refer the solved example by implementing a custom Date Deserializer as below:
#Component
public class CustomDateDeserializer extends StdDeserializer<Date> {
private static final long serialVersionUID = 1L;
private SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); // specify your specific timezone
public CustomDateDeserializer() {
this(null);
}
public CustomDateDeserializer(Class<?> vc) {
super(vc);
}
#Override
public Date deserialize(JsonParser jsonparser, DeserializationContext context)
throws IOException, JsonProcessingException {
String date = jsonparser.getText();
try {
return formatter.parse(date);
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
Also, add the deserializer on the setter method in your bean properties.
#JsonDeserialize(using = CustomDateDeserializer.class)
public void setReturnDateTime(Date returnDateTime) {
this.returnDateTime = returnDateTime;
}

Spring- Boot : How to handle timestamp (ie save and get timestamp to and from sql database)

I'm developing a spring boot application and i'm having problems in handling java.sql.timestamp. When i store the timestamp to database it store the right timestamp but when i fetch the timestamp from database it fetches the timestamp with 5:30 hours of difference. I'm getting wired result, sometimes i get same timestamp as in database and sometimes i'm getting timestamp with 5:30 hours of difference. I even used #JsonFormat(timezone = "GMT+05:30") annotation to get the consistent results. But some times it gives different results.
There is a better way to handle all Timezone problems while reading/writing to database in SpringBoot applications.
Use Java8 LocalDateTime instead of Timestamp. Create a converter class like this to match Timestamp in DB and LocalDateTime in your application:
#Converter(autoApply = true)
public class LocalDateTimeAttributeConverter implements AttributeConverter<LocalDateTime, Timestamp> {
#Override
public Timestamp convertToDatabaseColumn(LocalDateTime locDateTime) {
return (locDateTime == null ? null : Timestamp.valueOf(locDateTime));
}
#Override
public LocalDateTime convertToEntityAttribute(Timestamp sqlTimestamp) {
return (sqlTimestamp == null ? null : sqlTimestamp.toLocalDateTime());
}
}
Set your application's default timezone in your main class:
#SpringBootApplication
public class ExampleApplication {
private static final String ZONE_ID_ISTANBUL = "Europe/Istanbul";
public static void main(String[] args) {
TimeZone.setDefault(TimeZone.getTimeZone(ZONE_ID_ISTANBUL));
System.out.println("Application time zone: " + TimeZone.getDefault().getID());
SpringApplication.run(ExampleApplication.class, args);
}
}
By setting default time zone, whenever you use LocalDateTime, it will use this timezone as default, therefore even though your database runs in a different timezone with your application, you will run your code in your time zone.
Note that after creating the converter class, you have to use it in your entity as follows:
#Column(name = "insert_time", nullable = false)
#Convert(converter = LocalDateTimeAttributeConverter.class)
private LocalDateTime insertTime;

Bizzare Issue With Java Timestamp

I am working on an application which sends an object to a server for processing. The object is sent in JSON format using Spring.
My issue is that all the fields are passed correctly - EXCEPT for the Date variables. They show up as a completely different value, and I am stumped as to why.
Here is an abbreviated version of the object that is being passed:
public class TransactionParameters {
public Date startDate;
public Date endDate;
public List<String> transactionCodes;
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
public Date getEndDate() {
return endDate;
}
public void setEndDate(Date endDate) {
this.endDate = endDate;
}
public List<String> getTransactionCodes() {
return transactionCodes;
}
public void setTransactionCodes(List<String> transactionCodes) {
this.transactionCodes = transactionCodes;
}
}
Here is the JSON created:
{"transactionCodes":["195"],"startDate":1524456000000,"endDate":1524456000000}
Here is the client code:
String responseString =
restTemplate.postForObject("http://localhost:9080/app/transaction"
+ "testUser123", transactionParameters, String.class);
Here is the server code:
#ApiOperation(value="Get Transactions for Customer")
#POST
#Produces({ MediaType.APPLICATION_JSON })
#Consumes(MediaType.APPLICATION_JSON)
#Path("/customerAccountTransactions/{customerCode: [a-zA-Z0-9]+}")
#RequestMapping(value ="/transaction/{customerCode: [a-zA-Z0-9]+}", method=RequestMethod.POST, produces=MediaType.APPLICATION_JSON, consumes=MediaType.APPLICATION_JSON)
#ApiImplicitParams(#ApiImplicitParam(name = AUTHORIZATION, value = AUTHORIZATION, required = true, dataType = STRING, paramType = HEADER))
public Response getAccountTransactionsForCustomer(#PathVariable(CUSTOMER_CODE) #PathParam(CUSTOMER_CODE) final String customerCode, TransactionParameters transactionParameters) throws IntegrationException {
LOGGER.info("getAccountTransactionsForCustomer()");
Response response = null;
try {
final AccountTransactionsBean atb = getTransactions(customerCode, transactionParameters)
response = ResponseBuilder.buildSuccessResponse(atb);
} catch (final NotAuthorizedException nae) {
response = ResponseBuilder.buildNotAuthorizedResponse();
}
return response;
}
But here's my issue - When I put a breakpoint at where the client calls the endpoint, the date is correct.
However, the date is wildly incorrect as it enters the server's endpoint.
All the the other variables in the TransactionParameters bean are correct. I have also replicated this call using SOAP UI, to rule out any issues with the client, and the issue still persists.
Can anyone offer any suggestions?
Thanks in advance for any help.
The reason for this issue is that Date and String are two different data types. When you are converting your Object to JSON, it is directly converting the date to String and in that process losing its essence.
In order to solve this, you need to tell the code that those particular fields are dates and thus, need to be retained as it is. You can do that by using annotations in your POJO:
Example:
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss.SSSZ")
private Date changeTimestamp;
You can use the above syntax and then change the pattern as per your need.
Disclaimer admittedly I don't know much about Spring REST so I can only give you general pointers, but this really does seem like a de-serialization issue.
Some general things to consider:
Make sure the server and client have the same settings for serializing/de-serializing.
Make sure they are running the same versions of Spring REST and Jackson.
Set the JVM arg -Djavax.net.debug=all and run again to look at what is really being sent/recieved.
Being Spring REST this uses Jackson under the hood right?
Try explicitly annotating your dates and see if that helps:
public class TransactionParameters {
#JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
public Date startDate;
#JsonFormat(pattern="yyyy-MM-dd HH:mm:ss")
public Date endDate;
// ...
}
You probably have to either add or remove the milliseconds to get the conversion to work correctly. 000

How to compare Google Appengine DateTime against a Date object in Java?

I am new to Google Appengine. In my application, the datastore entities store a java.util.Date object. I want to query the datastore to return all the entities before a particular time. My code for this is :
Date date = new Date();
Query<Event> query = ofy().load().type(Event.class).order("date");
query = query.filter("date <", date);
which upon execution, gives the error : Invalid date/time format: Sat Apr 04 00:40:22 IST 2015
If this format is invalid, which format do I need to use to query?
Here is a simple code
import java.util.Date;
import com.googlecode.objectify.annotation.Entity;
import com.googlecode.objectify.annotation.Id;
import com.googlecode.objectify.annotation.Index;
#Entity
public class EntityDate {
public EntityDate() {
// Objectify needed
}
#Id
private Long id;
#Index
public Date date;
}
and here the code which makes the query
Date date = new Date();
Objectify ofy = ObjectifyService.ofy();
ObjectifyService.register(EntityDate.class);
EntityDate entityDate = new EntityDate();
entityDate.date = date;
ofy.save().entities(entityDate);
Query<EntityDate> ofyQuery = ofy.load().type(EntityDate.class).order("date");
ofyQuery = ofyQuery.filter("date <", date);
List<EntityDate> list = ofyQuery.list();
Logger.getLogger("EntityDate").info(list.toString());
The entities are correctly saved
and the query provides the 4 results
[EntityDate#6780874d, EntityDate#27330551, EntityDate#6a21cf2, EntityDate#7d1a5744]
The default toString() of the class is a bit ugly, but it makes the point about the query correctly executed.
Can you provide the source of your Event class and the continuation of your code which execute the query?

JPA java.util.Date issue

I have date field in the database where the value is "27-AUG-10 15:30:00". I am using JPA to retrieve and set into a model object. In the model object the date is one day extra "2010-08-28 04:00:00.0". When retrieved the date should be 27 Aug 2010, but it is coming 28 Aug 2010 Can you please suggest me why it is retrieving one extra day.
import java.util.Date;
import javax.persistence.Column;
public class Model
{
#Column(name = "BEGIN_DATE")
private Date startDate;
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
}
The database will by default store the timezone you are in. However in retriving the date out it won't add back the timezone you are in. That date is being displayed as GMT. Look at using JodaTime for a better Date library.
I'll give you an example. You need the JodaTime library and the JadiraTypes library to persist JodaTime dates with Hibernate.
http://mvnrepository.com/artifact/joda-time/joda-time/2.3
http://mvnrepository.com/artifact/org.jadira.usertype/usertype.jodatime/2.0.1
Your code will look something like this:
#Type(type="org.jadira.usertype.dateandtime.joda.PersistentLocalDateTime")
private LocalDateTime date;
This will persist your dates to Hibernate for you.
I'm 90% sure that JodaTime supports adding back the timezone for you. If you are really worried, store them as Timestamps in your database.
Try changing to JodaTime as above and let me know if you have any issues.
I think You should use InitBinder whenever you play with dates between view and controller.
Just put following in your controller
#InitBinder
public void initBinder(WebDataBinder webDataBinder) {
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
webDataBinder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}

Categories