I have a MySQL database which is storing a datetime value, let's say 2020-10-11 12:00:00. (yyyy-mm-dd hh:mm:ss format)
The type of this date (in mysql) is DATETIME
When I retrieve this data in my controller, it has the java 7 type "Date". But it adds a timezone CEST due to my locale I suspect. Here I already find confusing that when displaying this date which is not supposed to have a timezone attached it actually has... and the debugger says it is "2020-10-11 12:00:00 CEST".
My problem is that date was not stored with the CEST timezone. It was stored with the America/New_York one, for example. EDIT: What I mean with this line, is that the date was stored from new york using the timezone of new york. So, it was really 12:00:00 AM there, but here in Madrid it was 18:00:00 PM. I need that 18:00:00.
So in New York, someone did an insert at that time. Which means that the time in Europe was different. I need to calculate which time was in Europe when in America was 12AM. But my computer keeps setting that date to CEST when I retrieve it so all my parsing attempts are failing... This was my idea:
Date testingDate // This date is initialized fetching the "2020-10-11 12:00:00" from mySql
Calendar calendar = new GregorianCalendar()
calendar.setTime(testingDate)
calendar.setTimeZone(TimeZone.getTimeZone("America/New_York")
SimpleDateFormat localDateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
TimeZone localTimeZone = TimeZone.getTimeZone("Europe/Madrid")
localDateFormatter.setTimeZone(localTimeZone)
String localStringDate = localDateFormatter.format(calendar.getTime())
Date newDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(localStringDate)
Here my idea is that: I create a brand new calendar, put on it the time that I had on America and I also say hey this calendar should have the America Timezone. So when I get the time of it using a formatter from Europe it should add the corresponding hours to it. It makes a lot of sense in my head but it is just not working in the code D: And I really don't want to calculate the time difference by myself and adding or substracting the hours because that would look extremely hardcoded in my opinion.
Can any one give me some ideas of what I'm interpreting wrong or how should I tackle this problem in a better way?
Important: I'm using java 7 and grails 2.3.6.
My problem is that date was not stored with the CEST timezone. It was stored with the America/New_York one, for example.
From what I know of MySQL, this is impossible.
Calendar calendar = new GregorianCalendar()
No, don't. The calendar API is a disaster. Use java.time, the only time API in java that actually works and isn't completely broken / very badly designed. If you can't (java 7 is extremely out of date and insecure, you must upgrade!), there's the jsr310 backport. Add that dependency and use it.
Let me try to explain how to understand time first, because otherwise, any answer to this question cannot be properly understood:
About time!
There are 3 completely different concepts and they are all often simplified to mean 'time', but you shouldn't simplify them - these 3 different ideas are not really related and if you ever confuse one for another, problems always occur. You cannot convert between these 3 concepts unless you do so deliberately!
"solarflares time": These describe the moment in time as a universal global concept that something occurred or will occur. "That solar flare was observed at X" is a 'solarflares' time. Best way to store this is millis since epoch.
"appointment time": These describe a specific moment in time as it was or will be in some localized place, but stated in a globally understandable way. "We have a zoom meeting next tuesday at 5" is one of these. It's not actually constant, because locales can decide to adopt new timezones or e.g. move the 'switch date' for daylight savings. For example, if you have an appointment at your dentist on 'november 5th, at 17:00, 2021', and you want to know how many hours are left until your appointment starts, then the value should not change just because you flew to another timezone and are looking at this number from there. However, it should change if the country where you made the appointment in decided to abolish daylight savings time. That's the difference between this one and the 'solarflares' one. This one can still change due to political decisions.
"wake-up-alarm time": These describe a more mutable concept: Some way humans refer to time which doesn't refer to any specific instant or is even trying to. Think "I like to wake up at 8", and thus the amount of time until your alarm will go off next is continually in flux if you are travelling across timezones.
Now, on to your question:
I have a MySQL database which is storing a datetime value, let's say 2020-10-11 12:00:00. (yyyy-mm-dd hh:mm:ss format)
Not so fast. What exact type does that column have? What is in your CREATE TABLE statement? The key thing to figure out here is what is actually stored on disk? Is it solarflare, appointment, or wakeup-alarm? There's DATE, DATETIME and TIMESTAMP, and over the years, mysql has significantly changed how these things are stored.
I believe that, assuming you are using the modern takes on storage (So, newish mysql and no settings to explicitly emulate old behaviour), e.g. a DATETIME stores sign, year, day, hour, minute, and second under the hood, which means it is wakeup alarm style: There is no timezone info in this, therefore, the actual moment in time is not set at all and depends on who is asking.
Contrast to TIMEZONE which is stored as UTC epoch seconds, so it's solarflares time, and it doesn't include any timezone at all. You'd have to store that separately. As far as I know, the most useful of the 3 time representations (appointment time) is not a thing in mysql. That's very annoying; mysql tends to be, so perhaps par for the course.
In java, all 3 concepts exist:
solarflares time is java.time.Instant. java.util.Date, java.sql.Timestamp, System.currentTimeMillis() are also solarflares time. That 'Date' is solarflares timestamp is insane, but then there is a reason that API was replaced.
appointment time is java.time.ZonedDateTime
wakeup-alarm time is java.time.LocalDateTime.
When I retrieve this data in my controller, it has the java 7 type "Date".
Right. So, solarflares time.
Here's the crucial thing:
If the type of time stored in MySQL does not match the type of time on the java side, pain happens.
It sure sounds like you have wakeup-alarm time on disk, and it ends up on java side as solarflares time. That means somebody involved a timezone conversion. Could have happened internally in mysql, could have happened in flight between mysql and the jdbc driver (mysql puts it 'on the wire' converted), or the jdbc driver did it to match java.sql.Timestamp.
The best solution is not to convert at all, and the only real way to do that is to either change your mysql table def to match java's, so, make that CREATE TABLE (foo TIMESTAMP), as TIMESTAMP is also solarflares time, or, to use, at the JDBC level, not:
someResultSet.getTimestamp(col);
as that returns solarflares time, but:
someResultSet.getObject(col, LocalDateTime.class);
The problem is: Your JDBC driver may not support this. If it doesn't, it's a crappy JDBC driver, but that happens sometimes.
This is still the superior plan - plan A. So do not proceed to the crappy plan B alternative unless there is no other way.
Plan B:
Acknowledge that conversion happens and that this is extremely annoying and errorprone. So, make sure you manage it, carefully and explicitly: Make sure the proper SET call is set up so that mysql's sense of which timezone we are at matched. Consider adding storing the timezone as a column in your table if you really need appointment time. etcetera.
Thanks to #rzwitserloot I was able to find out a solution.
First I'll get the data from the database. I'll get rid of any timezone added by the driver / mysql by converting it to a LocalDateTime. Then, I'll create a new ZonedDateTime using the Timezone that was used when storing the data in the database.
Once I have a ZonedDateTime, it is time to convert it using my current timezone. I'll get a new ZonedDateTime object with the proper time.
Then I just add a few more lines to convert it back to my main "Date" class:
I've used the ThreeTen backport as suggested.
Date dateMySQL //Initialized with the date from mysql
Calendar calendar = new GregorianCalendar()
calendar.setTime(dateMySQL)
org.threeten.bp.LocalDateTime localDateTime = org.threeten.bp.LocalDateTime.of(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH)+1,
calendar.get(Calendar.DAY_OF_MONTH), calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE),
calendar.get(Calendar.SECOND))
String timezone //Initialized with the timezone from mysql (Ex: "America/New_York")
ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, ZoneId.of(timezone))
ZonedDateTime utcDate = zonedDateTime.withZoneSameInstant(ZoneId.of("Europe/Madrid"))
calendar.setTimeInMillis(utcDate.toInstant().toEpochMilli())
Date desiredDate = calendar.time
dateMySQL: "2020-10-11 10:00:00" // CEST due to my driver
timezone: "America/New_York"
desiredDate: "2020-10-11 19:00:00" // CEST Yay!
Let's assume that I have client's time saved in my database as 2020-09-22T10:50:37.276240900
I need to present this date in web-service for client app depending on client timezone, for example I need to add 2 hours to saved date if client lives in UTC+2 timezone.
So what am I doing for ?
Getting date from entity and adding timezone to time taken from database (startDate: LocalDateTime)
entity.startDate.atZone(ZoneId.of("Europe/Vienna"))
what gives me the value of ZonedDateTime 2020-09-22T10:50:37.276240900+02:00[Europe/Vienna]
This value is what I'm expecting for, basically "initial time plus 2 hours". After that I would to format this time to have output with this 2 hours of being added, some kind of this
12:50 22.09.2020
but when I do format like this
entity.startDate
.atZone(ZoneId.of("Europe/Vienna"))
.format(DateTimeFormatter.ofPattern(NotificationListener.EUROPEAN_DATE_FORMAT, Locale.ENGLISH))
where const val EUROPEAN_DATE_FORMAT = "HH:mm dd.MM.yyyy"
I get this output 10:50 22.09.2020 which looks like my format is not applied properly, so I cannot see my 2 hours.
So my questions are:
am I correct to adding timezone of client app in described way ?
how to apply timezone in more precise way and format this date to see timezone zone applied ?
LocalDateTime.atZone does not "move" the point in time. In fact it tries to present the point in time where the local time in the given timezone is exactly what the LocalDateTime shows.
In other words: if your LocalDateTime represented 10:00 at some date, then the ZonedDateTime output of atZone will also represent 10:00 local time at the specified time zone (except in cases where that local time doesn't exist due to DST changes).
So if your stored time is actually in UTC, you need to add one more step:
ZonedDateTime utcTime = entity.startDate.atZone(ZoneOffset.UTC);
ZonedDateTime localTime = utcTime.withZoneSameInstant(ZoneId.of("Europe/Vienna"));
Alternatively you can avoid calculating the localTime each time and instead configure the DateTimeFormatter to use a given time zone (which means it'll do the necessary calculations internally) using DateTimeFormatter.withZone. If you do this then you can pass the utcTime to it directly.
I have a String like this "15:30:10". Is there anyway to get TimeZone object from this String?
I received a Time String "HH:MM:SS" from another application in other countries (not same with my country). And I have to show the TimeZone. That string is all I have.
Is there anyway to get TimeZone object from this String?
No.
The String contains no timezone information, and you cannot extract information that isn't there.
I received a Time String "HH:MM:SS" from another application in other countries (not same with my country). And I have to show the TimeZone. That String is all I have.
Same answer. You can't do it.
Thinking outside the box a little bit ...
If the time string was supposed to represent the time >>now<< in some unknown timezone, then you could calculate the offset from UTC for that timezone. (It just requires some simple arithmetic which is too trivial to mention.)
But that doesn't give you a real TimeZone. For example, you won't be able to tell the difference between the timezones for France and Namibia.
What is the base timezone of your application? Is there any date associated with this time? If you have date and know the base timezone of your application then it is possible. Otherwise forget it
To give you an overview, we have an MDB application on which we receive transactions throughout the day. One of the column in the feed is a date in the format ddmmyyyy hhmiss. We are trying to parse it through the SimpleDateFormat (java api) so that we could parse the string to a date object to insert into a table.
What is happening is that in some cases my date is moved to a previous date. The observation was that it has moved back by 5-6 hours causing the transaction to become a BVT although it was not.
For eg: One of the transactions had arrived as 24th Sept 12:01 am GMT, but in the table it was inserted as 23rd Sep 7:30pm.
I would also like you to note that the servers are in Chicago. So is it that it is considering the server time since there is almost a time difference of 5-6 hours between London and Chicago?
The code snippet is a below:
SimpleDateFormat lDtFormat = new SimpleDateFormat("yyyy-MM-dd-HH.mm.ss");
Timestamp lDtmp = new java.sql.Timestamp(lDtFormat.parse(strParseString).getTime());
Now my question is should i be changing the above code by passing a locale to the constructor.
SimpleDateFormat lDtFormat = new SimpleDateFormat("yyyy-MM-dd-HH.mm.ss",Locale.UK);
Would the above code snippet fix the issue?
Is it that now after setting the locale it would not convert the time and eventually the date ?
Giving the local explicitly in the constructor of SimpleDateFormat is not sufficient. Its purpose is mainly to support formatting or parsing of localized strings like "March" (english), "Maart" (dutch) etc.
You also need to set the time zone in SimpleDateFormat, here probably the time zone of London like this:
lDtFormat.setTimeZone(TimeZone.getTimeZone("Europe/London"));
You should also check if the time zone on the server should not better be configured to UTC ("GMT") in your scenario of international date and time exchange.
I am trying to run a report every Sunday at midnight that will include data from the previous Saturday at midnight, to 11:59:59 just prior to the report kicking off. Hence, if it were to get kicked off this coming Sunday (2/17), it would:
Execute at 2/17/2013 at midnight (or Monday morning, however you like to think of it)
Include data starting at 2/10/2013 12:00:00 AM (last Saturday midnight)
Include data up to 2/16/2013 11:59:59 PM (this Saturday night, just 1 second prior to the report firing)
I'm trying to obtain a startDateTime and endDateTime to encapsulate the time range, and want to use a JODA LocalDateTime to hold each value. I then need to format them into YYYYMMDD_HHmmss-formatted strings.
Here's my best attempt:
LocalDateTime rightNow = new LocalDateTime();
LocalDateTime startDateTime = rightNow.minusDays(7);
LocalDateTime endDateTime = rightNow.minusDays(1);
String startDateTimeFormatted = startDateTime.toString();
String endDateTimeFormatted = endDateTime.toString();
System.out.println(startDateTimeFormatted);
System.out.println(endDateTimeFormatted);
When I run this I get:
2013-02-06T12:10:27.411
2013-02-12T12:10:27.411
Note: since I'm running this code today (2/13/2013) the start/end dates are different then they would be on Sunday 2/17 (or any other Sunday), but its the time representation and the String formatting I'm really interested in here.
So I ask:
How to get startDateTime to represent (for this example - but I need the answer to be dynamic!) 2/10/2013 12:00:00 AM
How to get endDateTime to represent (for this example - but I need the answer to be dynamic!) 2/16/2013 11:59:59 PM
How to format both startDateTime and endDateTime to appear as 20130210_120000 and 20130216_115959 respectively?
Thanks in advance.
Okay, as mentioned before, I would strongly recommend using UTC as the time zone, and an exclusive end point. So, you can use:
private static final DateTimeFormatter FORMATTER =
DateTimeFormat.forPattern("yyyyMMdd'_'HHmmss")
.withLocale(Locale.US);
...
// I prefer to be explicit about using "the current time". I prefer to use
// an injectable dependency such as a Clock type, too...
DateTime now = new DateTime(System.currentTimeMillis(), DateTimeZone.UTC);
// Note: this *doesn't* ensure that it's the right day of the week.
// You'd need to think about that separately - it may be as simple as
// scheduling it appropriately... but bear your local time zone in mind!
DateTime end = now.withTimeAtStartOfDay();
DateTime start = end.minusDays(7);
String startText = FORMAT.print(start);
String endText = FORMAT.print(end);
Further note that this will use a 24-hour clock, so it would give 20130210_000000 and 20130217_000000 rather than using 120000 as the time. This is far more consistent and unambiguous. Given that it's always midnight though, you might want to just use yyyyMMdd and remove the time part entirely.
System.out.println(DateTime.now().toString("yyyy-MM-dd"));
the toString method accepts a formatting template, see: http://joda-time.sourceforge.net/apidocs/org/joda/time/format/DateTimeFormat.html