The way Java time handles simple things like timestamps leaves me a little taken aback. Maybe I'm doing it wrong.
I want to generate an ISO 8601 timestamp string. The obvious way would be to create an Instance.now() and format it use DateTimeFormatter.ISO_INSTANT to format it. Except that Instance has no format() method, so I can't format it with the "instance" formatter. Imagine that.
So I have to create a ZonedDateTime from the instance. It shouldn't matter which zone I choose, so I choose UTC because that's what DateTimeFormatter.ISO_INSTANT is going to put it in anyway:
Instant.now().atZone(ZoneOffset.UTC).format(DateTimeFormatter.ISO_INSTANT)
Now I get back a string, and obviously I want to get it back to a ZonedDateTime instance, because that's what I used to format it with! So I try:
ZonedDateTime.parse(timestamp, DateTimeFormatter.ISO_INSTANT)
It complains at me that there is no time zone information. But I used a ZonedDateTime to generate it --- it's not my fault that it wouldn't let me use an Instance to format it.
Oddly Instance doesn't have a format() method, but it does have a parse() method, which, again oddly, is parsed using DateTimeFormatter.ISO_INSTANT (even though I can't format an Instance using that formatter).
As pointed out in the comments, I can use DateTimeFormatter.ISO_INSTANT.format(Instance.now()) to format an instance directly, so why isn't there an Instance.format(…)?
So just generating and parsing a timestamp seems convoluted and inconsistent. Or am I doing it wrong?
First, you can just DateTimeFormatter.ISO_INSTANT.format(Instant.now()).
Next, I don't see why you expect to parse back what you've formatted. Formatting is not guaranteed to be a lossless operation. If you format a LocalDateTime with just the year, you wouldn't expect to parse it back to LocalDateTime, would you?
And of course you can parse Instant using DateTimeFormatter.ISO_INSTANT. DateTimeFormatter.ISO_INSTANT.parse(text, Instant::from) - this is what Instant.parse(...) does.
There is no need for a format(DateTimeFormatter) method in Instant, because toString() does the job. The toString() method of Instant is implemented as:
return DateTimeFormatter.ISO_INSTANT.format(this);
Thus, all you need to do is call instant.toString().
To round-trip parse this back in, simply use parse():
Instant instant = Instant.parse(text);
Why do you need to respecify the formatter when parsing back the string?
Can't you just do ZonedDateTime.parse(timestamp);?
Related
I am converting milliseconds into ZonedDateTime
Long lEpochMilliSeconds = 1668415926445;
System.out.println(ZonedDateTime.ofInstant(Instant.ofEpochMilli(lEpochMilliSeconds),ZoneId.of("UTC"));
It gives output:
2022-10-28T12:59:34.939Z[UTC]
I don't want the time zone "[UTC]" part in my output.
I need my out to be like this in ZonedDateTime format:
2022-10-28T12:59:34.939Z
I need the format in ZonedDateTime only not string, as I will be returning the value & use it somewhere else
I need the format in ZonedDateTime only not string
This is like saying "I need an int that knows it's in hex rather than decimal". There's just no such concept.
If you need to format the value in a particular way, you should apply that format where you do the formatting.
It's possible that what you should actually do is return an Instant instead of a ZonedDateTime. That will format the way you want by default, although it's still not "part of the object" - just the default format for all instants.
It's important to understand the difference between "the value being represented" (and the type you're using to represent that value) and "a string representation of that value". You should try use the semantically-appropriate type for what you're trying to represent (e.g. ZonedDateTime, Instant etc) for as much of the time as possible, only converting to and from string representations at system boundaries. Those system boundaries need to be aware of the expected textual representation, and perform the appropriate conversion, rather than expecting a particular string representation to travel with the value itself through the system.
It is giving me the desired output when I gave ZoneOffset as an argument instead of ZoneId.
ZonedDateTime.ofInstant(Instant.ofEpochMilli(lEpochMilliSeconds), ZoneOffset.UTC);
I'm using ibm's MessageFormat library to localize an incoming date.
The task here is to run a few checks on the date before showing it to the end user. I get a ZonedDateTime object and I need to make sure that it doesn't fall in the weekend, which I do using the getDayOfWeek.
My problem happens when I try to convert my date to a string using MessageFormat. Since MessageFormat accepts only java.util.Date objects, I convert my ZonedDateTime -> Instant -> Date. Unfortunately, this method results in my "Monday" becoming a "Sunday," as shown below.
I noticed that this "loss" happens upon the Date conversion. This is because the Date.toString() object is being invoked by MessageFormat, and the former uses the JVM's default timezone (in my case, PST). As a result, my UTC gets implicitly converted to a PST and I lose a day.
Any ideas how to tackle this? Is there anything else that I can pass to MessageFormat? Is there a way to use Date but not get this undesired behavior? Is there another localization library I can use?
Internally, MessageFormat uses a DateFormat object but does not allow you to set its timezone. #Assylias linked a question where the answer tries to pull out the internal DateFormat, set its timezone, and then use the MessageFormat as usual, which resolves the issue.
However, I found that to be too wordy, particularly because you have to create a new MessageFormat everytime (as opposed to reusing the MessageFormat that you already set the timezone for).
What I opted for was to simply use SimpleDateFormat directly.
// I have a ZonedDateTime zonedDateTime that I want to print out.
final SimpleDateFormat dateFormat = new SimpleDateFormat("EEEE, MM dd", locale);
dateFormat.setTimeZone(TimeZone.getTimeZone(zonedDateTime.getZone()));
final String formattedDateString = dateFormat.format(Date.from(zonedDateTime.toInstant()));
I then use String.format to insert my formatted date into a larger string. Hope this helps.
I am developing a library to store complex data in an Object. One of the field in this object is a date. When I set the date using setter method, it is assumed that the date object is in GMT timezone. Internally, the Date is stored as a long with the number of milliseconds from epoch. In my get() method, I am doing the following :
return new Date(storedDateinMilliseconds);
The problem is that if anyone is calling toString() on the returned object, it uses the default timezone to return the date. Therefore, the returned date does not always match with the date provided in GMT. Is there a way to fix this? so that the user of this implementation will always get the GMT date when they invoke toString()?
I tried the following :
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
But this would modify the default timezone of the application using it.
As Sotirios Delimanolis’ comment said, you are giving a java.util.Date object to the calling programmer. What she does with it is up to her. And it is up to her to understand all the goofy problems that come with that object including its toString method applying the JVM’s current default time zone in generating a string representation of its date-time value.
If you want to return a date-time value with an assigned time zone, then return a different object.
Alternative Date-Time Objects
You have at least three alternatives to returning a java.util.Date object.
java.time
In Java 8 and later, the obvious choice is to use the new java.time framework (Tutorial). Give the calling programmer a ZonedDateTime object which is basically a Instant object plus a ZoneId object.
Tip: When specifying a time zone, use a proper time zone name. Never use the 3-4 letter codes like EST or IST.
Joda-Time
Joda-Time was the inspiration for java.time. This 3rd-party library is excellent, and well-worn from popular use. It supports multiple versions of Java and Android too.
The DateTime class is a moment on the timeline plus a time zone, similar to java.time’s ZonedDateTime.
ISO 8601
A third alternative is to give the calling programmer a string representation of a date-time value. The obvious choice of formats is to use those defined by the ISO 8601 standard. These formats are sensible, well thought-out, and unambiguous.
2015-09-16T18:06:14Z
…or…
2015-09-16T11:06:14-07:00
Both java.time and Joda-Time use these formats by default in parsing and generating strings. The formats are wisely extended by java.time to append the proper name of the time zone in square brackets.
2015-09-16T11:06:14-07:00[America/Los_Angeles]
Never Adjust Default Time Zone
You stated that setting the default time zone affected your entire app. Wrong. It affects all code of all apps in all threads running in that JVM. Worse, it does so immediately during runtime while that other code is running.
Set the default time zone only as a last resort when all other approaches to resolve a date-time problem have been exhausted. This is rare. The usual solution is to:
Use java.time or Joda-Time.
Always specify the desired/expected time zone rather than rely implicitly on the default.
Use UTC in most of your business logic, data storage, and data exchange.
Avoid whenever possible the mess that is java.util.Date/.Calendar.
Search StackOverflow
All of these topics have been discussed many times over on StackOverflow.com. Please search for more information and examples.
AFAIK, you have 2 options:
Option 1. This may sound like an overkill, but you could roll out your own Date object just for this complex class of yours, and overwrite the toString() method. Maybe something like
public class GMTDate extends java.util.Date {
#Override
public String toString() {
//return GMTDate
}
}
Option 2: Keep your date as java.util.Date, but don't expose a public getter for it. Expose instead a public getter that returns your date in GMT format, and maybe a public getter that returns your date as a long (with the number of milliseconds from epoch)
EDIT:
And a 3rd option: AspectJ. You can use aspect oriented programming to intercept calls to the toString() method and return a GMT string date
Relevant Stack Overflow Question: AspectJ: Intercept method execution/call and make it return
Using the joda-time-2.0 version library, I was wondering, which of this functions is better to construct from an ISO date (suposed XML xs:dateTime format): new DateTime(String) versus DateTime.parse(String).
Because both return a different result from same value. Example:
new DateTime("2012-08-16T07:22:05Z")
DateTime.parse("2012-08-16T07:22:05Z")
Resulting different because of the ISOChronology. First says is ISOChronology[Europe/Paris] and second ISOChronology[UTC], although milliseconds are the same.
Also, here recomends to use ISODateTimeFormat.dateTimeNoMillis(), giving the same result as using the first version (new).
The two methods use two different conversion methods: the constructor uses an instance of InstantConverter, which in case of strings is a StringConverter and which doesn't yet support reading the timezone from the passed string, while the parse method uses a DateTimeFormatter which knows how to parse the timezone.
Although both formats in theory accept an ISO datetime format, I consider that the constructor is buggy since it always uses the system timezone instead of the one specified in the string. This is inconsistent with the other possible values accepted by this constructor, which do take into account a chronology with its timezone offset. For example, this constructor will return a DateTime object with the UTC timezone:
new DateTime(DateTime.parse("2012-08-16T07:22:05Z"))
I'm getting an Xml representation of an XmlObject using the xmlText() method. The XmlDateTime objects are coming out with timezone offsets at the end of the string which is valid according to XML Schema: dateTime. Is there any way to force the XmlObject to convert to xml with the Zulu formatting?
Getting this: 2002-10-10T12:00:00-05:00
and need this instead: 2002-10-10T17:00:00Z
I was asking about the instantiation of the XmlDateTime object because I ran into a similar issue a while ago. From what I could figure out, the way the XmlDateTime is printed to xml depends on the value of the internal representation, which in turn depended on the setter which was invoke to provide that value. The issue was with the setDate(...) method.
The default implementation of XmlDateTime keeps the value of the datetime internally as an org.apache.xmlbeans.GDate which is built using a GDateBuilder. When you set the date on the XmlDateTime object it eventually passes the value onto a GDateBuilder.
If you look at the source of the setDate() method, the javadoc states that:
Sets the current time and date based on a java.util.Date instance.
The timezone offset used is based on the default TimeZone. (The default TimeZone is consulted to incorporate daylight savings offsets if applicable for the current date as well as the base timezone offset.)
If you wish to normalize the timezone, e.g., to UTC, follow this with a call to normalizeToTimeZone.
Since the XmlDateTime object has a setGDate(...) method you can test the normalize method like this:
XmlDateTime xmlDateTime = XmlDateTime.Factory.newInstance();
xmlDateTime.setStringValue("2002-10-10T12:00:00-05:00");
System.out.println(xmlDateTime.xmlText());
GDateBuilder gdb = new GDateBuilder(xmlDateTime.getDateValue());
gdb.normalize();
xmlDateTime.setGDateValue(gdb.toGDate());
System.out.println(xmlDateTime.xmlText());
For me this printed:
<xml-fragment>2002-10-10T12:00:00-05:00</xml-fragment>
<xml-fragment>2002-10-10T17:00:00Z</xml-fragment>
That was the only way that I could get it to print in UTC.
I hope there is a better way, although sadly I couldn't find it...