Setting server timezone to add/subtract time from date - java

I have users that set their timezone when they create their account. Their account can expire after so many days and the user is able to search for accounts expiring on X day. When the search is run it uses the browser's timezone instead of the users timezone. I have set the timezone like so:
CountryDAO countryDao = new CountryDAO();
String timezone = countryDao.findTimezone(advisor.getPlannerID());
TimeZone.setDefault(TimeZone.getTimeZone(timezone));
DateFormat sdf = new SimpleDateFormat();
sdf.setTimeZone(TimeZone.getDefault());
this code does not set the time zone to the server. I use datapicker to select the date and search but it still uses GMT and not the timezone that is set.
How do I use the time zone that is set?

As the correct answer by Meno Hochschild, java.util.TimeZone.setDefault() is a static method affecting your entire JVM. Not what you want.
Avoid using java.util.Date & Calendar classes. They are badly designed and implemented.
Instead, use the Joda-Time library to manipulate your date-times. Nearly all of Joda-Time is immutable and thread-safe. So it is good for use in servlets & JSP.
Generally you want to work in UTC/GMT, meaning no time zone offset. When presenting to the user, then localize to a particular time zone, format, and language.
I don't know how you are doing it, but it seems that you are determining the user's time zone. Good. (a) Be sure your time zone string is a name of a time zone rather than the outmoded 3-letter code. Those 3-letter codes are neither standardized nor unique. (b) If you are using some kind of JavaScript or other trick to detect and report system settings on the client machine, consider offering the user a way to select a time zone as well. The user may be "thinking" in one time zone while their machine is set for another.
Use that time zone name string to instantiate a DateTimeZone in Joda-Time. Pass that timeZone as an argument to various Joda-Time methods.
DateTimeZone timeZone = DateTimeZone.forId( timeZoneName );
In Joda-Time, a DateTime knows its own time zone (in contrast to a java.util.Date). If you do not specify a time zone, you get the JVM's default time zone. Big tip: Always specify a time zone rather than rely on default, to avoid surprises when running in production.
DateTime dateTimeInUTC = new DateTime( DateTimeZone.UTC );
DateTime dateTimeInParis = dateTimeInUTC.toDateTime( DateTimeZone.forId( "Europe/Paris" ) );
You can add and subtract years, months, days, hours, and such in Joda-Time. And comparison methods test if dates come before or after each other. To find the beginning of a day, call withTimeAtStartOfDay (do not just set hours to zero, as not all days in all zones start at midnight).
DateTime aWeekAgoDateTime = new DateTime( timeZone ).withTimeAtStartOfDay().minusWeeks( 1 ).withTimeAtStartOfDay();
…
boolean isMoreThanWeekOld = someDateTime.isBefore( aWeekAgoDateTime );
Beyond that, I cannot help any more. Your question is not clear. What search? A database? An array or other collection? In JavaScript on client? In Java on server?

TimeZone.setDefault(...) is statically setting the time zone of the JVM (here on the server) for all users. I assume you rather want to set the time zone of the current user session. If so, then you need a special jsp session attribute where you store and read the time zone preference of the user.

Related

Java: Fix incorrect timezone in date object

