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.
Related
I have a rest service that takes date as a string and an adapter parses it to java.util.date:
private static final String FORMAT_DATE = "dd.MM.yyyy";
/*
* Omitted.
*/
SimpleDateFormat sdf = new SimpleDateFormat(FORMAT_DATE);
sdf.setLenient(false);
try {
setTime(sdf.parse(stringRealizationDate).getTime());
invalidDate = false;
} catch (ParseException parseException) {
invalidDate = true;
LOG.error("Instantiation failed");
}
The stringRealizationDate is: 23.02.2017 and after parsing it it becomes 22.02.2017 23:00.
How to make it always be 23.02.2017 00:00 without any time zones aspects? It cannot be shifted under no circumstances.
You cannot rely on parsing a String not explicitly containing timezone information without somehow defining the timezone.
The SimpleDateFormat internally uses a Calendar to store the parsing result. This calendar will be instantiated with system locale and therefore system timezone. Your stringRealizationDate will be treated as being passed with the context of system timezone which seems to be +01:00.
Now, SimpleDateFormat.parse() returns a Date. Date however is intended to store UTC values, so the +01:00 timezone offset causes to have one hour being substracted during conversion to UTC.
One solution would be having the correct timezone (+01:00) set when using your Date so the calculation from UTC to +01:00 would result in the correct value being e.g. printed on screen. But that is some kind of tricky to manage in larger applications.
Another solution would be using SimpleDateFormat.setTimeZone() before calling parse() to explicitly define the timezone the formatter should assume your input has (if not defined there explicitly). However, the problem with having a Datethat always carries UTC timezone values is not solved at all.
The best solution would be using the new Time API which is available since Java8. There you have classes explicitly holding non-timezoned values, in your case LocalDate.
The interesting method there would be LocalDate.parse(CharSequence text,
DateTimeFormatter formatter) since it allows you to pass your input String and define your expected date format. But be alert, you cannot convert a LocalDate to a Unix timestamp without passing a timezone (and making assumptions about the time parts of it) since it does not represent a point-in-time but a fragment of it, the date part.
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);?
I would like to start by saying that I've read several threads similar to this one, but none of them really solved my problem.
I would also like to state that I've tried to use SimpleDateFormat and joda.DateTime without any success.
The problem is the following:
I have a Calendar object that holds the information about a specific date: 2008-04-30T00:00:00Z
When using the calendar.getTime() method I can get different results because I know that that method is looking for the local value
Thus:
UK: 2008-04-30T01:00:00.000+0100
US: 2008-04-30T20:00:00.000-0400
But I would like to get a Date object that holds just the Date and Time values "2008-04-30T00:00:00" ignoring completely any timezone.
How can I do that?
As I mentioned before I tried to use
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss")
but I always end up with the same results.
Any help would be really appreciated
Cheers.
Found out that you can clear the Timezone by using code below:
Calendar cal = Calendar.getInstance();
cal.clear(Calendar.ZONE_OFFSET);
Calendars and Dates mean nothing without a TimeZone.
Calendars and dates cannot exist without a timezone.
You can't ignore completely any timezone.
You can create a Calendar for Greenwich Mean Time (offset zero) like this:
TimeZone zone = TimeZone.getTimeZone("Etc/GMT");
Calendar cal = Calendar.getInstance(zone);
This represents a Date/Calendar that is only meaningful in the GMT timezone.
It sounds like you want a timestamp, which represents an instant in time.
As others have pointed out, Calendar and Date objects cannot exist without a time zone.
I believe you may want to use the LocalDateTime class introduced in Java 8 with the new time API:
LocalDateTime literal = LocalDateTime.of(2008, 4, 30, 0, 0, 0);
LocalDateTime parsed = LocalDateTime.parse("2008-04-30T00:00:00"); // ISO-8601 by default
Assert.assertEquals(literal, parsed);
Do you use a standard constructor for initializing Calendar? What if you used the constructor which allows to specify the time zone and locale?
protected Calendar(TimeZone zone, Locale aLocale)
Old, but still incorrect.
"When using the calendar.getTime() method I can get different results because I know that that method is looking for the local value"
That is a misconception. getTime() will get the Milliseconds only. Countet as GMT.
ONLY during formatting of the Output the time zone becomes relevant. Sind the original poster did not show the code, it can not be decided, where the error occurs.
I'd like to insert the value derived from this JXDatePicker into a Date field in Java DB. How should I get ONLY date off this controller in a way that time is represented as dd/mm/yyyy and nothing else??!
You can get the Date from the JXDatePicker.getDate() and then use a SimpleDateFormat to format the date into the required format.
Try:
SimpleDateFormat formater = new SimpleDateFormat("dd/MM/yyyy");
formater.format(myDatePicker.getDate());
Notice that instead of the mm that you've used I used MM to represent the month. Please see the SimpleDateFormat javadoc for pattern letters you can use.
Follow-Up
I feel compelled to mention, for completeness, that it is generally a bad idea to put formatted strings representing dates into a database; what happens when you want to display it in another format, for instance, or do a simple comparison using SQL.
One way to store date/times is to use the timestamp that you get from Date.getTime(). Here's the Date class' getTime() javadoc:
Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT
represented by this Date object.
Storing this representation of a Date in your database makes it much simpler to create a Date object when you retrieve the timestamp:
Long myTimeStamp = getTimeStampFromResultSet();
Date date = new Date(myTimeStamp);
Or use the column in SQL to do a simple comparison:
SELECT * FROM MY_TABLE WHERE MY_DATE > ?
It also makes it somewhat portable so you can, for instance, send the timestamp to a thin client that is built using a different technology.
That being said, it is also in your best interest to use a date and time library like Joda Time instead of using the unreliable and inconvenient Java Date or Calendar classes.
I want to save a Date object to a readable string (for example 22/10/2009 21:13:14) that is also parsable back to a Date object.
I have tried many things and the best I could find was to use DateFormater for parsing and formating but it has a setback. When you format a date you lose seconds information. I tried to find if there is an option to format it and display the seconds (even better would be to the millisecond level since that's the resolution the Date object allows you to have) but I came up short.
Any ideas?
Take a look at java.text.SimpleDateFormat
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss.SSS");
Date dt = new Date();
String S = sdf.format(dt); // formats to 09/23/2009 13:53:28.238
Date dt2 = sdf.parse(S); // parses back
SimpleDateFormat can format and parse a date based on a very simple pattern system that include second and even milliseconds.
Other answers are all good.
But when doing this kind of thing please pick a format that sorts properly when coded as a string.... "yyyy/MM/dd HH:mm:ss" is fine. It always astounds me when software engineers pick a date format which doesn't sort in the obvious, convenient way.
You'll save your fellow developers a lot of pain at some distant point in the future - think of it as good karma :-)
ISO 8601
Use ISO 8601 format.
It’s flexible, it includes seconds and fraction of second if there are any, but you may also leave them out if they are 0.
It’s standard, so more and more tools format and parse it. Great for serialization for storage or data interchange.
It goes like 2009-10-22T21:13:14, I should say it’s pretty human-readable (though the T in the middle that denotes the start of the time part may feel unusual at first).
The strings sort properly, as mikera requested in another answer, as long as the years are in the four-digit range from 1000 through 9999.
The classes of java.time, the modern Java date and time API, as well as those of Joda Time parse ISO 8601 as their default, that is, without any explicit formatter, and produce the same format from their toString methods.
A modest demonstration of using java.time:
LocalDateTime dateTime = LocalDateTime.of(2009, 10, 22, 21, 13, 14);
String readableString = dateTime.toString();
System.out.println(readableString);
LocalDateTime parsedBack = LocalDateTime.parse(readableString);
System.out.println(parsedBack);
This prints two identical lines:
2009-10-22T21:13:14
2009-10-22T21:13:14
The latter System.out.println() call implicitly calls toString() once more, so this shouldn’t surprise.
A little off-topic, but I always feel the need to remind people that DateFormat and SimpleDateFormat are not thread safe! The Sun documentation clearly states this, but I keep finding code out in the wild where people stick a SimpleDateFormat in a static ...
If you want to do it a little simpler, and be spared from making your own DateFormat that most other Answers involve, you can leverage the default format in java.time.Instant:
(new Date()).toInstant.toString();