SimpleDateFormat + java.sql.Timestamp in Jasper Reports - java

I have a timezone-delicate report in Jasper and I can't really seem to figure out how to show a few dates relative to a timezone.
I have a view which returns dates with the following format:
"2015-03-02 11:45:00+01"
"2015-03-02 23:59:59+01"
"2015-03-03 00:00:00+01"
"2015-03-03 08:00:00+01"
"2015-03-03 09:20:00+01"
"2015-03-03 11:00:00+01"
"2015-03-03 09:00:00+01"
"2015-03-03 09:30:00+01"
etc (notice the +01 at the end)
In my report, I have:
new SimpleDateFormat("HH:mm", $P{REPORT_LOCALE}).format($F{start_date});
However, for example, for "2015-03-02 11:45:00+01" I don't get 12:45 shown, I get 11:45.
Also, I need to sum-up the hours (they're intervals) and this gives me a 1hr (in this case) error.
Can anyone help me show the correct hour?
Thanks!

SimpleDateFormat takes a Date, not a Calendar - which means it can't be provided the time zone in the value itself.
Assuming you need to stick with SimpleDateFormat (rather than using Joda Time or Java 8's java.time, for example) then you'll need to set the time zone on the SimpleDateFormat itself. If you need to take the time zone from the data (rather than having a report-wide zone) then you'll need to call setTimeZone before formatting each value - but of course, you'll also need to make sure you've got the time zone in the value, and java.sql.Timestamp doesn't have any notion of a time zone, as far as I'm aware.

Related

How can I get the UTC-converted Java timestamp of current local time?

Could somebody please help with getting UTC-converted Java timestamp of current local time?
The main goal is to get current date and time, convert into UTC Timestamp and then store in Ignite cache as a Timestamp yyyy-MM-dd hh:mm:ss[.nnnnnnnnn].
My attempt was Timestamp.from(Instant.now()). However, it still considers my local timezone +03:00. I am getting '2020-02-20 10:57:56' as a result instead of desirable '2020-02-20 07:57:56'.
How can I get UTC-converted Timestamp?
You can do it like this :
LocalDateTime localDateTime = Instant.now().atOffset(ZoneOffset.UTC).toLocalDateTime();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
System.out.println(localDateTime.format(formatter));
Don’t use Timestamp
You most probably don’t need a Timestamp. Which is good because the Timestamp class is poorly designed, indeed a true hack on top of the already poorly designed Date class. Both classes are also long outdated. Instead nearly 6 years ago we got java.time, the modern Java date and time API. Since JDBC 4.2 this works with your JDBC driver too, and also with your modern JPA implementation.
Use OffsetDateTime
For a timestamp the recommended datatype in your database is timestamp with time zone. In this case in Java use an OffsetDateTime with an offset of zero (that is, UTC). For example:
OffsetDateTime now = OffsetDateTime.now(ZoneOffset.UTC);
System.out.println(now);
PreparedStatement statement = yourDatabaseConnection
.prepareStatement("insert into your_table (tswtz) values (?);");
statement.setObject(1, now);
int rowsInserted = statement.executeUpdate();
Example output from the System.out.println() just now:
2020-02-22T13:04:06.320Z
Or use LocalDateTime if your database timestamp is without time zone
From your question I get the impression that the datatype in your database is timestamp without time zone. It’s only the second best option, but you can pass a LocalDateTime to it.
LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);
The rest is the same as before. Example output:
2020-02-22T13:05:08.776
If you do need an old-fashioned java.sql.Timestamp
You asked for a Timestamp in UTC. A Timestamp is always in UTC. More precisely, it’s a point in time independent of time zone, so converting it into a different time zone does not make sense. Internally it’s implemented as a count of milliseconds and nanoseconds since the epoch. The epoch is defined as the first moment of 1970 in UTC.
The Timestamp class is a confusing class though. One thing that might have confused you is when you print it, thereby implicitly calling its toString method. The toString method uses the default time zone of the JVM for rendering the string, so prints the time in your local time zone. Confusing. If your datatype in SQL is timestamp without time zone, your JDBC driver most probably interprets the Timestamp in your time zone for the conversion into an SQL timestamp. Which in your case is incorrect since your database uses UTC (a recommended practice). I can think of three possible solutions:
Some database engines allow you to set a time zone on the session. I haven’t got any experience with it myself, it’s something I have read; but it may force the correct conversion from your Java Timestamp to your SQL timestamp in UTC to be performed.
You may make an incorrect conversion in Java to compensate for the opposite incorrect conversion being performed between Java and SQL. It’s a hack, not something that I would want to have in my code. I present it as a last resort.
LocalDateTime now = LocalDateTime.now(ZoneOffset.UTC);
Timestamp ts = Timestamp.valueOf(now);
System.out.println(ts);
2020-02-22 13:05:08.776
You notice that it only appears to agree with the UTC time above. It‘s the same result you get from the answer by Vipin Sharma except (1) my code is simpler and (2) you’re getting a higher precision, fraction of second is included.
Have you database generate the current timestamp in UTC instead of generating it in Java.
Links
Oracle tutorial: Date Time explaining how to use java.time.
Related question: Java - Convert java.time.Instant to java.sql.Timestamp without Zone offset
Despite what the Ignite docs say you can pass in a 24hr time.
The docs says yyyy-MM-dd hh:mm:ss[.nnnnnnnnn] so you may be tempted in your code to use this to format your dates but this will lead to times after midday being wrong. Instead, format your dates with yyyy-MM-dd HH:mm:ss[.nnnnnnnnn].
Notice the upper case HH. If you're using ZonedDateTime or Joda's DateTime when you call now with UTC now(UTC) and then toString("yyyy-MM-dd HH:mm:ss") will store the correct time in UTC.

SimpleDateFormat parse method mystery

I am aware that SimpleDateFormat.parse rely on the Calendar API which depends on local JVM timezone (computer's). Assume JVM timezone is IST.
SimpleDateFormat srcDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
srcDateFormat.setTimeZone(TimeZone.getTimeZone("EST"));
Date objDt = srcDateFormat.parse("2018-10-16 11:28:25"); //Time : 21:58:25
From the output it seems it converts from EST to IST(JVM local timezone).
SimpleDateFormat srcDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
srcDateFormat.setTimeZone(TimeZone.getTimeZone("IST"));
Date objDt = srcDateFormat.parse("2018-10-16 11:28:25"); //Time : 11:28:25
It keeps time unmodified in this case. In this case I set timezone same as JVM local timezone.
Please help me to understand the behavior of the parse method. Nevertheless, I am curious to know the reason behind such behavior.
I know that java.util.Date and java.text.SimpleDateFormat legacy classes are obsolete now.
References:
Why SimpleDateFormat.format() and SimpleDateFormat.parse() are giving different time though setting only one TimeZone?
How do I convert date/time from one timezone to another?
SimpleDateFormat parses a string to wrong time
https://www.codeproject.com/Tips/1190426/When-Parsing-Formatting-Dates-in-Java-Make-Sure-Yo
First, you are correct that Date and SimpleDateFormat are legacy classes and now obsolete. So I recommend you don’t use them and use java.time, the modern Java date and time API, instead. Among many advantages it is much more explicit about conversions between time zones, which I would expect to help you understand what the code does.
Second, you are doing a number of things incorrectly or at least inadequately:
Don’t store date-times as strings. Always store them as date-time objects in Java. When you do this, you will never need to convert a date-time string from one zone to another. Instant (a class from java.time) is a point in time and as far as I can see the one you should use here.
Don’t rely in three letter time zone abbreviations. Very many of them are ambiguous, including both of IST and EST, and the latter isn’t a true time zone, so what you get at this time of year (when America/New_York zone uses EDT rather than EST), I don’t know.
And repeating myself, use the modern classes, not the obsolete ones.
Out of curiosity what happened?
An old-fashioned Date represents a point in time independently of time zone (internally it stores its value as a count of milliseconds since the epoch, but this is an implementation detail that we need not know or concern ourselves with).
In your first example your string is parsed into a point in time that corresponds to 16:28:25 UTC, 21:58:25 in India, 12:28:25 in New York or 11:28:25 in Jamaica. I mention Jamaica because it’s one of the few places that happens to use Eastern Standard Time (EST) all year. Most of the locations that use EST only do so in winter, not at this time of year. When you look at the Date in your debugger, the debugger calls toString on the Date to get a string to show you. toString in turn uses the JVM’s time zone for generating the string. In your case it’s Asia/Kolkata, which is why you get 21:58:25.
In the second case the same string is parsed into a point in time that corresponds to 05:58:25 UTC, 11:28:25 in India, 01:58:25 in New York or 00:58:25 in Jamaica. Your debugger again calls toString, which again uses your JVM’s time zone and converts back into 11:28:25 IST. When you parse and print in the same time zone, you get the same time of day back.
Links
EST – Eastern Standard Time / Eastern Time (Standard Time)
Oracle tutorial: Date Time explaining how to use java.time.
Time Zone Converter – Time Difference Calculator, online, practical for converting between Asia/Kolkata, UTC, Kingston (Jamaica), New York and other time zones.

How to change miillisecond to Date Object in yyyy-MM-dd HH:mm:ss format [duplicate]

This question already has answers here:
Unix epoch time to Java Date object
(7 answers)
Convert timestamp in milliseconds to string formatted time in Java
(10 answers)
Closed 5 years ago.
How to change milliseconds to Date object in yyyy-MM-dd HH:mm:ss format like 2017-04-12 23:14:52?
You cannot do that. For a couple of reasons.
TL;DR: Don’t use Date, use Instant. Neither of these can have a format in them. Formatting into a string is dependent on time zone, so you need to choose a time zone.
First, I understand from the discussion that you are asking for a java.util.Date object having the format yyyy-MM-dd HH:mm:ss. A Date object does not have and cannot have a format in it. The thing you should try to understand here is the difference between data itself and presentation of data to a user. An int may hold the value 25389, but it doesn’t hold it in the format 25389 (in fact the internal representation is quite different from 25389). The same int may be presented to a user as 25389, 000025389, 25,389 or +25389, just to mention a few out of many possibilities. The formatting happens outside the int while the int stays just the same.
Similarly, a Date object holds a point in time. The same date may be formatted into for example 2017-04-12 23:14:52 or April 12, 2017 11:14:52 PM. It may even be formatted for different time zones, which would be a good idea if the system has users in different time zones. Alternatively we may show the user a calendar leaf and/or a clock showing the time. Again, formatting happens outside of the Date while the Date stays just the same.
Elaborating on the time zone issue, the same point in time represented by the same millisecond value could be formatted to 2017-04-12 17:44:52 in UTC, 2017-04-12 19:44:52 in my time zone, 2017-04-12 23:14:52 in Asia/Kolkata time zone or even 2017-04-13 05:44:52 in Pacific/Auckland time zone. Note that in the last case not even the date is the same. So there is not just one way to change your milliseconds into the format you asked for. We need to know which time zone you want it for before we can help you.
So what I believe you need is not one thing, but two
A way to store your point in time in your program.
A way to format your point in time into a string in yyyy-MM-dd HH:mm:ss format for a user in some time zone.
For storing your point in time, use either of
A long for the milliseconds value you already have
A java.time.Instant object.
Why didn’t I mention java.util.Date? Because this class is long outdated. Its design turned out to be troublesome very quickly. They tried to repair it by deprecating most of the methods and introducing java.util.Calendar, but that didn’t work very well either. Finally, drawing on the experiences from a library known as Joda-Time they introduced the java.time classes in Java 8 in 2014. That’s three years ago as of writing, and counting. So IMHO we should by now have thrown Date and friends overboard and started using the newer classes. So prefer Instant over Date.
Changing your milliseconds to an Instant is straightforward:
long milliseconds = 1492019092000L;
Instant pointInTime = Instant.ofEpochMilli(milliseconds);
For formatting your instant into a string for the user, as I said, we require a time zone. Then do
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu-MM-dd HH:mm:ss");
String formattedDateTimeString = pointInTime.atZone(ZoneId.of("Asia/Kolkata"))
.format(formatter);
So you need to fill in the desired time zone where I put ZoneId.of("Asia/Kolkata"). If you want to use the JVM’s current time zone setting, just fill in ZoneId.systemDefault(). Beware, though, that the time zone setting may be changed, even by an unrelated program running in the same JVM, so relying on this may be fragile.
The result of the above code snippet is a string like
2017-04-12 23:14:52
PS If after reading the above you really insist, here’s how to get a java.util.Date from the above:
Date myOutdatedDateInstance = Date.from(pointInTime);
(and excuse me for repeating, it still doesn’t have the desired format, that is not possible).
You can try this sample code.
public class MillDateConverter {
public static String dFormat = "yyy-MM-dd HH:mm:ss";
private static SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dFormat);
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
long milliSec=Long.parseLong("1086073200000");
System.out.println(milliSec);
calendar.setTimeInMillis(milliSec);
System.out.println(simpleDateFormat.format(calendar.getTime()));
}
}