An external API returns an object with a date.
According to their API specification, all dates are always reported in GMT.
However, the generated client classes (which I can't edit) doesn't set the timezone correctly. Instead, it uses the local timezone without converting the date to that timezone.
So, long story short, I have an object with a date that I know to be GMT but it says CET. How can I adjust for this mistake withouth changing my local timezone on the computer or doing something like this:
LocalDateTime.ofInstant(someObject.getDate().toInstant().plus(1, ChronoUnit.HOURS),
ZoneId.of("CET"));
Thank you.
tl;dr ⇒ use ZonedDateTime for conversion
public static void main(String[] args) {
// use your date here, this is just "now"
Date date = new Date();
// parse it to an object that is aware of the (currently wrong) time zone
ZonedDateTime wrongZoneZdt = ZonedDateTime.ofInstant(date.toInstant(), ZoneId.of("CET"));
// print it to see the result
System.out.println(wrongZoneZdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
// extract the information that should stay (only date and time, NOT zone or offset)
LocalDateTime ldt = wrongZoneZdt.toLocalDateTime();
// print it, too
System.out.println(ldt.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME));
// then take the object without zone information and simply add a zone
ZonedDateTime correctZoneZdt = ldt.atZone(ZoneId.of("GMT"));
// print the result
System.out.println(correctZoneZdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME));
}
Output:
2020-01-24T09:21:37.167+01:00[CET]
2020-01-24T09:21:37.167
2020-01-24T09:21:37.167Z[GMT]
Explanation:
The reason why your approach did not just correct the zone but also adjusted the time accordingly (which is good when desired) is your use of a LocalDateTime created from an Instant. An Instant represents a moment in time which could have different representations in different zones but it stays the same moment. If you create a LocalDateTime from it and put another zone, the date and time are getting converted to the target zone's. This is not just replacing the zone while keeping the date and time as they are.
If you use a LocalDateTime from a ZonedDateTime, you extract the date and time representation ignoring the zone, which enables you to add a different zone afterwards and keep the date and time as it was.
Edit: If the code is running in the same JVM as the faulty code, you can use ZoneId.systemDefault() to get the same time zone as the faulty code is using. And depending on taste you may use ZoneOffset.UTC instead of ZoneId.of("GMT").
I am afraid you will not get around some calculations here. I'd strongly suggest to follow an approach based on java.time classes, but alternatively you might use the java.util.Calendar class and myCalendar.get(Calendar.ZONE_OFFSET) for those calculations:
https://docs.oracle.com/javase/8/docs/api/java/util/Calendar.html#ZONE_OFFSET

What is the right way to format Time between different timezones?

I want to format time like 19:19:00 to different time zones. If I use SimpleDateFormat it always takes into account the start of the epoch: 1970.01.01.
Some timezones have different offsets on the start of the epoch and now. For example, the default offset from Europe/Kiev now is UTC+0200 but in 1970 it was UTC+0300. That means if I run my server under Europe/Kiev the client which login under Europe/Berlin(UTC+0100) will see three hours different instead of two.
I can solve this problem by writing a custom formatter for java.sql.Time. But I want to ask maybe there are some common approach or Java tools/libraries which can solve it.
Another solution can be using joda-time:
TimeZone.setDefault(TimeZone.getTimeZone("Europe/Kiev"));
DateTimeZone.setDefault(DateTimeZone.forID("Europe/Kiev"));
DateTimeFormat.forPattern("HH:mm:ss.SSS")
.withZone(DateTimeZone.forID("Europe/Berlin"))
.print(Time.valueOf("19:00:00").getTime());
You can't format just a time to different time zones. You need a date.
If you want to assume that the date of that time is today, you can try this code:
ZoneId originalZone = ZoneId.of("Europe/Kiev");
ZoneId targetZone = ZoneId.of("Europe/Berlin");
LocalTime originalTime = LocalTime.parse("19:19:00");
LocalTime convertedTime = LocalDate.now(originalZone)
.atTime(originalTime)
.atZone(originalZone)
.withZoneSameInstant(targetZone)
.toLocalTime();
System.out.println(convertedTime);
Is java.time.instant an alternative for you? It handles all Timestamps internally as UTC-Time.
One way to parse it from a string is Instant.parse("2018-05-30T19:00:00")
If you want to have the time for a specific timezone you can get it with myInstant.atZone("Zone")
ZoneId originalZone = ZoneId.of("Europe/Kiev");
ZoneId targetZone = ZoneId.of("Europe/Berlin");
LocalDate assumedDate = LocalDate.now(originalZone);
String formattedTime = assumedDate.atTime(LocalTime.parse("19:19:00"))
.atZone(originalZone)
.withZoneSameInstant(targetZone)
.format(DateTimeFormatter.ofPattern("HH:mm:ss"));
System.out.println(formattedTime);
Today this printed:
18:19:00
When you know the date, you should of course use that instead of just today. In the case of Kyiv and Berlin I think they follow the same rules for summer time (DST), so the precise date may not be important. If converting between zones that don’t use the same transitions, or between a time zone that uses summer time and one that doesn’t, it’s suddenly crucial. And who knows in which of those two countries the politicians will change the rules next year? Better be safe.
Link: Oracle tutorial: Date Time explaining how to use java.time.

