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

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.

Related

How to find epoch format current time of GMT using java

I have written below code which is running, and giving output. But I'm not sure It's a right one.
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Date date = new Date();
sdf.setTimeZone(TimeZone.getTimeZone("GMT-7"));
String value = sdf.format(date);
System.out.println(value);
Date date2 = sdf.parse(value);
long result = date2.getTime();
System.out.println(result);
return result;
The above code what I'm trying is, I just need to get the current time of GMT time zone and convert it as epoch format which is gonna used in Oracle db.
Can someone tell me that format, and the above code is right?
First, you should not store time since the epoch as a timestamp in your database. Look into the date-time datatypes that your DMBS offers. In Oracle I think that a date column will be OK. For most other DBMS you would need a datetime column. timestamp and timestamp with timezone may be other and possibly even sounder options depending on your exact requirements.
However, taking your word for it: Getting the number of milliseconds since the epoch is simple when you know how:
long millisecondsSinceEpoch = System.currentTimeMillis();
System.out.println(millisecondsSinceEpoch);
This just printed:
1533458641714
The epoch is defined in UTC, so in this case we need to concern ourselves with no other time zones.
If you needed seconds rather than milliseconds, it’s tempting to divide by 1000. However, doing your own time conversions is a bad habit since the libraries already offers them, and using the appropriate library methods gives clearer, more explanatory and less error-prone code:
long secondsSinceEpoch = Instant.now().getEpochSecond();
System.out.println(secondsSinceEpoch);
1533458641
You said:
I just need to get the current time of GMT time zone…
Again taking your word:
OffsetDateTime currentTimeInUtc = OffsetDateTime.now(ZoneOffset.UTC);
System.out.println(currentTimeInUtc);
long millisecondsSinceEpoch = currentTimeInUtc.toInstant().toEpochMilli();
System.out.println(millisecondsSinceEpoch);
2018-08-05T08:44:01.719265Z
1533458641719
I know that GMT and UTC are not exactly the same, but for most applications they can be (and are) used interchangeably.
Can someone tell me (if) the above code is right?
When I ran your code just now, its output agreed with mine except the milliseconds were rounded down to whole thousands (whole seconds):
1533458641000
Your code has some issues, though:
You are using the old, long out-dated and poorly designed classes SimpleDateFormat, Date and TimeZone. The first in particular has a reputation for being troublesome. Instead we should use java.time, the modern Java date and time API.
Bug: In your format pattern string you are using lowercase hh for hour of day. hh is for hour within AM or PM, from 1 through 12, so will give you incorrect results at least half of the day. Uppercase HH is for hour of day.
Don’t use GMT-7 as a time zone. Use for example America/Los_Angeles. Of course select the time zone that makes sense for your situation. Edit: You said:
I just want to specify the timezone for sanjose. GMT-7 is refer to
sanjose current time.
I believe many places are called San Jose. If you mean San Jose, California, USA, you are going to modify your program to use GMT-8 every time California goes back to standard time and opposite when summer time (DST) begins?? Miserable idea. Use America/Los_Angeles and your program will work all year.
Since you ask for time in the GMT time zone, what are you using GMT-7 for at all?
There is no point that I can see in formatting your Date into a string and parsing it back. Even if you did it correctly, the only result you would get would be to lose your milliseconds since there are no milliseconds in your format (it only has second precision; this also explained the rounding down I observed).
Links
Oracle tutorial: Date Time explaining how to use java.time, the modern Java date and time API.
San Jose, California on Wikipedia
Why not use Calendar class?
public long getEpochTime(){
return Calendar.getInstance(TimeZone.getTimeZone("GMT-7")).getTime().getTime()/1000; //( milliseconds to seconds)
}
It'll return the current Date's Epoch/Unix Timestamp.
Based on Harald's Comment:
public static long getEpochTime(){
return Clock.system(TimeZone.getTimeZone("GMT-7").toZoneId() ).millis()/1000;
}
Here is a solution using the java.time API
ZonedDateTime zdt = LocalDateTime.now().atZone(ZoneId.of("GMT-7"));
long millis = zdt.toInstant().toEpochMilli();

How to parse offset it is not specified?

