Connection between DateTime and Calendarsystem, to calculate time differences - java

i have a MySQL Database which contains several timestamps e.g. startDate and endDate. Now i want the difference between the two timestamps.
(I read a lot about the advantages of joda time, but did not work with the included java implementation.)
I do not know if i understand the concepts correctly:
I need a formatter e.g. DateTimeFormatter to format the startDate string into a valid dateTime format.
//yyyy-MM-dd HH:mm:ss.m, the last .m, is for the miliseconds i got from jdbc/mysql
DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss.m");
String startDate = resultSet.getString("StartDateInMySQLDB");
DateTime dateTime = formatter.parseDateTime(startDate);
Now i have a DateTime object. However i need a connection to a calendar system, to e.g. calculate the time between two dates. But is not a simple time of 24h for a day (i found several solutions with joda and without a calendar). I need something like a work-time: e.g. 09:00 AM to 05:00 PM (Monday to thursday)
Therefor i thought, i need a calendar sytem, but i do not know to connect datetime and the calendar system correctly.

Related

How to localize timestamps in an Android app using Java?

I'm working on an app where users can timestamp themselves IN or OUT from their workplace. At the moment I'm trying to get the localization of the timestamps done. For example when I make a timestamp in UTC +02:00 at 08:00 02.01.2020, it works correctly and shows the time as 08:00 and right date as well. But when I change to UTC +01:00 in my phone settings, and do the same timestamp, the time becomes 07:00 and date becomes 01.01.2020.
The code I have so far for "parsing" the time looks like this:
String formattedTime = "";
String datetime2 = "1970-01-01T" + returntime;
Log.v("DATE", datetime2);
OffsetDateTime odt2 = OffsetDateTime.parse(datetime2);
Date date2 = Date.from(odt2.toInstant());
SimpleDateFormat sdf2 = new SimpleDateFormat("HH:mm",Locale.getDefault());
formattedTime = sdf2.format(date2);
Log.v("FORMTIME", formattedTime);
I'm using a similar code snippet for "parsing" the date as well.
The output for the two logs (when in UTC +01:00):
V/DATE: 1970-01-01T15:00:00+02:00
V/FORMTIME: 14:00 //SHOULD BE 15:00
V/DATE: 1970-01-01T08:00:00+02:00
V/FORMTIME: 07:00 //SHOULD BE 08:00
V/DATE: 1970-01-01T08:00:00+02:00
V/FORMTIME: 07:00 //SHOULD BE 08:00
It seems like the change in UTC from +02:00 to +01:00 reduce the time and date also with 1...
So is it wrong to use the OffsetDateTime class and "toInstant" (Instant class) for what I'm trying to achieve? What would be the right solution?
OffsetTime
I don’t understand what that offset of +02:00 in your string signifies. In particular it confuses me what you want to do when the offset changes. In any case java.time, the modern Java date and time API, parses and formats your time pretty easily. Let’s first define the formatter that describes your desired output format:
private static final DateTimeFormatter timeFormatter
= DateTimeFormatter.ofPattern("HH:mm");
With this in place you may do:
String returntime = "15:00:00+02:00";
OffsetTime time = OffsetTime.parse(returntime);
String formattedTime = time.format(timeFormatter);
System.out.println(formattedTime);
Output:
15:00
The offset is parsed, but is not used for anything. The output time will always be the same as the time in the string.
I take it that the date 1970-01-01 that you used in your code is arbitrary and without significance. The OffsetTime that I am using hasn’t got a date, so saves us from choosing a date for processing the time.
Word use: There isn’t any localization going on here. Localization is when for an American audience you print 3:00 PM instead of 15:00, for example.
EDIT:
If your string contains a date too, OffsetDateTime is the right class to use, and again we need no explicit formatter for parsing (only for formatting). Your code in the comment is fine (except that you had accidentally reversed the order of day, month and year in the string).
String returnDate1 = "2020-12-05T00:00+02:00";
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy");
OffsetDateTime dateTime = OffsetDateTime.parse(returnDate1);
String formattedDate = dateTime.format(dateFormatter);
System.out.println(formattedDate);
05-12-2020
What went wrong in your code?
It seems you were over-complicating things. In particular you were mixing old and modern date-time classes. The old ones, Date and SimpleDateFormat, are poorly and confusingly designed, which no doubt contributed to your unexpected results. And when mixing, you are going to need conversions that are not really needed for your job, again just making your code more complicated than needed.
Your sdf2 was using your default time zone for printing the time. You had got offset +02:00 in the string, so when you set the phone to UTC+01:00, a conversion takes place. When the time is 08:00 at offset +02:00, it is only 07:00 at offset +01:00. So this was the result you got. This in turn means that if the user’s time zone was at offset +01:00 on 1970-01-01, then you were getting the correct times for that time zone.

