While writing integration test I was expecting a hardcoded date in the response.
Basically I hardcoded expected date value '2020-11-10T00:00:00.000-05:00'
and ran new GregorianCalendar(2020, 10, 10).getTime()
When I put and assert and run it locally it passes, however when the same code was pushed to bamboo build server the actualValue it generated was'2020-11-10T00:00:00.000Z' and so my test failed.
Why the same calendar.getTime generating two different times, is it because the server machine is configured to be on GMT?
Can I do something to have them the same time or any other workaround?
NOTE: Making it string or comparing dates without time is not an option here, as I am using Spring MockWebServiceServer, where in I must hardcode the responseXML and specify date, something like this in Enum.
REQUESTAUTOMATESETTLEMENTWORKCASE("<aut:AutomateSettlementWorkcaseRequest xmlns:aut=\"http://www.abcd.com/abcd/workflow/services/workcase/model/AutomateSettlementWorkcase_1_0_0\">" +
" <aut:customerAccountId>5049903033584324</aut:customerAccountId>\n" +
" <aut:settlementDate>2020-11-10T00:00:00.000-05:00</aut:settlementDate>\n" +
" </aut:AutomateSettlementWorkcaseRequest>"),
The "-5" part in your input data is throwing it off... ideally you should specify the time zone when constructing the calendar, and then set it to 5am UTC (for example). Basically, 2020-11-10T00:00:00.000-05:00 is the same instant as 2020-11-10T05:00:00.000Z.
Now, what we don't know is whether it's important to you that you preserve the offset from UTC. If it is, you need to set an appropriate time zone in the calendar - one which has the same rules as whatever's generating your input data. If it isn't, I'd use UTC and set the time appropriately.
I would personally recommend using Joda Time instead of Calendar and Date though - it's a much better date and time API. No 0-based months, for starters :)
Related
My project uses Javascript and Java (Android) for the client and Java for the backend.
When I started working on my project, I stored dates as days from epoch (long) and all was good. I then found out that my project doesn't work well with timezones. Suddenly dates were +1 -1 days off. Depending on the client's location in the world.
After a short investigation, I saw that the foolproof way to avoid it was to store the dates as String yyyy-MM-ddT00:00 so when using the Javascript's new Date(dateStr), it creates it correctly and all was good. Ofcourse I could store the dates as yyyy-MM-dd and just send it to the client as yyyy-MM-ddT00:00 but that won't solve the question I have.
After that, I was wondering whether Java (backend) is handled correctly. I use LocalDate when I want to "play" with dates and LocalDate.parse doesn't like yyyy-MM-ddT00:00 format, instead it works with yyyy-MM-dd so whenever I needed dates, I did LocalDate.parse(dateStr.substring(0,10)). LocalDateTime does work with yyyy-MM-ddT00:00 but I don't need the time part and it had its own issues, which I don't remember what they were at the moment.
So now I have a lot of String manipulation (inside loops) that actually creates more String objects. One can say it's not that much of a stress and I shouldn't pay attention to that but I want to make sure I'm not missing something and maybe there's another way (maybe silly enough that I've missed) to overcome this.
Thanks
Update: The events are stored from a different source and only the date itself is important so if an event happened on 2020-06-17, this is the date all users should see, no matter where they are.
I'm using new Date(dateStr) in Javascript. If dateStr is 2020-06-17, the date object uses the client's timezone and the date might be +-1 depending on the client's timezone. If dateStr is 2020-06-17T00:00 then the date object is created as expected no matter where the client is located.
Assuming the above, which I hope is clearer now, creating String objects over and over again is a memory stress that I should consider or is it something Java handles with no problem and I shouldn't worry about this?
My question was closed and I was told to edit it to be more focused. After editing my question, how can I re-open my question to answers?
As you have discovered, storing dates in terms of days since some epoch only works if everyone who uses your system is using the same time zone. If two different users in different time zones have a different idea about the date on which some event occurred (e.g., the person in New York says that the system crashed on Sunday night, but the the person in Hong Kong says it crashed on Monday morning), then you have to store the time zone in which the event occurred in order to show the date of that event accurately.
But if that's the situation you're in, why not just store the time zone along with the date? There's no compelling reason to combine the date and timezone into a string.
When you parse a ISO-formatted timestamp into a LocalDate using only the first 10 characters, be aware that you're losing the time zone information. Implicitly the LocalDate that you get is in the time zone of the original timestamp. So if the original timestamp is New York time, and you take the date part and add 1 day, then you'll get the next day in the New York time zone. But if you then take the date from a second timestamp, you can't compare it to the date you got from the first timestamp, in terms of determining if it represents the "same day." You can only test for "same day" if both dates are implicitly in the same time zone.
UPDATE
After reading your additional comments, I realize that what's happening is this. You have a date stored in your database, like 2020-06-15. You send that to the UI as the string '2020-06-15' and then do new Date('2020-06-15') and then you're surprised when you render the date in the UI and get June 14!
This is the transformation that happens:
The string '2016-06-15' gets parsed into a JavaScript Date representing midnight UTC on the June 15.
When you render the date, it gets converted into a string using the browser's local time zone, which (if you're in the United States) will give you June 14, because at midnight UTC on June 15 it's still June 14 in all time zones west of Greenwich.
You discovered that if you make the string "2020-06-15T00:00" that it works, because now JavaScript uses the browser's local time zone to parse the string. In other words, this string means midnight local time, not UTC, on June 15. So now the sequence is:
'2020-06-15T00:00' gets parsed using the local time zone and becomes June 15 4:00AM UTC.
When you render the date, it gets converted back to local time and is rendered as June 15.
The easiest way to avoid all this messiness is just to send the regular date string '2020-06-15' to the UI and render it using DateTimeFormat, specifying the time zone as UTC:
new Intl.DateTimeFormat('en-US', {timeZone: 'UTC'}).format(d)
Since dates in JavaScript are always UTC, and you're asking DateTimeFormat to output the date in UTC, no date shift occurs.
You could also use the Date methods getUTCFullYear, getUTCMonth, etc. to get the date components and format them however you like.
Once you're no longer sending dates back and forth with "T00:00" appended, you can just use LocalDate on the Java side.
Don't spend even a second worrying about the time required to manipulate strings. Think about the incredible amount of string manipulation that is necessary to build even a simple web page. A few more strings here and there isn't going to make a difference.
I have been reading different articles on the said question yet i am unable to figure out what should be the best strategy to store the date in db.
I will be receiving the ISO8601 date via path-param in a rest call. What I have decided
Use Joda-Time to parse the date.
Extract UTC-0 time out of the date and the hours offset
Store UTC-0 in DateTime datatype in mysql db and store offset in varchar(5).
When I have to search something based on the date (an exposed rest api). I will use the search criteria (input date) extract the UTC-0 time and hours offset and compare the two columns in the db i.e. where table.dateInUTC0 = :inputDateInUTC0 AND table.hoursOffset = :inputHoursOffset
I am not sure about step 4. Am i doing i right ?
I am not sure about step 4. Am i doing i right ?
Really, it depends on what you are trying to do.
If you want the search to only match if the searcher is using the same timezone as the original data, then you are doing it right.
If you don't want that, you are doing it wrong.
Ask yourself this: If you enter the same date / time in your local time zone and UTC (or some other time zone), do they mean the same thing for clients of your server? Should they?
Quick summary of my issue first, then details further below.
I have a Calendar date with 00:00:00 as the time as it's not important to the business. This value is sent to a webservice which generates a XML in which the value ends up in the following format : 2014-09-12T07:55:07.000Z. I have noticed that this is the original value converted to a +0 timezone (UTC) (ours is CET, +1, but currently CEST, +2, because of DST).
I currently have no idea whether the system reading the XML takes timezones into account or would extract 2014-09-12 and assume it's in the +2 timezone.
What I've noticed is that sending "2014-09-12 00:00:00" local time (tz +2) ends up as 2014-09-11T22:00:00.000Z in the XML. No big surprise, it converted it... but if it's interpreted as is by the other system, it will think the date is a day earlier than it should be.
What can be done to ensure this gets interpreted as intended?
I was thinking of using noon instead of midnight to make sure timezone shifts wouldn't impact interpretation, but it feels like a dirty trick. Or maybe I should cheat and have the Calendar timezone be +0 so it's not time-shifted when put in the XML?
Q&A
Why do you "send a Calendar to a webservice"?
The application is in Coldfusion 7. To communicate with SOAP webservices, the server generates a set of Java classes that fit the definition of the argument expected by the webservice. The argument is apparently a single big object with a lot of attributes and sub-attributes. So one instantiates the main Java class and uses setters and further instanciations of other classes to "fill out" all the attributes.
Do you have to use Calendar?
Yes, the Java object definition cannot be changed. It expects Calendar for all dates.
What's this 2014-09-11T22:00:00.000Z format?
I have no idea. This seems to be what the end system expects for dates.
You should use JODA
Unless JODA classes extend Calendar and are compatible with Java 1.3 (current Java version on the Coldfusion server -- yes it's old), I doubt it will work.
How much can you do on the other system?
The other system is the responsibility of a different team and is apparently very hard to change. I expect the solution will have to be found on the side of our application.
Although the time value in your Calendar object is not important to your business, it is important to the webservice that you use and have no control over. The calendar object specifies an instant in time, so you must make sure that instant is in the day that is important to you. I recommend you use midday, as you suggested already. I also recommend that you create your Calendar object in the UTC timezone:
Calendar myCalendar=Calendar.getInstance(TimeZone.getTimeZone("UTC"));
How to get client/request timezone in jsp?
Unfortunately this information is not passed in HTTP headers.
Usually you need cooperating JavaScript to fetch it for you.
Web is full of examples, here is one http://www.coderanch.com/t/486127/JSP/java/Query-timezone
you cannot get timezone, but you can get current time from client side.i.e. through javascript and than post back. On server side, you can convert that time to GMT/UTC. The UTC shows the TimeZone.
If you just need the local timezone in order to display local times to the user, I recommend representing all times in your service in UTC and rendering them in browsers as local times using Moment.js.
My general rule is to handle and store times in UTC everywhere except at the interface with the user, where you convert to/from local time. The advantage of UTC is that you never have to worry about daylight-saving adjustments.
Note that if you want to show the age of something (e.g. "posted 3 hours ago") you just need to compare the UTC timestamp with the current UTC time; no need to convert to local times at all.
Best solution for me is sending date/time as a string, and then parse with server's timezone to get a timestamp. Timestamps are always UTC (or supposed to be) so you will not need client's TimeZone.
For example, sending "10/07/2018 12:45" can be parsed like:
SimpleDateFormat oD = new SimpleDateFormat();
oD.applyPattern("dd/MM/yyyy HH:mm");
oD.setTimeZone(TimeZone.getDefault()); // ;)
Date oDate = oD.parse(request.getParameter("time"));
Obviously you can set your specific date/time format.
I have 2 different computers, each with different TimeZone.
In one computer im printing System.currentTimeMillis(), and then prints the following command in both computers:
System.out.println(new Date(123456)); --> 123456 stands for the number came in the currentTimeMillis in computer #1.
The second print (though typed hardcoded) result in different prints, in both computers.
why is that?
How about some pedantic detail.
java.util.Date is timezone-independent. Says so right in the javadoc.
You want something with respect to a particular timezone? That's java.util.Calendar.
The tricky part? When you print this stuff (with java.text.DateFormat or a subclass), that involves a Calendar (which involves a timezone). See DateFormat.setTimeZone().
It sure looks (haven't checked the implementation) like java.util.Date.toString() goes through a DateFormat. So even our (mostly) timezone-independent class gets messed up w/ timezones.
Want to get that timezone stuff out of our pure zoneless Date objects? There's Date.toGMTString(). Or you can create your own SimpleDateFormatter and use setTimeZone() to control which zone is used yourself.
why is that?
Because something like "Oct 4th 2009, 14:20" is meaningless without knowing the timezone it refers to - which you can most likely see right now, because that's my time as I write this, and it probably differs by several hours from your time even though it's the same moment in time.
Computer timestamps are usually measured in UTC (basically the timezone of Greenwich, England), and the time zone has to be taken into account when formatting them into something human readable.
Because that milliseconds number is the number of milliseconds past 1/1/1970 UTC. If you then translate to a different timezone, the rendered time will be different.
e.g. 123456 may correspond to midday at Greenwich (UTC). But that will be a different time in New York.
To confirm this, use SimpleDateFormat with a time zone output, and/or change the timezone on the second computer to match the first.
javadoc explains this well,
System.currentTimeMillis()
Note that while the unit of time of the return value is a millisecond, the granularity of the value depends on the underlying operating system and may be larger. For example, many operating systems measure time in units of tens of milliseconds.
See https://docs.oracle.com/javase/7/docs/api/java/util/Date.html#toString().
Yes, it's using timezones. It should also print them out (the three characters before the year).