Java 8 epoch-millis time stamp to formatted date, how?

Before Java-8 I got accustomed to always keep anything date/time related as milliseconds since Epoch and only ever deal with human readable dates/times on the way out, i.e. in a UI or a log file, or when parsing user generated input.
I think this is still safe with Java-8, and now I am looking for the most concise way to get a formatted date out of a milliseconds time stamp. I tried
df = Dateformatter.ofPattern("...pattern...");
df.format(Instant.ofEpochMilli(timestamp))
but it bombs out with Unsupported field: YearOfEra in Instant.getLong(...) which I half understand. Now what to use instead of Instant?
LocalDateTime.ofEpoch(Instant, ZoneId) seems wrong, since I don't care to have local time. I just want to see the local time zone when applying the formatter. Internally it should be just the Instant.
The same goes for ZonedDateTime.ofInstant(Instant, ZoneId), I thought to apply the ZoneId only when formatting. But I notice that the DateTimeFormatter does not itself deal anymore with time zones, it seems, so I reckon I need to use one of the above.
Which one is preferred and why? Or should I use yet another way to format an epoch-millis time stamp as a date/time with time zone?
An Instant does not contain any information about the time-zone, and unlike in other places, the default time-zone is not automatically used. As such, the formatter cannot figure out what the year is, hence the error message.
Thus, to format the instant, you must add the time-zone. This can be directly added to the formatter using withZone(ZoneId) - there is no need to manually convert to ZonedDateTime *:
ZoneId zone = ZoneId.systemDefault();
DateTimeFormatter df = DateTimeFormatter.ofPattern("...pattern...").withZone(zone);
df.format(Instant.ofEpochMilli(timestamp))
* regrettably, in early Java 8 versions, the DateTimeformatter.withZone(ZoneId) method did not work, however this has now been fixed, so if the code above doesn't work, upgrade to the latest Java 8 patch release.
Edit: Just to add that Instant is the right class to use when you want to store an instant in time without any other context.
The error you have when formatting an Instant using a formatter built with a year or other fields is expected; an Instant does not know which year or month or day it is, it only knows how much milliseconds have elapsed since the Epoch. For the same instant, it could be 2 different days on 2 different places of the Earth.
So you need to add a time zone information if you want to print the day. With an Instant, you can call atZone(zone) to combine it with a ZoneId in order to form a ZonedDateTime. This is very much like an instant, only that it has a time zone information. If you want to use the system time zone (the one of the running VM), you can get it with ZoneId.systemDefault().
To print it, you can use the two built-in formatter ISO_OFFSET_DATE_TIME or ISO_ZONED_DATE_TIME. The difference between the two is that the zoned date time formatter will add the zone id to the output.
Instant instant = Instant.now();
DateTimeFormatter formatter = DateTimeFormatter.ISO_OFFSET_DATE_TIME;
System.out.println(formatter.format(instant.atZone(ZoneId.systemDefault())));
System.out.println(formatter.format(instant.atZone(ZoneId.of("America/Los_Angeles"))));
when run on my machine, which has a system time zone of "Europe/Paris", you'll get:
2016-07-31T18:58:54.108+02:00
2016-07-31T09:58:54.108-07:00
You can of course build your own formatter if those one do not suit you, using ofPattern or the builder DateTimeFormatterBuilder.
I agree that this is somewhat confusing, especially when compared with it's predecessor Joda DateTime.
The most confusing thing is that the documentation for LocalDateTime says that it is "A date-time without a time-zone", and yet LocalDateTime.ofInstant method takes both an instant and a timezone as parameters.
That said, I think that you can achieve what you want by using Instant and LocalDateTime.ofInstant by using the UTC timezone.
public LocalDateTime millisToDateTime(long millis) {
return LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), ZoneId.of("Z");
}