Date is not properly calculated in Java. Issue with zone

I have a date format stored in DB, for example:
Thu Aug 27 2020 00:00:00 GMT-0400 (Eastern Daylight Time)
I want to display the same date as output. Seems like I am missing something zone. It's evolving to be one day prior to this date.
I did the following:
DateTimeFormatter etFormat = DateTimeFormatter.ofPattern("MM/dd/yyyy 'at' hh:mma 'ET'");
ZoneId zoneId = ZoneId.of("America/New_York");
ZonedDateTime zonedDateTime = ((Timestamp) date).toLocalDateTime().atZone(zoneId);
etFormat.format(zonedDateTime)
Output:
08/26/2020 at 08:00PM ET
What am I doing wrong?
In your database you have the date time with offset UTC-04:40 (which is 4 hr behind from UTC assuming America/New_York timezone). And when it converts into Timestamp it will be stores in UTC without offset which is 08/26/2020 at 08:00PM.
So first convert the Timestamp into Instant of UTC and then convert the Instant into ZonedDateTime with the zone information
ZonedDateTime dateTime = timestamp.toInstant()
.atZone(ZoneOffset.UTC)
.withZoneSameInstant(ZoneId.of("America/New_York"));
etFormat.format(dateTime); //08/27/2020 at 00:00PM ET
The central issue is this:
java.sql.Timestamp, which is what e.g. resultSet.getTimestamp() returns, does not contain any timezone data. It is simply an instant in time, and it is stored as milliseconds since the epoch (jan 1st, 1970), UTC zone.
This does not match what most DBs store, because most DBs do in fact explicitly store the timezone with that. If your DB does not do this, or you picked a column type which does not do this, you should strongly consider changing that.
So, if the database has stored 'midnight in new york, aug 27th', and the database is forced by JDBC to put this in java.sql.Timestamp terms, there's nothing the DB engine can do about it, other than do its best, which is to return that exact time, in UTC terms. If you then print the UTC timestamp in human terms, you end up with '4 at night', and not 'midnight' (because new york is 4 hours earlier than UTC).
You then, with your code say: Okay, take the timestamp, turn it into a local date time (that'd be the notion of '27th of august, 4 o clock at night', without any inkling of in which czone that is in, and by itself not a thing that can ever be turned back into an epoch with more info), and then you put this at the new york zone, giving you '4 at night in new york', which is 4 hours later than where we started.
Okay, but how do I fix this?
Every other answer (so far) is just giving you silly ways to fight the symptoms.
I propose you fix the disease.
The actual error occurs when you ask the DB to transfer the fully timezoned information from its tables into the timezoneless java.sql.Timestamp object. Stop doing that.
Don't call (I assume your column is called 'mark', fill in whatever it might be):
resultSet.getTimestamp("mark").
Call:
resultSet.getObject("mark", ZonedDateTime.class);
or possibly try LocalDateTime.class, or possibly OffsetDateTime.class, but ZDT is preferred.
Then if that does not work, complain to your DB and/or JDBC driver because they're messing up and making it next to impossible to do timezone stuff properly when interacting with that DB from the java side.
Actually, the DB should store just a moment-in-time
If truly the time being stored represents the notion of an 'instant in time' and not so much 'as humans would ever talk to you about it', then there are data types for that too, but convert your java.sql.Timestamp object to a java.time.Instant asap (via .toInstant()), or straight up ask for it: resultSet.getObject("colName", Instant.class) and have java and the db line up the datatypes straight away.
Eh, whatever. Cures are for wussies, just work around it
Eh, well, the only thing you really need to do then is not to magically add 4 hours. This will do it:
ZonedDateTime dateTime = timestamp.toInstant()
.atZone(ZoneOffset.UTC)
.withZoneSameInstant(ZoneId.of("America/New_York"));
even if the tz stored in the DB is something else (it'll then give you that instant in time, but in new york, e.g. if the db has stored 'midnight in amsterdam', this will give you a time 6 hours earlier (or possibly 7 or 5, there are a few days in the year where things go ape due to US and europe having different shift days for daylight savings).
The format that you have used is not correct. I hope you will be able to understand the difference by comparing your pattern with mine. The reason why I've presented the parsing logic is that you have not made it clear the type of date-time. Whatever type it may be, it looks like you have a date-time string, Thu Aug 27 2020 00:00:00 GMT-0400 (Eastern Daylight Time) which you want to parse into ZonedDateTime and display the same into the pattern of the date-time string you have. I guess, the main problem you are having is how to format the ZonedDateTime instance into the same form.
Do it as follows:
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.time.format.TextStyle;
public class Main {
public static void main(String[] args) {
// Given date-time string
String dateStr = "Thu Aug 27 2020 00:00:00 GMT-0400 (Eastern Daylight Time)";
// Define the formatter for parsing
DateTimeFormatter parsingFormat = new DateTimeFormatterBuilder()
.appendPattern("EEE MMM dd uuuu HH:mm:ss zX")
.appendLiteral(" (")
.appendGenericZoneText(TextStyle.FULL)
.appendLiteral(")")
.toFormatter();
// Parse the given date-time into ZonedDateTime
ZonedDateTime zonedDateTime = ZonedDateTime.parse(dateStr, parsingFormat);
// Display in default format [i.e. zonedDateTime.toString()]
System.out.println(zonedDateTime);
// Define the formatter for output
DateTimeFormatter outputFormat = new DateTimeFormatterBuilder()
.appendPattern("EEE MMM dd uuuu HH:mm:ss z")
.appendLiteral(" (")
.appendPattern("zzzz")
.appendLiteral(")")
.toFormatter();
// Get the string representation in the custom format
String strDate = zonedDateTime.format(outputFormat);
// Display the string representation in the custom format
System.out.println(strDate);
}
}
Output:
2020-08-27T00:00-04:00[America/New_York]
Thu Aug 27 2020 00:00:00 GMT-04:00 (Eastern Daylight Time)
Note: By any chance, if you also have difficulty to convert the timestamp into ZonedDateTime, you can refer other answers on this page and use this answer to solve the problem with formatting.
java.time
I recommend that you use java.time, the modern Java date and time API, exclusively for your date work. Instead of getting a Date or Timestamp from your database, since JDBC 4.2 (in the case of MySQL that’s many years now) get a modern LocalDate from your result set. An example:
PreparedStatement ps = yourDatabaseConnection.prepareStatement("select your_date from your_table;");
ResultSet rs = ps.executeQuery();
while (rs.next()) {
LocalDate date = rs.getObject("your_date", LocalDate.class);
// Do something with date
}
A LocalDate is a date without time of day and without time zone. So this will relieve you of all time zone trouble.
If you want to print the start of the day in North American Eastern time zone to the user in the format used in the question:
DateTimeFormatter etFormat = DateTimeFormatter.ofPattern("MM/dd/yyyy 'at' hh:mma v");
ZoneId zoneId = ZoneId.of("America/New_York");
LocalDate date = LocalDate.of(2020, Month.AUGUST, 27);
ZonedDateTime startOfDay = date.atStartOfDay(zoneId);
String result = startOfDay.format(etFormat);
System.out.println(result);
Output from this example is:
08/27/2020 at 12:00AM ET
Do use pattern letter v for time zone in the format pattern rather than hard-coding ET. The latter will produce false and confusing results when one day a junior programmer feeds a ZonedDateTime in an other time zone into the code.
What went wrong in your code?
It’s not clear to me how you got your date from your database. Apparently date even though declared a Date was really a Timestamp (a bad practice since the inheritance relationship between the two classes is really one of implementation, not a conceptual one) denoting the start of the day in UTC. toLocalDateTime() is a dangerous and often meaningless call: it uses the time zone of the JVM for converting the Timestamp to a LocalDateTime. At 0:00 UTC it is 8 PM the evening before in Eastern time zone, so your LocalDateTime becomes 2020-08-26T20:00. Next atZone(zoneId) only gives the correct time because zoneId happens to coincide with the JVM’s time zone used in the previous step.
Link
Oracle tutorial: Date Time explaining how to use java.time.
The date is converted with timezone set to GMT.
final static String datePattern = "EEE MM/dd/yyyy HH:mm:ss 'GMT'Z '('z')'";
DateFormat df = new SimpleDateFormat(datePattern, Locale.getDefault());
simpledateformat.setTimeZone(TimeZone.getTimeZone("GMT"))
simpleDateFormat.format(givenDate)

Java how to remove nth minute from given time string without considering time zone

I tried to remove minute from given time, but some how it is converting time to my local time zone
String timeStamp="20180623 05:58:15" ;
dateFormat inputFormatter = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
Date date = inputFormatter.parse(timeStamp);
date.setMinutes(-2);
logger.info("Before converting : "+date);
DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd HH:mm:ss");
Here it is converting to my local time and subtracting 2 minutes from given time, but I don`t want to check the time zone here instead, what ever time I give it should just subtract 2 minutes.
Start with understanding into how Date works. When you do...
logger.info("Before converting : "+date);
The Date class uses it's toString method to format the the date/time information represented by the Date class into a human readable format. It doesn't "convert" the date/time value in anyway
So taking your code from above (and reworking it so it works), it outputs...
Before converting : Sat Jun 23 04:58:15 AEST 2018
20180623 04:58:15
on my machine - why are the values the same? Because the input doesn't have any time zone information, so the time is likely been treated as been in the machines local timezone (and the value is simply been formatted for output).
Date is just a container for the number of milliseconds since the Unix Epoch, it's format agnostic - meaning it carries not formatting information.
Date is also effectively deprecated - not to mention that setDate is also very much deprecated
A better (starting point) overall is to make use the newer date/time API introduced in Java 8 (and which has back port support for earlier versions of the API)
String timeStamp = "20180623 05:58:15";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd HH:mm:ss", Locale.ENGLISH);
LocalDateTime ldt = LocalDateTime.parse(timeStamp, formatter);
ldt = ldt.minusMinutes(2);
System.out.println(ldt);
System.out.println(ldt.format(formatter));
This will output...
2018-06-23T05:56:15
20180623 05:56:15
The input and the output are still consider as been in the machines local time zone.
but I don`t want to check the time zone here instead, what ever time I give it should just subtract 2 minutes
Just remember, the API still needs to have some concept of time zone, weather it's the local time zone or UTC/GMT, but since your input doesn't provide any kind of information, you need to make a choice over "how" best to handle that issue. The example above just "assumes" local time, but you could use ZonedDateTime and convert it to "common" time zone from which your operations are executed or, better yet, make all your strings carry time zone information
Oh, and for the love of my sanity, stop managing date/time values in String format - get them into an appropriate container as soon as possible and manage them from there - I've spent a week wrangling inappropriately formatted date strings and I'm not happy Jan, not happy

How to keep original timezone with JodaTime

I have date in String format I need to parse. The format is as following with timezone from all over the world :
String stringDate = "2016-04-29 12:16:49.222+04:30";
String pattern = "yyyy-MM-dd HH:mm:ss.SSSZ";
It seems that java.util.Date doesn't accept timezone with : separator. So I'm trying with Jodatime library :
DateTime formattedDate = DateTimeFormat.forPattern(pattern).parseDateTime(stringDate);
LocalDateTime formattedDate2 = DateTimeFormat.forPattern(pattern).parseLocalDateTime(stringDate);
MutableDateTime formattedDate3 = DateTimeFormat.forPattern(pattern).parseMutableDateTime(stringDate);
System.out.println(formattedDate);
System.out.println(formattedDate2);
System.out.println(formattedDate3);
These lines output :
2016-04-29T09:46:49.222+02:00
2016-04-29T12:16:49.222
2016-04-29T09:46:49.222+02:00
As far as I understand the formatter modify output timezone to comply on mine (I'm in Paris, UTC+2), but I want the output keep its original timezone. Is it possible to do it with Jodatime library? Or should I change for another?
Edit :
Actually I need to get a Date object on which the timezone offset would be 270 (the timezone offset of the stringDate : 4 hour and 30 minutes) in place of 120 (my local timezone offset):
System.out.println(formattedDate.toDate().getTimezoneOffset()); // I expect 270 but I get 120
What you missed is DateTimeFormatter#withOffsetParsed:
Returns a new formatter that will create a datetime with a time zone equal to that of the offset of the parsed string.
Otherwise the formatter will parse it into your local time zone (surprising, I know).
#Test
public void preserveTimeZone() {
String stringDate = "2016-04-29 12:16:49.222+04:30";
String pattern = "yyyy-MM-dd HH:mm:ss.SSSZ";
DateTime dt = DateTimeFormat.forPattern(pattern).withOffsetParsed().parseDateTime(stringDate);
System.out.println(dt); // prints "2016-04-29T12:16:49.222+04:30"
}
As for your edit - java.util.Date does not hold time zone information and the deprecated getTimezoneOffset() method only
Returns the offset, measured in minutes, for the local time zone relative to UTC that is appropriate for the time represented by this Date object.
So you'd better use Joda Time or java.time classes to handle time zones properly.
When I run the same code that you have posted, I end up with
2016-04-29T02:46:49.222-05:00
2016-04-29T12:16:49.222
2016-04-29T02:46:49.222-05:00
which if you will notice, has different hour values AND time-zone values. However, if you look at their millis:
System.out.println(formattedDate.getMillis());
System.out.println(formattedDate2.toDateTime().getMillis());
System.out.println(formattedDate3.getMillis());
you'll see the output
1461916009222
1461950209222
1461916009222
So they have the same epoch time, but are printed out differently. This is due to the mechanism of toString() on DateTime objects, and how they are to be interpreted.
DateTime and LocalDateTime(MutableDateTime is just a mutable version of DateTime) deal with the same epoch time in different ways. LocalDateTime will always assume that epoch time is UTC time(per the javadoc for LocalDateTime), while DateTime will assume that epoch is represented in the time zone of the Chronology which it holds(per the javadoc again). If the TimeZone is not specified at construction time, then the Chronology will assume that you want the timezone of your default Locale, which is set by the JVM. In your case, the default Locale is Paris France, while mine is St. Louis USA. Paris currently holds a +2:00 time zone offset, while St. Louis has -5:00, leading to the different time zone representations when we print it.
To get even more annoying, those offsets can change over time. If I come back in 6 months and try to answer this again, my values will show -6:00 (stupid Daylight savings time!)
The important thing to remember is that these two dates have the same epoch time: we are talking about the same instant in time, we are just representing that time differently when we print it out.
If you want to use a different time zone for representing the output of the parse result, then you can set the DateTimeZone during formatting using DateTimeFormat.withZone() or DateTimeFormat.withLocale:
DateTimeFormatter sdf = DateTimeFormat.forPattern(pattern).withZone(DateTimeZone.forOffsetHoursMinutes(4,30));
System.out.println(formattedDate.getMillis());
System.out.println(formattedDate2.toDateTime().getMillis());
System.out.println(formattedDate3.getMillis());
which will print
2016-04-29 12:16:49.222+0430
2016-04-29 12:16:49.222
2016-04-29 12:16:49.222+0430
notice that the LocalDateTime version still prints out without the TimeZone. That's kind of the feature of LocalDateTime: it is represented without having to deal with all this business.
So that is why your printing values look weird. To further your question about getting a java.util.Date object from the parsed DateTime object: toDate will give you a java.util.Date which represents the same epoch time. However, java.util.Date behaves similarly to DateTime, in that unless otherwise stated, it will use the TimeZone of the default Locale. If you know the Locale ahead of time, then you can use the toDate(Locale) method to ensure you use that Locale's TimeZone offset.
It gets a lot harder if you don't know the TimeZone ahead of time; in the past, I've had to hand-parse the TimeZone hour and minute offsets to determine the proper TimeZone to use. In this exact case that's not too difficult, since the last 6 characters are extremely well-formed and regular(unless, of course, they aren't :)).

Convert LocalTime (Java 8) to Date

I'm trying to convert a java.time.LocalTime object to java.util.Date but can't find any suitable method. What's the correct way to do this?
Is there any reason why java doesn't seem to ship with a built-in direct conversion method?
To possible duplicates:
How to convert joda time - Doesn't work for me, probably I'm missing some "joda" libraries?
How to convert Date to LocalTime? - This adresses conversion the other way around.
LocalTime actually can't be converted to a Date, because it only contains the time part of DateTime. Like 11:00. But no day is known. You have to supply it manually:
LocalTime lt = ...;
Instant instant = lt.atDate(LocalDate.of(A_YEAR, A_MONTH, A_DAY)).
atZone(ZoneId.systemDefault()).toInstant();
Date time = Date.from(instant);
Here's a blog post which explains all the conversions between the new and the old API.
There's no simple built-in conversion method, because these APIs approach the idea of date and time in completely different way.
LocalTime lt = ...;
Instant instant = lt.atDate(LocalDate.of(A_YEAR, A_MONTH, A_DAY)).
atZone(ZoneId.systemDefault()).toInstant();
Date time = Date.from(instant);
From :
http://blog.progs.be/542/date-to-java-time
I added the data (hour, minute, second) one by one (from localtime to date):
reta.setHours(vol.getRetard().getHour());
reta.setMinutes(vol.getRetard().getMinute());
reta.setSeconds(vol.getRetard().getSecond());
Note :
reta: Date veriabble ;
vol.getRetard (): localtime variable
As others have said, it’s a problematic question in that a LocalTime and a Date really represent quite different and almost unrelated concepts. A LocalTime is a time of day without time zone, such as 19:45 (or 7:45 PM). A Date is a point on the time line; if it happens to coincide with 19:45 on some date in some time zone, it will not in other time zones.
I believe that the conventional way of misusing (indeed) a Date for an hour of day is setting it to that time of day on January 1, 1970 in the default time zone of the JVM. This practice carries all of the liabilities already mentioned. In particular the JVM default time zone setting can be changed at any time from another part of your program or any other program running in the same JVM. This means that a completely unrelated program may suddenly cause your Date to indicate a different time of day than the one you had initialized it to.
There’s nothing better we can do, so here goes:
LocalTime time = LocalTime.of(11, 0);
Instant timeOnEpochDayInDefaultTimeZone = LocalDate.EPOCH
.atTime(time)
.atZone(ZoneId.systemDefault())
.toInstant();
Date oldfashionedDateObject = Date.from(timeOnEpochDayInDefaultTimeZone);
System.out.println(oldfashionedDateObject);
In my time zone output from this snippet is:
Thu Jan 01 11:00:00 CET 1970
Here is another approach:
We can add a LocalDate to the LocalTime in order to make it a LocalDateTime and then convert it to Date using the valueOf method of java.sql.Timestamp like this:
LocalTime localTime = LocalTime.now();
Date date = java.sql.Timestamp.valueOf(localTime.atDate(LocalDate.now()));
As #Dariusz said, we cannot convert LocalTime to Date directly as it contains only time part but Date must contain all the value along with the timeZone.
In order to get the date part, we can use LocalDate.now(). It will give us LocalDate object with today's date.
Now, we have both LocalDate and LocalTime, we can now use the LocalDateTime.of(date: LocalDate, time: LocalTime) or localTime.atDate(date: LocalDate) to get the LocalDateTime object.
And now we can convert the LocalDateTime to Date using below kotlin extension function.
fun LocalDateTime.toDate(): Date {
return Date.from(this.atZone(ZoneId.systemDefault()).toInstant())
}

Categories