Java 8 - tz database time zones

I have to work with Java 8 dates / times in different time zones. For instance:
LocalDateTime dateTime = LocalDateTime.of(2017, Month.JUNE, 1, 13, 39);
Instant instant = dateTime.atZone(ZoneId.of("Europe/Paris")).toInstant();
and a Time Frame is an instance between to dateTimes
But I don't want to hardcode the Time Zone, that is always a bad practice
I couldn't find any constants in the Java API to represent the diffenent Time Zones like https://en.m.wikipedia.org/wiki/List_of_tz_database_time_zones
Is there any mapping as it was in the short zone IDs ????
https://docs.oracle.com/javase/8/docs/api/java/time/ZoneId.html#of-java.lang.String-java.util.Map-
Time zones are frequently redefined by politicians. New zones appear. Old ones get renamed (ex: Asia/Kolkata). Some are determined to not actually be distinct, and end up pointing to another (ex: America/Montreal). And that is just the names – the offsets within each zone are also often modified by politicians for anomalies such as Daylight Saving Time (DST), or deciding to get off DST altogether or deciding to stay on DST all-year-round or deciding to change the offset by 15 minutes to make some political statement such as distinguishing yourself from your neighboring countries. So there is no simple permanent list.
Java comes with a copy of the tzdata time zone database. If any zones you care about experience a change, you need to update this tzdata in your Java installations. Oracle provides a tool for this chore in their implementation; I do not know about others. Similarly you should also update the tzdata in your host computer’s OS as well as other utilities such as your database like Postgres.
As for references to a ZoneId object in Java, you may define some as constants. The java.time classes are thread-safe. So you may keep a single instance around as a constant.
public class TimeUtils {
static public ZoneId ZONEID_EUROPE_PARIS = ZoneId.of( "Europe/Paris" ) ;
static public ZoneId ZONEID_ASIA_KOLKATA = ZoneId.of( "Asia/Kolkata" ) ;
}
You have a LocalDateTime representing potential moments, not a specific point on the timeline. A LocalDateTime has no time zone or offset information. So a LocalDateTime of noon June 1 this year could mean many different moments, with the first noon happening in Kiribati where the time zone has an offset fourteen hours ahead of UTC. Noon in Bangladesh comes later, and noon in Paris France still hours later. So a LocalDateTime has no real meaning until you apply a time zone for context.
LocalDateTime noon1June2017Anywhere = LocalDateTime.of( 2017 , Month.JUNE , 1 , 12 , 0);
Use those constants where you need a ZoneId.
ZonedDateTime noon1June2017EuropeParis = noon1June2017Anywhere.atZone( TimeUtils.ZONEID_EUROPE_PARIS ) ;
ZonedDateTime noon1June2017AsiaKolkata = noon1June2017Anywhere.atZone( TimeUtils.ZONEID_ASIA_KOLKATA ) ;
Note that noon1June2017EuropeParis and noon1June2017AsiaKolkata are two different moments, different points on the timeline. Noon happens much earlier in India than it does in France.
Let's see those two values in UTC as Instant objects. These two Instant objects are not equal, as the Kolkata one is several hours earlier than the Paris one.
Instant instantNoon1June2017EuropeParis = noon1June2017EuropeParis.toInstant() ; // Extract the same moment but in UTC zone.
Instant instantNoon1June2017AsiaKolkata = noon1June2017AsiaKolkata.toInstant() ; // Extract the same moment but in UTC zone.
Externalize
If the intent of your Question is to externalize the decision of what zone to apply, so that you may change that choice without recompiling your source code again, simply store a string of the zone name such as Europe/Paris as a string in some external resource.
Pass your string to ZoneId.of.
ZoneId z = ZoneId.of( someStringOfZoneName ) ;
Possible storage mechanisms commonly used by folks:
Store the text in a file.
Store the text in a database row to retrieved by your app.
Store the text as an entry in a JNDI facility (LDAP server, a configuration file in your Servlet container, etc.) (see Tutorial)
Query from a Web Service. (See Tutorial)
Ask the user for their preference and store in a variable such as a member of a class, or in a Servlet environment store string as an attribute on the context object or on the session object. You can provide the user with a choice list of all zones by calling ZoneId.getAvailableZoneIds.
You can ask for the JVM’s current default zone: ZoneId.systemDefault. But beware, this can be changed at anytime by any code in any app in that JVM.