I have time 12:00:00 in format HH:mm:ss.
I know that this time comes from server witch is setup with +3 offset.
If i use SimpleDateFormat df = new SimpleDateFormat("HH:mm:ss");, it parses time with regard to device, which can be in a different timezone.
Is there another way to parse it with regard to +3 offset except adding it to the original string?
First, should your server rather send the time in UTC? If clients are everywhere, this would seem more time zone neutral and standardized. However, the way to handle it in code wouldn’t be much different. In any case the server offset form UTC could be constant:
private static final ZoneOffset serverOffset = ZoneOffset.ofHours(3);
In real code you will probably want to make it configurable somehow, though. To parse:
OffsetTime serverTime = LocalTime.parse("12:00:00").atOffset(serverOffset);
System.out.println(serverTime);
This prints
12:00+03:00
Since your time format agrees with LocalTime’s default (ISO 8601), we need no explicit formatter. If a representation of the time with offset is all you need, we’re done. If you need to convert to the user’s local time, to do that reliably you need to decide both a time zone and a date:
LocalTime clientTime = serverTime.atDate(LocalDate.of(2018, Month.JANUARY, 25))
.atZoneSameInstant(ZoneId.of("Indian/Maldives"))
.toLocalTime();
System.out.println(clientTime);
With the chosen day and zone we get
14:00
Please substitute your desired time zone and date.
Just hypothetically, if you knew the user’s offset from UTC, you could use just that:
LocalTime clientTime = serverTime.withOffsetSameInstant(ZoneOffset.of("-08:45"))
.toLocalTime();
The example yields 00:15. However, no one knows when the politicians introduce summer time (DST) or other anomalies in the user’s time zone, so I discourage relying on an offset alone.
And yes, I too am using java.time. SimpleDateFormat is not only long outdated, it is also notoriously troublesome, so java.time is what I warmly recommend.
Set the timezone on your SimpleDateFormat object:
SimpleDateFormat fmt = new SimpleDateFormat("HH:mm:ss");
fmt.setTimeZone(TimeZone.getTimeZone("GMT+03:00"));
I recommend you use the Java 8 date and time API (package java.time) instead of the old API, of which SimpleDateFormat is a part.
Using the Java 8 DateTime API:
DateTimeFormatter formatter = DateTimeFormatter
.ofPattern("HH:mm:ss");
LocalTime clientLocalTime = LocalTime
.parse("12:00:00", formatter)
// Create an OffsetTime object set to the server's +3 offset zone
.atOffset(ZoneOffset.ofHours(3))
// Convert the time from the server timezone to the client's local timezone.
// This expects the time value to be from the same day,
// otherwise the local timezone offset may be incorrect.
.withOffsetSameInstant(ZoneId.systemDefault().getRules().getOffset(Instant.now()))
// Drop the timezone info - not necessary
.toLocalTime();

How to check if 2 dates are on the same day in Java

