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"));
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 a Java service which needs to return date/time information that is formatted relative to a user's current timezone (C#). For example, say a user is on the west coast (pacific time) where it is 8:00pm. They connect to a service that is hosted in the midwest (central time) where it is 10:00pm. If the user were to ask the server for the current time, the server should respond, "8:00pm" for the user.
My thought is that the client application (C#) will pass information to the service about its current timezone or UTC offset. Java will then create/format all dates using that timezone.
However, I am having trouble finding a good way to pass C# System.TimeZone information in a way where Java can create and use a java.util.TimeZone object. I can get the UTC offset from C# but not a three digit timezone code. In Java I can create a TimeZone from a three digit code but can't find a way to create one from a UTC offset. From everything I've seen in Java, TimeZones are created with a code ("PST") or country/region ("America/Los_Angeles"), and I don't believe there is a clear way to get the timezone in this format in C#.
How might this be accomplished?
Work In UTC
As the comment suggested, usually the best way to go is to work your business logic in UTC. Adjust into a local time zone only for presentation when expected by a user.
So your Java backend should be returning a UTC date-time value. Usually the best way to do that is to serialize the date-time value as a string in the standard ISO 8601 format. Then let the client app handle the presentation by generating a string representation of the date-time value adjusted into a particular time zone.
See this Question about best practices for date-time work.
But somehow this is not feasible in the context of this Question. So the client app needs to communicate to the backend the desired/expected time zone.
Time Zone
Avoid the 3-4 letter codes such as EST or IST. These codes are neither standardized nor unique. Furthermore they invoke Daylight Saving Time in a confusing way.
Instead use official time zone names. These are mostly in the format of "continent", slash, and "region/city" in English, such as America/Montreal or Asia/Kolkata.
.Net Fails To Support Proper Time Zone Naming
Unfortunately, it looks like the .Net team did not know about proper time zone naming.
The System.TimeZone class offers properties such a StandardName. But the examples in the System.TimeZone doc show "Pacific Standard Time" rather than a proper name such as "America/Los_Angeles".
Noda Time
My first suggestion is to consider using the Noda Time project, an alternative date and time API for .NET. It was inspired by the highly successful Joda-Time library in Java, which in turn inspired the new java.time framework built into Java 8 and later.
Looks like Noda Time has support for proper time zones. Rather than use System.TimeZone, use Noda Time to obtain the time zone information.
Roll Your Own Mapping
If Noda Time is not an option, then I might look to see if my users are all in a few time zones. If so, I would make my own mapping of such as "Pacific Daylight Time" returned by C# the standard name for a time zone being the proper name "America/Los_Angeles".
Java application writes events to a log file, including a timestamp (as returned from Date.toString()), which in turn includes the time zone. On the Windows machines I use, I see the string returned by Date.toString() having the time zone represented as a three-character string (e.g. "CST"). But on some customer machines, the dates are being written to the log file with the time zone represented as an offset from GMT (e.g. "GMT-06:00").
We have a tool that parses the text of log files for various pieces of information, but unfortunately, its original implementation assumed the three-character representation and won't work on those log files that have the GMT-offset representation. We've fixed the tool to be indifferent to that now, but we'd like to be able to advise customers who are running an old version and are having this problem due to their strings having GMT-offset time zones, that they can get the tool to start working if they change their system settings so that their logs files are written with three-character string time zone strings going forward. Additionally, we'd like to account for this variability in our future test plans, ensuring that we test things using each setting. But I haven't been able to determine just what in Windows setting tells Java to use "CST" vs. use "GMT-06:00".
I see a couple of time-zone related registry settings, but nothing that I can clearly identify as controlling that particular choice. Some of the registry settings refer to tzres.dll. Is the choice baked into that? Is there any simple way on Windows to get Date.toString() to formulate its string using one time zone representation vs. using the other?
I don't know the exact cause of this difference in behavior, but I can guess. A time zone is an offset from UTC and history & info about anomalies for that particular are such as Daylight Saving Time. Some machines (or the JVM default) may be set to only an offset rather than a specific named time zone.
The java.util.Date class is notoriously troublesome in general, and should be avoided. Specifically, the toString is terrible in two ways. (A) The format it uses to generate the string is bad, as you have discovered. (B) the JVM's default time zone is applied. That application causes confusion as it implies a Date has a time zone when in fact it does not. This method should only be used temporarily for quick-and-dirty purposes, never for logging.
Use a decent date-time library. That means either Joda-Time or the new java.time package in Java 8. Both use the sensible and useful ISO 8601 format by default.
Generally best practice is to do your logging in UTC time zone (no offset).
Example: 2014-05-04T10:36:34Z
To generate such a value in Joda-Time:
String output = new DateTime( DateTimeZone.UTC ).toString();
If your question is, "How do I change the time zone used by a JVM running an app I cannot alter?", one solution is setting the JVM's time zone by passing an argument when launching the JVM. See this question.
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 :)
I'm setting the standards for our application.
I've been wondering, what default date format should I choose to use ?
It should be:
Internationalization & timezone aware, the format should be able to represent user local time
Can be efficiently parsed by SimpleDataFormat (or alike, jdk classes only)
Programming Language agnostic (can parse in java, python, god forbid C++ :) and co.)
Preferably ISO based or other accepted standard
Easy to communicate over HTTP (Should such need arises, JSON or YAML or something in this nature)
Can represent time down to seconds resolution (the more precise the better, micro seconds if possible).
Human readable is a plus but not required
Compact is a plus but not required
Thank you,
Maxim.
yyyy-MM-ddThh:mmZ (See ISO 8601) You can add seconds, etc
You can read it easily, it will not be a problem for SimpleDateFormat.
The most canonical and standard form is probably "Unix Time": The number of seconds elapsed since midnight Coordinated Universal Time (UTC) of January 1, 1970.
If you set that as the default time-format you can easily parse it, store it in memory, write it to disk, easily communicate it over HTTP and so on. It is also definitely an accepted standard, and in a sense it is "time-zone aware", since it is well-defined regardless of time-zones.
(This is the format in which I always store all my time stamps; in databases, in memory, on disk, ...)
The "right" default format really depends on what you're doing with it. The formats for parsing, storing, and displaying can all be different.
For storing the date you're (almost) always going to want to use UTC as aioobe says, even when you want to display it in user local time. I say "(almost)" but I really can't think of a case where I would not want UTC for a saved date. You may want to store the TZ information for where the date originated also, so you can report it in that local time, but more often you want to display the local time for the whoever is currently looking at the date. That means having a way to determine the current user's local time regardless of what the original local time was.
For displaying it, the "default format" should usually be determined by the viewers locale. 08/09/10 usually means 2010-Aug-9 in the U.S. ("Middle endian") but normally means 2010-Sep-8 in most of the rest of the world ("Little endian"). The ISO-8601 format "2010-09-10" is safe and unambiguous but often not what people expect to see. You can also look over RFC-3339 for Date and Time on the internet and RFC-2822 for message format (transmitting the date)
For parsing a date, you'll want to parse it and convert it to UTC, but you should be fairly flexible on what you accept. Again, the end users Locale and timezone, if discoverable, can help you determine what format(s) of string to accept as input. This is assuming user-typed strings. If you're generating a date/time stamp you can control the form and parsing will be no problem.
I also second BalusC link which I hadn't seen before and have now favorited.