How to show the correct timezone to users?

I have a Java SE server application with a Saas website and registered users.
I have many events that occur on my server in different days.
Time is registered in localhost in a long number via SYSTEM.currentTimeMillis()/1000
Registered users can check these events time from their respective country and they need to see the correct time based on their timezone (not server timezone) through the website.
How do I show them the historical time of the events in their timezone?
Any idea about how you would deal with this situation?
Easiest way is to use http://momentjs.com/timezone/. Idea is following - you send sth like this in html markup
<div class="raw-datetime">2014-12-01 12:00:00 UTC+03:00</div>
And after page loads - you run javascript that adjusts all raw datetime to browser timezone.
java.time
First you need to determine the user’s time zone. Search on StackOverflow to learn that ultimately the best way to do that is to ask the user. You can try to use JavaScript on the browser to auto-detect a time zone, but there are issues.
You need to arrive at a proper time zone name, usually a continent/cityOrRegion such as America/Montreal or Asia/Kolkata. Never use those 3-4 letter codes like EST or IST as they are neither standardized nor unique.
To localize a date-time you need to know the user’s Locale, a human language and a set of cultural norms.
With a time zone in hand, use the new java.time framework built into Java 8 and later. Inspired by Joda-Time, defined by JSR 310, and extended by the ThreeTen-Extra project.
long epochSeconds = … ;
Instant instant = Instant.ofEpochSeconds( epochSeconds );
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant, zoneId);
Locale locale = Locale.CANADA_FRENCH;
DateTimeFormatter formatter = DateTimeFormatter.ofLocalizedDateTime( FormatStyle.FULL ).withLocale( locale );
String output = zdt.format( formatter );
First of all, we will use UTC as a default and unique time zone in the system. Because, the time never go back or go to the future in UTC. There is no time shift for daylight saving.
So, all applications (applications which we develop and has timezone support for their users) environment require a JVM parameter, which provides a UTC based environment.
Timezone JVM parameter usage
-Duser.timezone=UTC
For Views
For views, the date/time object should be rendered according to the specified time-zone. For Java world, this is handled by formatDate tld in jstl.
Every project contains its own timezone holding logic itself.
User.timeZone : for admin panels
Some fmt:formatDate example
<fmt:formatDate value="${someDateTime}" timeZone="${user.timeZone}" pattern="MMM dd, yyyy - HH:mm:ss.S"/>
For further information about formatDate taglib please see check the link or google it.
For Forms (Getting the date / time data from the user)
When you getting date information via forms the you need to consider time zone and perform the time conversion. The conversion direction is User Time zone to UTC.
The time in database should be in UTC time zone.
Motto is save it globally; show it locally :)
EDIT:
Hold timezone as a subfield of User object and set it to your formatter when you want to show the time, you can use it in JavaSE.
SimpleDateFormat formatter = new SimpleDateFormat("dd-MM-yyyy HH:mm");
formatter.setTimeZone(TimeZone.getTimeZone(user.getTimeZone()));