I have 2 Date variables, Date1 and Date2.
I want to check if Date 1 fall on the same date as Date2 (but they are allowed to have different times).
How do i do this?
It looks like a really easy thing to do, but i'm struggling.
EDIT: I want to avoid external libraries and stuff
EDIT:
My orgional idea was to remove the hour, min, sec but those features are marked as depreciated in Java. So what should I use????
Although given answers based on date component parts of a java.util.Date are sufficient in many parts, I would stress the point that a java.util.Date is NOT a date but a kind of UNIX-timestamp measured in milliseconds. What is the consequence of that?
Date-only comparisons of Date-timestamps will depend on the time zone of the context. For example in UTC time zone the date-only comparison is straight-forward and will finally just compare year, month and day component, see other answers (I don't need to repeat).
But consider for example the case of Western Samoa crossing the international dateline in 2011. You can have valid timestamps of type java.util.Date, but if you consider their date parts in Samoa you can even get an invalid date (2011-12-30 never existed in Samoa locally) so a comparison of just the date part can fail. Furthermore, depending on the time zone the date component can generally differ from local date in UTC zone by one day, ahead or behind, in worst case there are even two days difference.
So following extension of solution is slightly more precise:
SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMdd");
fmt.setTimeZone(...); // your time zone
return fmt.format(date1).equals(fmt.format(date2));
Similar extension also exists for the more programmatic approach to first convert the j.u.Date-timestamp into a java.util.GregorianCalendar, then setting the time zone and then compare the date components.
Why don't you simply compare the year, month and day? You can write your method for doing it something like:
private boolean isDateSame(Calendar c1, Calendar c2) {
return (c1.get(Calendar.YEAR) == c2.get(Calendar.YEAR) &&
c1.get(Calendar.MONTH) == c2.get(Calendar.MONTH) &&
c1.get(Calendar.DAY_OF_MONTH) == c2.get(Calendar.DAY_OF_MONTH));
}
Today = Span Of Time
While the other answers may be correct, I prefer the approach where we recognize that "today" is actually a span of time.
Because of anomalies such as Daylight Saving Time (DST), days vary in length, not always 24 hours long. Here in the United States, some days are 23 hours long, some 25.
Half-Open
Commonly in data-time work, we use the "Half-Open" strategy where the beginning of a span is inclusive and the ending is exclusive. So that means "today" spans from the first moment of today up to, but not including, the first moment of tomorrow.
Time Zones
Time zones are critical, as explained in the correct answer by Meno Hochschild. The first moment of a day depends on its time zone rules.
Joda-Time
The Joda-Time library has nice classes for handling spans of time: Period, Duration, and Interval.
DateTimeZone timeZone = DateTimeZone.forID( "Europe/Paris" );
DateTime now = new DateTime( timeZone );
Interval today = new Interval( now.withTimeAtStartOfDay(), now.plusDays(1).withTimeAtStartOfDay() );
DateTime dateTimeInQuestion = new DateTime( date ); // Convert java.util.Date.
boolean happensToday = today.contains( dateTimeInQuestion );
Benefits
This approach using a span of time has multiple benefits:
Avoids Daylight Saving Time (DST) issues
Lets you compare date-time values from other time zones
Flexible, so you can use the same kind of code for other spans (multiple days, months, etc.)
Gets your mind shifted away from calendar dates (a layered abstraction) and onto date-times as points on a flowing timeline (the underlying truth).
Java 8 has a new java.time package built-in. These new classes are modeled after Joda-Time but are entirely re-architected. This same kind of code can be written using java.time.
When you use the toString() method what do you get? Is it only the year/month/day or time too? If it is then you could simply compare the strings of the two objects. (date1.toString().equals(date2.toString()));

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".

Convert time represented as String containing timezone names ('z') to UTC time

I want to convert Strings like "20000603163334 GST" or "20000603163334 -0300" to UTC time. The problem is that time zones in my strings can be 'general time zones', I mean they can be strings as CET, GST etc. etc. And I don't know how to convert these ones.
Because of these string time zones I can not use Joda Time's DateTimeFormat.forPattern("yyyyMMddhhmmss z").withZone(DateTimeZone.UTC);, because according to the documentation: "Time zone names ('z') cannot be parsed".
So, one question I have is if you know a method to go around this limitation in Joda Time? I would prefer to use Joda Time, if possible, instead of the standard Java API.
Another in which I thought I can solve this problem with time zone's names is to use the Java's SimpleDateFormat.
So I make something like:
SimpleDateFormat f = new SimpleDateFormat("yyyyMMddhhmmss z");
//f.setTimeZone(TimeZone.getTimeZone("UTC"));
f.setCalendar(new GregorianCalendar(TimeZone.getTimeZone("UTC")));
Date time = f.parse("20000603163334 GST");
The SimpleDateFormat parses the String (I don't care here about the problem that there are multiple time zones with the same name - what this class parses it's good for me).
The problem is that I don't know how to convert it from here to UTC. How can I do this?
The fact that I set the f's time zone to UTC (in both the two ways from above) doesn't help. I hope someone can help me fix this, I read a lot of questions and answers on this theme here, on stackoverflow, but I haven't found a solution yet.
I found two solutions to your problem. The first was to set the default time zone to UTC:
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
I'm not sure what other side effect this might have.
The second solution I found was to use a different SimpleDateFormat for output.
SimpleDateFormat f = new SimpleDateFormat("yyyyMMddhhmmss z");
f.setTimeZone(TimeZone.getTimeZone("UTC"));
Date time = f.parse("20000603163334 GST");
System.out.println(time);
System.out.println("(yyyyMMddhhmmss z): " + f.format(time));
SimpleDateFormat utc = new SimpleDateFormat("yyyyMMddhhmmss z");
utc.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println("(yyyyMMddhhmmss z): " + utc.format(time));
Using two SimpleDateFormat objects allowed the output to be put in UTC Time. Here is the output from running this code:
Sat Jun 03 08:33:34 EDT 2000
(yyyyMMddhhmmss z): 20000603043334 GST
(yyyyMMddhhmmss z): 20000603123334 UTC
Here may be the reason why Joda does not support 3 letter zone ids. This is from the TimeZone ( http://download.oracle.com/javase/6/docs/api/java/util/TimeZone.html ) JavaDoc. As far as Joda goes, I didn't see a workaround, but I'm not very familiar with that library.
Three-letter time zone IDs For
compatibility with JDK 1.1.x, some
other three-letter time zone IDs (such
as "PST", "CTT", "AST") are also
supported. However, their use is
deprecated because the same
abbreviation is often used for
multiple time zones (for example,
"CST" could be U.S. "Central Standard
Time" and "China Standard Time"), and
the Java platform can then only
recognize one of them.

Categories

Resources