Reputation: 13083
I need a single timezone, and I have timezone offset.
For example, I have offset = 14400000
. I would like to have a timezone (any place, any city). I can use this timezone in SimpleDateFormat
so it prints the correct time for this zone.
This is what I found (I refactored a bit):
private TimeZone getTimeZone(int offset) {
final TimeZone UTC_TIME_ZONE = TimeZone.getTimeZone("UTC");
String[] ids = TimeZone.getAvailableIDs(offset);
if (ids == null || ids.length == 0) {
return UTC_TIME_ZONE;
}
String matchingZoneId = ids[0];
return TimeZone.getTimeZone(matchingZoneId);
}
I get:
sun.util.calendar.ZoneInfo[id="Asia/Baku",offset=14400000,dstSavings=0,useDaylight=false,transitions=68,lastRule=null]
I do not like this approach, because TimeZone
will call ZoneInfoFile
getZoneIds(int var1)
which is here. This is decompiled code.
public static String[] getZoneIds(int var0) {
ArrayList var1 = new ArrayList();
String[] var2 = getZoneIds();
int var3 = var2.length;
for(int var4 = 0; var4 < var3; ++var4) {
String var5 = var2[var4];
ZoneInfo var6 = getZoneInfo(var5);
if (var6.getRawOffset() == var0) {
var1.add(var5);
}
}
var2 = (String[])var1.toArray(new String[var1.size()]);
Arrays.sort(var2);
return var2;
}
You can see, that it loops over existing objects to build an array of Zone ids, while I just need one!
My question, is there a more effective way to do so? I just need ANY timezone id or a single timezone of given offset.
Upvotes: 0
Views: 4154
Reputation: 338201
Do not do what you are attempting.
Does not make sense, as zones sharing an offset at one moment in time may not share that offset at other moments in the past or future.
The Answer by Ole V.V. is correct. Here is a bit more discussion.
I need a single timezone, and I have timezone offset.
Not really possible.
An offset-from-UTC is nothing more than a number of hours, minutes, and seconds ahead of, or behind, UTC.
A time zone is much more. A zone is a history of past changes in offset used by the people of a specific region. A zone also has rules for present and future changes to the offset for that region. So a time zone is always preferable to an mere offset, but only when known with certainty. In your case, you do not know the zone, and should not guess; just stick with using the offset.
A given offset cannot lead you to a particular time zone without ambiguity and without error.
Europe/Gibralter
& Europe/Oslo
& Africa/Lagos
all share the same offset today, one hour ahead of UTC. So with only an offset-from-UTC of +01:00
how would you know which is appropriate?Note how the ZoneRules
class can give you an offset for a particular zone only if you provide a moment (a date-time value) passed as an argument.
ZoneOffset offset = ZoneId.of( "Africa/Tunis" ) // Instantiate a `ZoneId`.
.getRules() // Obtain a `ZoneRules` object from that `ZoneId` object.
.getOffset( Instant.now() ) ; // Interrogate that `ZoneRules` for an offset-from-UTC in use at a specific moment in history.
I just need ANY timezone id or a single timezone of given offset.
This in not advisable. As discussed above, the time zones sharing a particular offset on one date may not share that offset on another date. Each zone varies by its history of changes in offset. So picking a zone arbitrarily is not sensible.
As explained in the other Answer, the TimeZone
& ZoneInfo
classes are now legacy. They are part of the troublesome old date-time classes that are supplanted by the java.time classes.
Instead use ZoneOffset
(offset-from-UTC) and ZoneId
(time zone).
Specify a proper time zone name in the format of continent/region
, such as America/Montreal
, Africa/Casablanca
, or Pacific/Auckland
. Never use the 3-4 letter abbreviation such as EST
or IST
as they are not true time zones, not standardized, and not even unique(!).
ZoneId z = ZoneId.of( "America/Montreal" );
The java.time framework is built into Java 8 and later. These classes supplant the troublesome old legacy date-time classes such as java.util.Date
, Calendar
, & SimpleDateFormat
.
The Joda-Time project, now in maintenance mode, advises migration to the java.time classes.
To learn more, see the Oracle Tutorial. And search Stack Overflow for many examples and explanations. Specification is JSR 310.
Where to obtain the java.time classes?
The ThreeTen-Extra project extends java.time with additional classes. This project is a proving ground for possible future additions to java.time. You may find some useful classes here such as Interval
, YearWeek
, YearQuarter
, and more.
Upvotes: 4
Reputation: 86173
The TimeZone
class is long outdated together with the classes you would usually use it with; Calendar
, GregorianCalendar
, DateFormat
and SimpleDateFormat
.
I recommend you start using java.time
, the modern Java date and time API instead. It is generally so much nicer to work with. Then you will need a ZoneId
object for your time zone. For a ZoneId
with constant offset, we create an instance of ZoneOffset
, one of the two subclasses of ZoneId
.
int offsetMilliseconds = 14_400_000;
int offsetSeconds = (int) TimeUnit.MILLISECONDS.toSeconds(offsetMilliseconds);
// to check that we haven’t lost any precision, convert back to milliseconds and compare
if (TimeUnit.SECONDS.toMillis(offsetSeconds) != offsetMilliseconds) {
throw new IllegalArgumentException("Millisecond precision not supported: " + offsetMilliseconds);
}
ZoneId tzid = ZoneOffset.ofTotalSeconds(offsetMilliseconds / 1000);
System.out.println(tzid);
// test the time zone we got
System.out.println(ZonedDateTime.now(tzid));
On my computer this just printed:
+04:00
2018-01-16T19:44:22.863760+04:00
A ZoneOffset
only has precision of seconds, not milliseconds, which is fine for your case of 14 400 000 milliseconds. Should you have a number of milliseconds that don’t makeup a whole number of seconds, you may want to round to whole seconds rather than throw an exception, your decision.
Only if you need to pass an old-fashioned TimeZone
object to some legacy API that you cannot change or don’t want to change just now, convert the ZoneId
we got above:
TimeZone oldfashionedTimeZone = TimeZone.getTimeZone(tzid);
This will give you a TimeZone
with display name GMT+04:00
and (of course) an offset of 4 hours. Beware, however, that if TimeZone
doesn’t recognize the time zone you are trying to convert to (a likely case if your offset was some odd number of seconds), it will tacitly give you GMT (just one of the problems with the old classes). So you should probably check the TimeZone
object you got, for example like this:
if (oldfashionedTimeZone.getRawOffset() != offsetMilliseconds) {
throw new IllegalStateException("Conversion to legacy TimeZone object failed");
}
Upvotes: 2
Reputation: 12751
You can construct a TimeZone instance for any offset, using SimpleTimeZone:
new SimpleTimeZone(offset, "My TimeZone");
Upvotes: 2