android java time

i'm building an android application which have a chat.
in this chat i each message to have its time sent signature.
my question is as follow:
lets say that the time in my country is X. my friend is abroad and his time is X minus 7 hours.
i'm sending him a message at 16:00 local time.
i want to avoid the situation that he will get at 09:00 a message which it signature will be 16:00 (which is a time in future if you're looking in the eyes of that friend in his country).
is there a way that in my phone the message will be written as 16:00 and in his phone it will be written as 09:00 ? i there a way to convert a time to a local time ?
System.currentTimeMillis() does give you the number of milliseconds since January 1, 1970 00:00:00 UTC. Date object does not save your local timezone.
You can use DateFormats to convert Dates to Strings in any timezone:
DateFormat df = DateFormat.getTimeInstance();
df.setTimeZone(TimeZone.getTimeZone("gmt"));
String gmtTime = df.format(new Date());
linked response
You should keep all time communications using UTC time. Then localize it for display based on the devices current timezone setting.
Use a long to save your time information as milliseconds since "epoch" (which is January 1, 1970, 00:00:00 GMT). It can be retreived with the Date.getTime() method and new Date objects are easily created using the Date(long millis) constructor. The Date objects are then displayed using the local timezone settings on each device.
EDIT:
Epoch is a defined point in time which is expressed differently in different time zones: 1970-01-01 00:00:00 GMT but
1969-12-31 19:00:00 EST. The timestamp is just the number of milliseconds elapsed since that time. So, for example the timestamp 1341169200 corresponds to 2012-07-01 19:00:00 GMT and 2012-07-01 14:00:00 EST.
You will need to save the time zone which your message will be saved in, and transfer it (or send the unix epoch time) and then on the other side make sure you read it in with the Locale time (using the Android documentation for things like http://developer.android.com/reference/java/util/Calendar.html can help).
Take a look at the answer over here:
https://stackoverflow.com/a/6094475/346232
You need to change the time to UTC and then convert on the device to the timezone.
Avoid java.util.Date/.Calendar
The java.util.Date/.Calendar classes bundled with Java (and Android) are notoriously troublesome, flawed in both design and implementation.
Joda-Time
The Joda-Time library is the way to go. This library inspired the java.time package now built into Java 8 (not available on Android).
UTC
As other answers suggested, the best practice (generally) is to keep your business logic and data storage/communication in UTC time zone (which some think of as no time zone or an "anti" time zone). Adjust to a specific time zone only when expected by the user or data-consumer.
Time Zone
The DateTime class in Joda-Time represents a date-time value along with an assigned time zone.
Note that it is best to specify a time zone in all your operations. Otherwise you will be implicitly relying on the JVM’s current default time zone. This is risky because that zone can change – even at runtime at any moment by any code in any thread of any app running within your app’s JVM. And use proper time zone names, never the 3-4 letter codes.
Example Code
Example code in Joda-Time 2.7.
DateTime sent = DateTime.now( DateTimeZone.getDefault() ) ;
DateTime sentUtc = nowMine.withZone( DateTimeZone.UTC ) ; // Generally, use this for your work, including communicating to other threads and apps and such.
When ready to display to the other user, adjust to the expected time zone.
DateTimeZone zone = DateTimeZone.forID( "America/Montreal" ) ; // Or DateTimeZone.getDefault() if you want to rely on their JVM’s current default. To be absolutely sure of expected time zone, you really must ask the user.
DateTime sentMontréal = sentUtc.withZone( zone );
To generate a textual representation of those date-time objects, search the many Questions and Answers on StackOverflow.com on that subject. Search for terms like "joda" and "DateTimeFormatter" and "DateTimeFormat".

Categories

Resources