DateTime getDayOfYear() Returning 366 for 12-31-2013 instead of 365

Does anyone know why this method is behaving this way? 2013 is not a leap year, so I would expect that if I have a DateTime object like:
DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss");
DateTime dt = formatter.parseDateTime("2013-12-31 23:59:52");
That
dt.getDayOfYear()
would give me 365 but it's giving me 366. Can anyone see what the problem is?
I am also getting 366 if I do
dt.toString("DDD");
I would appreciate any input anyone might have.
Let me try an answer based on what I read from the docs and the code.
I assume your time stamp is indeed what you think it is, namely that last minute of 2013 UTC.
You create a DateTime object with the stamp in the default timezone. So if your local time zone is +01:00, this is 2013-14-01 00:59 +01:00.
Now you use withZoneRetainFields(UTC). From the docs I understand that this changes the milliseconds such that they actually reflect 2014-01-01 00:59 UTC, Basically this requires to add 3600*1000 millis to the time stamp, while changing the time zone entry in the object to UTC.
When you then ask for the day of the year, what actually happens internally is
.dayOfYear().get(getMillis())
My hunch is that dayOfYear is still based on 2013 and from this baseline you get day 366.
(I am not totally convinced myself of this answer I must admit and I wonder if there might be simply a bug lurking in Joda.)
Anyway: if you have UTC timestamps, I might suggest to just use new DateTime(stamp, UTC) where UTC a DateTimeZone object. Thereby you avoid the mind-bending double zone shifting.
EDIT: Further, if your stamp from the DB is genuinely UTC, by usingwithZoneRetainFields you change that stamp to denote another point in time, which has a good chance of being the wrong way to do whatever you are actually trying to do. In the confusing area of date/time handling, the time stamp in millis is the one easy to understand item. If it is not just for display purposes, I would never touch it.

Categories