Java getTimeZone() behaving differently for different kind of inputs - java

For the below code I get different outputs
TimeZone t = TimeZone.getTimeZone("GMT-8"); // Also tried UTC-8 and GMT-8:00
//TimeZone t = TimeZone.getTimeZone("America/Los_Angeles");
SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy");
Date d = null;
Calendar c = Calendar.getInstance(t);
c.set(Calendar.MONTH, Calendar.AUGUST);
c.set(Calendar.DAY_OF_MONTH, 22);
c.set(Calendar.YEAR, 2013);
d = c.getTime();
String s = sdf.format(d);
System.out.println(s + " " + t.inDaylightTime(d));
Output is:
08/22/2013 false
Now America/Los_Angeles is GMT-8 / UTC-8 or PST. But when I change parameter from GMT-8 to America/Los_Angeles,
Output is:
08/22/2013 true
Can't use PST like abbreviations since its deprecated. Also CST can mean both Central Standard time and China Standard Time.
The input I have is like -8, -9 -14 etc. to which I wish to prepend GMT/UTC to know if I can get the DST activation on a given date.
Please guide me in this regard.

The results are correct, when you specify GMT with an offset there is no way to know which country/city is this, so it is not possible to determine if Day Light Time is active or no.
So inDaylightTime() will be false for all time zones specified with GMT and an offset.
CST in Java is Central Standard Time, you can check yourself by this line of code:
System.out.println(TimeZone.getTimeZone("CST").getDisplayName());
China Standard Time zone ID in Java is CTT
Edit
Consider using Joda time and date API for more up-to-date Timezone information

The results are correct, as explained by #iTech. Just to add, you can lookup the timezone ids available as
import java.util.TimeZone;
public class TimeZones {
public static void main(String[] args) {
String[] timezones = TimeZone.getAvailableIDs();
System.out.println(timezones.length);
for (String t : timezones) {
System.out.println(t);
}
}
}
This prints a long list of 600 timezones available.
You can then get the display name for the abbreviated ones as
System.out.println(TimeZone.getTimeZone("IST").getDisplayName());

Related

Java and converting displayed value into local time zone and daylight savings

I know this is an oft asked type of question, and I've been looking around the web for solutions, but to no avail. The problem is that we're fetching a date value from the database and want to display it in a PDF in local time. The raw formatted value is "2022/05/24 15:18:10" and using Costa Rica as an example client time zone (we're in LA), we'd want this "2022/05/24 16:18:10". However, we're getting this: "2022/05/24 17:18:10".
One of the odd things I'm seeing when I run the code below is that it seems that our server time zone is PST, according to Java, but is actually PDT.
creationDate from db=Tue May 24 15:18:10 PDT 2022
Desired creationDate output=2022/05/24 16:18:10
cal.getTimeZone.getDisplayName()=Pacific Standard Time
formatted creationDate=2022/05/24 17:18:10
So one question is: why are we seeing PST instead of PDT and the second question is how do I fix this?
Example code:
public static void main(
String[] args)
{
final String DATE_TIME_PATTERN = "yyyy/MM/dd HH:mm:ss";
final String LA_TIMEZONE_ID = "America/Los_Angeles";
DateFormat df = new SimpleDateFormat(DATE_TIME_PATTERN);
TimeZone serverTimeZone = TimeZone.getTimeZone(LA_TIMEZONE_ID);
Calendar cal = Calendar.getInstance(/* serverTimeZone */); // works same with or without param
TimeZone clientTimeZone = TimeZone.getTimeZone("CST"); // Costa Rica = Central Standard Time
Date creationDate;
df.setTimeZone(clientTimeZone);
// Values as fetched from database.
cal.set(Calendar.DAY_OF_MONTH, 24);
cal.set(Calendar.MONTH, Calendar.MAY);
cal.set(Calendar.YEAR, 2022);
cal.set(Calendar.HOUR_OF_DAY, 15);
cal.set(Calendar.MINUTE, 18);
cal.set(Calendar.SECOND, 10);
creationDate = cal.getTime();
System.out.println("creationDate from db=" + creationDate.toString());
System.out.println("Desired creationDate output=2022/05/24 16:18:10");
System.out.println("cal.getTimeZone.getDisplayName()=" + cal.getTimeZone().getDisplayName());
System.out.println("formatted creationDate=" + df.format(creationDate));
}
So, after a bit of hacking and googling, it would appear (at least to me), that "Central Standard Time" isn't what you want to use (nor should you use it generally).
From time-and-date/Central Standard Time (CST)
Caution: This is NOT the current local time in most locations in that time zone
North America: Only some locations are currently on CST because most places in this time zone are currently on summer time / daylight saving time and are observing CDT.
And then add in time-and-date/Time Zone in Costa Rica
Costa Rica observes Central Standard Time all year. There are no Daylight Saving Time clock changes.
That's not confusing at all 🙄. So, as I "understand" it, CST is normally -6 hours and CDT is -5 hours, but Costa Rica is always -6 hours.
So, based on the observations of your code, CST seems to be having the day light savings value applied to it, regardless of what you do.
So, what to do about it? Well, the simple answer is, don't use it, in fact, stop using the java.util.Date and related APIs altogether and instead, make use of the replacement java.time APIS and the America/Costa_Rica time zone directly, for example...
final String DATE_TIME_PATTERN = "yyyy/MM/dd HH:mm:ss";
final String LA_TIMEZONE_ID = "America/Los_Angeles";
final String dateStringValue = "2022/05/24 15:18:10";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
LocalDateTime ldt = LocalDateTime.parse(dateStringValue, formatter);
ZoneId zoneId = ZoneId.of(LA_TIMEZONE_ID);
ZonedDateTime zdt = ldt.atZone(zoneId);
System.out.println(" Local = " + ldt);
System.out.println(" zdt = " + zdt);
System.out.println("Costa_Rica = " + zdt.withZoneSameInstant(ZoneId.of("America/Costa_Rica")));
System.out.println("US/Central = " + zdt.withZoneSameInstant(ZoneId.of("US/Central")));
which prints...
Local = 2022-05-24T15:18:10
zdt = 2022-05-24T15:18:10-07:00[America/Los_Angeles]
Costa_Rica = 2022-05-24T16:18:10-06:00[America/Costa_Rica]
US/Central = 2022-05-24T17:18:10-05:00[US/Central]
This is probably also a good (and over due) reason to dump the old java.util.Date and related classes and update to the java.time APIs, see the date/time trail for more details
Handling Daylight Savings Time in Java might also be worth a read

What timezone abbreviations does SimpleDateFormat (not TimeZone) support for parsing?

My previous question was closed as duplicate of Which three-letter time zone IDs are not deprecated? but I believe this is something completely different.
The other question is about the java.util.TimeZone class.
My question is about the java.text.SimpleDateFormat class.
As I already mentioned in the original question, a simple test revealed that these 2 classes do not support the same timezone abbreviations.
For example:
TimeZone supports "CTT" but SimpleDateFormat does not
SimpleDateFormat supports "CEST" but TimeZone does not
Where (or how) can I find a full list of abbreviations that SimpleDateFormat is able to parse when using "z" in the parse string, and what each abbreviation is considered to mean?
Original question for reference:
[Context: I'm not a developer but need to document existing code]
I understand that there are no standards for timezone abbreviations and so CST can mean Central Standard Time, China Standard Time, Cuba Standard Time, ...
But if I have code like this:
String time = "12:00:00.000 CST Tue Dec 17 2019";
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS zzz EEE MMM dd yyyy");
Date utcTime = sdf.parse(time);
System.out.println(utcTime.toGMTString());
Then the result is:
17 Dec 2019 18:00:00 GMT
So this means that SimpleDateFormat.parse() interprets CST as Central Standard Time / UTC-6.
How/where can I get a full list of timezone abbreviations that SimpleDateFormat.parse() supports AND their meaning (i.e. just knowing that CST is supported is not enough, I need to know that this is interpreted as Central Standard Time, not Cuba Standard Time).
I would expect to find the answer on the Javadocs page for SimpleDateFormat but it only gives PST as example, not a full list.
The Javadocs page for TimeZone says :
For compatibility with JDK 1.1.x, some other three-letter time zone
IDs (such as "PST", "CTT", "AST") are also supported.
But doesn't say which ones.
It also only mentions three-letter IDs, while the code above will also work fine with CEST for example (Central European Summer Time).
If I check the list returned by TimeZone.getAvailableIDs(); it does not contain CEST either, but it does contain items like "Cuba" and "Eire", which do not work in the above code snippet (even when I change zzz to zzzz in the parse string).
So I conclude that java.text.SimpleDateFormat does not support the same abbreviations as java.util.TimeZone.
Where can I find a list of abbreviations that SimpleDateFormat is able to parse?
It sounds like a simple question, but the answer is complicated. The time zone abbreviations supported by Java (whether we are talking the modern DateTimeFormatter or the troublesome and outdated SimpleDateFormat) are not defined by Java itself but by the locale data that Java uses. Complications include
Java can be configured to take locale data from different sources. This means that setting the system property java.locale.providers when you run your program will change the set of abbreviations supported.
The default locale providers were changed from Java 8 to Java 9. So running your program on a different Java version will give you a different set of supported time zone abbreviations.
CLDR, the locale data that are the default from Java 9, come in versions. So every new Java version may come with a different set of supported abbreviations.
Time zone abbreviations come in languages. So a formatter with French locale will support other abbreviations than a formatter with German locale, for example.
Time zone abbreviations are ambiguous. So even if PST is a supported abbreviation, you don’t know whether it parses into Pitcairn Standard Time, Pacific Standard Time or Philippine Standard Time.
One way to get an idea: Run a program formatting dates at different times of year into a time zone abbreviation. Since many time zones use summer time (DST) and use a different abbreviation during summer time, you will want to try to hit a date in the summer and a date in the standard time of year for those zones.
System.out.println("java.locale.providers: " + System.getProperty("java.locale.providers"));
DateTimeFormatter zoneAbbreviationFormatter = DateTimeFormatter.ofPattern("zzz", Locale.FRENCH);
Instant northernSummer = Instant.parse("2019-07-01T00:00:00Z");
Instant southernSummer = Instant.parse("2020-01-01T00:00:00Z");
Set<String> supportedAbbreviations = new TreeSet<>();
for (String zid : ZoneId.getAvailableZoneIds()) {
ZoneId zone = ZoneId.of(zid);
supportedAbbreviations.add(northernSummer.atZone(zone).format(zoneAbbreviationFormatter));
supportedAbbreviations.add(southernSummer.atZone(zone).format(zoneAbbreviationFormatter));
}
System.out.println("" + supportedAbbreviations.size() + " abbreviations");
System.out.println(supportedAbbreviations);
Output on my Java 9 was (scroll right to see it all):
java.locale.providers: null
205 abbreviations
[ACDT, ACST, ACT, ACWST, ADT, AEDT, AEST, AFT, AKDT, AKST, ALMT, AMST, AMT, AQTT, ART, AST, AWST, AZOST, AZOT, AZT, America/Punta_Arenas, Asia/Atyrau, Asia/Barnaul, Asia/Famagusta, Asia/Tomsk, BDT, BNT, BOT, BRST, BRT, BST, BTT, CAT, CCT, CDT, CEST, CET, CHADT, CHAST, CHOT, CHUT, CKT, CLST, CLT, COT, CST, CVT, CXT, ChST, DAVT, DDUT, EASST, EAST, EAT, ECT, EDT, EEST, EET, EGST, EGT, EST, Etc/GMT+1, Etc/GMT+10, Etc/GMT+11, Etc/GMT+12, Etc/GMT+2, Etc/GMT+3, Etc/GMT+4, Etc/GMT+5, Etc/GMT+6, Etc/GMT+7, Etc/GMT+8, Etc/GMT+9, Etc/GMT-1, Etc/GMT-10, Etc/GMT-11, Etc/GMT-12, Etc/GMT-13, Etc/GMT-14, Etc/GMT-2, Etc/GMT-3, Etc/GMT-4, Etc/GMT-5, Etc/GMT-6, Etc/GMT-7, Etc/GMT-8, Etc/GMT-9, Europe/Astrakhan, Europe/Kirov, Europe/Saratov, Europe/Ulyanovsk, FJST, FJT, FKT, FNT, GALT, GAMT, GET, GFT, GILT, GMT, GST, GYT, HADT, HAST, HDT, HKT, HOVT, HST, ICT, IDT, IOT, IRDT, IRKT, IRST, IST, JST, KGT, KOST, KRAT, KST, LHDT, LHST, LINT, MAGT, MART, MAWT, MDT, MEST, MET, MHT, MIST, MMT, MSK, MST, MUT, MVT, MYT, NCT, NDT, NFT, NOVT, NPT, NRT, NST, NUT, NZDT, NZST, OMST, PDT, PET, PETT, PGT, PHOT, PHT, PKT, PMDT, PMST, PONT, PST, PWT, PYST, PYT, RET, ROTT, SAKT, SAMT, SAST, SBT, SCT, SGT, SRET, SRT, SST, SYOT, TAHT, TFT, TJT, TKT, TLT, TMT, TOT, TVT, ULAT, UTC, UYT, UZT, VET, VLAT, VOST, VUT, WAKT, WEST, WET, WFT, WGST, WGT, WIB, WIT, WITA, WSDT, WSST, XJT, YAKT, YEKT]
Edit:
You may modify the code snippet to produce the time zones that each abbreviation may be parsed to too.
I would certainly expect that the formatter can parse the same time zone abbreviations that it can produce by formatting.
Supplement: Code for getting each abbreviation with the time zones that it may parse into:
Set<String> zids = ZoneId.getAvailableZoneIds();
Map <String, List<String>> supportedAbbreviations = new TreeMap<>();
supportedAbbreviations.putAll(zids.stream()
.collect(Collectors.groupingBy(zid -> northernSummer.atZone(ZoneId.of(zid))
.format(zoneAbbreviationFormatter))));
supportedAbbreviations.putAll(zids.stream()
.collect(Collectors.groupingBy(zid -> southernSummer.atZone(ZoneId.of(zid))
.format(zoneAbbreviationFormatter))));
System.out.println("" + supportedAbbreviations.size() + " abbreviations");
supportedAbbreviations.forEach((a, zs) -> System.out.format("%-5s %s%n", a, zs));
Excerpt from the output (still on my Java 9):
205 abbreviations
ACDT [Australia/Yancowinna, Australia/Adelaide, Australia/Broken_Hill, Australia/South]
ACST [Australia/North, Australia/Darwin]
ACT [America/Eirunepe, America/Porto_Acre, Brazil/Acre, America/Rio_Branco]
ACWST [Australia/Eucla]
ADT [Canada/Atlantic, America/Grand_Turk, America/Moncton, Atlantic/Bermuda, America/Halifax, America/Glace_Bay, America/Thule, America/Goose_Bay, SystemV/AST4ADT]
AEDT [Australia/Hobart, Australia/Tasmania, Australia/ACT, Australia/Victoria, Australia/Canberra, Australia/Currie, Australia/NSW, Australia/Sydney, Australia/Melbourne]
AEST [Australia/Queensland, Australia/Brisbane, Australia/Lindeman]
…
WSDT [Pacific/Apia]
WSST [Pacific/Apia]
XJT [Asia/Kashgar, Asia/Urumqi]
YAKT [Asia/Chita, Asia/Yakutsk, Asia/Khandyga]
YEKT [Asia/Yekaterinburg]
By reading the source code of SimpleDateFormat class, the main method for parsing the zone is the method subParseZoneString, as below (source code below comes from jdk1.8.0_151, omitted some part)
/**
* find time zone 'text' matched zoneStrings and set to internal
* calendar.
*/
private int subParseZoneString(String text, int start, CalendarBuilder calb) {
boolean useSameName = false; // true if standard and daylight time use the same abbreviation.
TimeZone currentTimeZone = getTimeZone();
// At this point, check for named time zones by looking through
// the locale data from the TimeZoneNames strings.
// Want to be able to parse both short and long forms.
int zoneIndex = formatData.getZoneIndex(currentTimeZone.getID());
TimeZone tz = null;
String[][] zoneStrings = formatData.getZoneStringsWrapper();
String[] zoneNames = null;
int nameIndex = 0;
if (zoneIndex != -1) {
zoneNames = zoneStrings[zoneIndex];
if ((nameIndex = matchZoneString(text, start, zoneNames)) > 0) {
if (nameIndex <= 2) {
// Check if the standard name (abbr) and the daylight name are the same.
useSameName = zoneNames[nameIndex].equalsIgnoreCase(zoneNames[nameIndex + 2]);
}
tz = TimeZone.getTimeZone(zoneNames[0]);
}
}
...
// If fail, try zoneIndex by TimeZone.getDefault().getId(), omitted
...
// If fail, try all elements in zoneStrings, omitted
...
// Handle Daylight Saving, omitted
...
}
From the code, we see that zoneStrings will be the main source for matching against the input text. Each element zoneString in zoneStrings is a string array representing one zone information .For the source, the matching priority is:
The zoneString of the SimpleDateFormat time zone.
The zoneString of the default time zone (TimeZone.getDefault()).
The zoneString in the array order.
By tracing the method call, in DateFormatSymbols class,
/**
* Wrapper method to the getZoneStrings(), which is called from inside
* the java.text package and not to mutate the returned arrays, so that
* it does not need to create a defensive copy.
*/
final String[][] getZoneStringsWrapper() {
if (isSubclassObject()) {
return getZoneStrings();
} else {
return getZoneStringsImpl(false);
}
}
private String[][] getZoneStringsImpl(boolean needsCopy) {
if (zoneStrings == null) {
zoneStrings = TimeZoneNameUtility.getZoneStrings(locale);
}
...
// do array copying, omitted
...
}
The zoneStrings comes from TimeZoneNameUtility class method, which is internal APIs. After some debugging, it is found that the element of zoneStrings is a String array with length 7.
Index 0: Zone Id(TimeZone.getID())
Index 1: Zone Long Name
Index 2: Zone Abbreviation
Index 3: Zone Long Name(For Daylight Saving)
Index 4: Zone Abbreviation(For Daylight Saving)
Index 5,6: Don't know
From the matchZoneString method of SimpleDateFormat class,
private int matchZoneString(String text, int start, String[] zoneNames) {
for (int i = 1; i <= 4; ++i) {
// Checking long and short zones [1 & 2],
// and long and short daylight [3 & 4].
String zoneName = zoneNames[i];
if (text.regionMatches(true, start,
zoneName, 0, zoneName.length())) {
return i;
}
}
return -1;
}
The text input will only match element of zoneStrings from index 1 to 4. Zone ID is not involved in this case.
TL;DR
So now we can explain why
TimeZone supports "CTT" but SimpleDateFormat does not
SimpleDateFormat supports "CEST" but TimeZone does not
"CTT" is a Zone ID but not an abbreviation.
"CEST" is an abbreviation but not a Zone ID.
How/where can I get a full list of timezone abbreviations that
SimpleDateFormat.parse() supports AND their meaning (i.e. just knowing
that CST is supported is not enough, I need to know that this is
interpreted as Central Standard Time, not Cuba Standard Time).
We can try to retrieve all zone abbreviations that can be parsed by SimpleDateFormat and their corresponding Zone ID and Long Name, using the below program by following the matching priority of SimpleDateFormat.
import java.text.DateFormatSymbols;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.TimeZone;
public class GetParsableZone {
public static void main(String[] args) throws ParseException {
Locale locale = Locale.getDefault(Locale.Category.FORMAT);
System.out.println("Default Local for getZoneStrings " + locale.toString());
DateFormatSymbols dateFormatSymbols = new DateFormatSymbols(locale);
String[][] zoneStrings = dateFormatSymbols.getZoneStrings();
Set<String> possibleZoneAbbrs = new HashSet<String>();
for (String[] zoneString : zoneStrings) {
possibleZoneAbbrs.add(zoneString[2]);
possibleZoneAbbrs.add(zoneString[4]);
}
System.out.println("Try to parse all possibleZoneAbbrs");
SimpleDateFormat dateFormatWithZone = new SimpleDateFormat("dd/MM/yyyy zzz");
String template = "12/12/2012 %s";
for (String possibleZoneAbbr : possibleZoneAbbrs) {
dateFormatWithZone.parse(String.format(template, possibleZoneAbbr));
}
System.out.println("All possibleZoneAbbrs can be parsed!");
List<String> orderedPossibleZoneAbbrs = new ArrayList<String>(possibleZoneAbbrs);
Collections.sort(orderedPossibleZoneAbbrs);
String simpleDateFormatZoneId = new SimpleDateFormat().getTimeZone().getID();
String defaultZoneId = TimeZone.getDefault().getID();
Integer simpleDateFormatZoneIndex = getZoneIndex(zoneStrings, simpleDateFormatZoneId);
Integer defaultZoneIndex = getZoneIndex(zoneStrings, defaultZoneId);
System.out.println("Default SimpleDateFormat Time Zone " + new SimpleDateFormat().getTimeZone().getID());
System.out.println("Default Time Zone " + TimeZone.getDefault().getID());
System.out.println("Abbreviation\tZoneId\tLong Name");
for (String orderedPossibleZoneAbbr : orderedPossibleZoneAbbrs) {
// Do matching as SimpleDateFormat
int matchIndex = getZoneIndexMatchAbbreviation(zoneStrings, simpleDateFormatZoneIndex, defaultZoneIndex,
orderedPossibleZoneAbbr);
printIdAndFullName(zoneStrings[matchIndex], orderedPossibleZoneAbbr);
}
}
public static void printIdAndFullName(String[] zoneString, String abbreviation) {
String longName = "";
String id = zoneString[0];
if (zoneString[2].equals(abbreviation)) {
longName = zoneString[1];
} else {
longName = zoneString[3];
}
System.out.println(String.format("%s\t%s\t%s", abbreviation, id, longName));
}
public static final int getZoneIndex(String[][] zoneStrings, String ID) {
for (int index = 0; index < zoneStrings.length; index++) {
if (ID.equals(zoneStrings[index][0])) {
return index;
}
}
return -1;
}
public static boolean isAbbreviationMatchZoneString(String[] zoneString, String abbreviation) {
return zoneString[2].equals(abbreviation) || zoneString[4].equals(abbreviation);
}
public static int getZoneIndexMatchAbbreviation(String[][] zoneStrings, int simpleDateFormatZoneIndex,
int defaultZoneIndex, String abbreviation) {
String[] simpleDateFormatZoneString = zoneStrings[simpleDateFormatZoneIndex];
if (isAbbreviationMatchZoneString(simpleDateFormatZoneString, abbreviation)) {
return simpleDateFormatZoneIndex;
}
String[] defaultZoneString = zoneStrings[defaultZoneIndex];
if (isAbbreviationMatchZoneString(defaultZoneString, abbreviation)) {
return defaultZoneIndex;
}
for (int i = 0; i < zoneStrings.length; i++) {
if (isAbbreviationMatchZoneString(zoneStrings[i], abbreviation)) {
return i;
}
}
return -1;
}
}
In my environment, the SimpleDateFormat zone and TimeZone.getDefault() are both
Asia/Shanghai, so if the input is CST, the zone interpreted will be China Standard Time instead of Central Standard Time or Cuba Standard Time
It can be done through zoneId.getDisplayName(SHORT, ENGLISH)

Timezone changed Automatically when create new Date object in Java

I've timestamp based on IST ( Indian Standard Time / GMT + 5:30 Hrs), My application shows last activity time based on device timestamp.
that is current time - activity timestamp. Its fine when the device timezone is IST.
For example,
Activity timestamp - 11/Feb/2016 09:00:00 AM
Current timestamp (IST) - 11/Feb/2016 10:00:00 AM
My Calculation = Current timestamp - Activity timestamp
So Application shows 1 hr ago
But the device timezone changed to some other like PHT for the same time (Philippine Time / GMT + 8 Hr)
Activity timestamp - 11/Feb/2016 09:00:00 AM
Current timestamp(PHT) - 11/Feb/2016 12:30:00 AM (plus 2:30Hrs compare with IST)
My Calculation = Current timestamp - Activity timestamp
So Application shows 3 hrs 30 mis ago
My problem is, How to get the IST time always using java? whatever the timezone, I need IST time.
I tried below code, When i change the value to IST but the timezone automatically changed to device timezone.
Please refer below URL for source code http://goo.gl/dnvQF5
SimpleDateFormat sd = new SimpleDateFormat(
"yyyy.MM.dd G 'at' HH:mm:ss z");
Date date = new Date();
// TODO: Avoid using the abbreviations when fetching time zones.
// Use the full Olson zone ID instead.
sd.setTimeZone(TimeZone.getTimeZone("GMT"));
String gmtDate = sd.format(date);
System.out.println("GMT --> " + gmtDate);
String istDate = gmtDate.replace("GMT", "IST");
System.out.println("After Replace -> " + istDate);
sd.setTimeZone(TimeZone.getTimeZone("IST"));
try {
Date istConvertedDate = sd.parse(gmtDate);
System.out.println("After Convert --> " + istConvertedDate);
} catch (ParseException ex) {
ex.printStackTrace();
}
I got output like
GMT --> 2016.02.11 AD at 05:20:07 GMT
After Replace -> 2016.02.11 AD at 05:20:07 IST
After Convert --> Thu Feb 11 00:20:07 EST 2016
Please help me to resolve this.
The class java.util.Date is only a thin wrapper around elapsed milliseconds since UNIX epoch (1970-01-01T00:00:00Z). Objects of this type don't carry any format or timezone information. Therefore every such object has completely lost the timezone information after parsing a text with timezone identifier or name using SimpleDateFormat.
What you observe and are confused about is the fact that the method toString() of this class uses a specific representation based on the system timezone.
Another thing: If you apply a string manipulation replacing "GMT" by "IST" (an ambivalent timezone name - Israel? India? Ireland?) then you effectively change the moment/instant while keeping the local time representation. Do you really want this?
If you want to preserve the timezone information which was originally parsed then you could use ZonedDateTime in the library Threeten-ABP or DateTime in the library Joda-Time-Android or ZonalDateTime in my library Time4A.
Try
public static void main(String[] args) {
SimpleDateFormat sd = new SimpleDateFormat(
"yyyy.MM.dd G 'at' HH:mm:ss z");
Date date = new Date();
// TODO: Avoid using the abbreviations when fetching time zones.
// Use the full Olson zone ID instead.
sd.setTimeZone(TimeZone.getTimeZone("GMT"));
System.out.println(sd.format(date));
String gmtDate = sd.format(date);
System.out.println(gmtDate);
//Here you create a new date object
Date istDate= new Date();
sd.setTimeZone(TimeZone.getTimeZone("IST"));
String istDate=sd.format(istDate);
System.out.println(istDate)
}
This way the first printed time will be GMT and second will be ISD.
You can use java.util.Calendar
Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("IST"));
this calendar contains current time in IST timeZone.
You can set timeStamp:
calendar.setTimeInMillis(timeStamp);
And get java.util.Date or time stamp
Date date = calendar.getTime();
Long timeStamp = calendar.getTimeInMillis();
Replace
sd.setTimeZone(TimeZone.getTimeZone("IST"));
to
TimeZone.setDefault(TimeZone.getTimeZone("IST"));

TimeZone with Calendar confusing results

I have been working with timezone conversions lately and am quite astonished by the result i get.Basically, i want to convert a date from one timezone into another. below is the code, conversions working fine, but what i have observed while debugging is, the date is not converted unless i call Calendar#get(Calendar.FIELD).
private static void convertTimeZone(String date, String time, TimeZone fromTimezone, TimeZone toTimeZone){
Calendar cal = Calendar.getInstance(fromTimezone);
String[] dateSplit = null;
String[] timeSplit = null;
if(time !=null){
timeSplit = time.split(":");
}
if(date!=null){
dateSplit = date.split("/");
}
if(dateSplit !=null){
cal.set(Calendar.DATE, Integer.parseInt(dateSplit[0]));
cal.set(Calendar.MONTH, Integer.parseInt(dateSplit[1])-1);
cal.set(Calendar.YEAR, Integer.parseInt(dateSplit[2]));
}
if(timeSplit !=null){
cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(timeSplit[0]));
cal.set(Calendar.MINUTE, Integer.parseInt(timeSplit[1]));
}
// System.out.println("Time in " + fromTimezone.getDisplayName() + " : " + cal.get(Calendar.DATE) +"/"+ (cal.get(Calendar.MONTH)+1)+"/"+ cal.get(Calendar.YEAR) +" " + ((cal.get(Calendar.HOUR_OF_DAY)<10) ? ("0"+cal.get(Calendar.HOUR_OF_DAY) ): (cal.get(Calendar.HOUR_OF_DAY)))
// +":" + (cal.get(Calendar.MINUTE)<10 ? "0"+cal.get(Calendar.MINUTE) : cal.get(Calendar.MINUTE)) );
cal.setTimeZone(toTimeZone);
System.out.println("Time in " + toTimeZone.getDisplayName() + " : " + cal.get(Calendar.DATE) +"/"+ (cal.get(Calendar.MONTH)+1)+"/"+ cal.get(Calendar.YEAR) +" " + ((cal.get(Calendar.HOUR_OF_DAY)<10) ? ("0"+cal.get(Calendar.HOUR_OF_DAY) ): (cal.get(Calendar.HOUR_OF_DAY)))
+":" + (cal.get(Calendar.MINUTE)<10 ? "0"+cal.get(Calendar.MINUTE) : cal.get(Calendar.MINUTE)) );
}
public static void main(String[] args) throws ParseException {
convertTimeZone("23/04/2013", "23:00", TimeZone.getTimeZone("EST5EDT"), TimeZone.getTimeZone("GB"));
}
Expected Output: Time in Greenwich Mean Time : 24/4/2013 04:00
Output i got when i comment sysout 1: Time in Greenwich Mean Time : 23/4/2013 23:00
If i un-comment sysout1 i get the expected valid output.
Any help is appreciated
The internal representation of the given date is not evaluated until really needed, that is until you try to access it by those getters. However the best way of parsing dates is through SimpleDateFormat.
EDIT (added for summarize the comments below and to better clarify my answer).
Calendar works this way for better efficiency: instead of recalculate everithing each time you call a setter, it waits until you call a getter.
Calendar should be used mainly for date calculations (see add() and roll()), but you are using it for parsing and formatting: these tasks are better accomplished with SimpleDateFormat, that's why I say that your usage of Calendar is not elegant.
See this example:
private static void convertTimeZone(String date, String time,
TimeZone fromTimezone, TimeZone toTimeZone) throws ParseException {
SimpleDateFormat df = new SimpleDateFormat("dd/MM/yyyy HH:mm");
df.setTimeZone(fromTimezone);
Date d = df.parse(date + " " + time);
df.setTimeZone(toTimeZone);
System.out.println("Time in " + toTimeZone.getDisplayName() + " : " +
df.format(d));
}
I have reimplemented your method using SimpleDateFormat only. My method is smaller, there is no splitting logic (it's hidden in parse()), and also the output is handled in a simpler way. Furthermore the date format is expressed in a compact and standard way that can be easily internationalized using a ResourceBundle.
Also note that the timezone conversion is just a formatting task: the internal representation of the parsed date does not change.
The answer is partly explained in a commented section in setTimeZone():
Consider the sequence of calls: cal.setTimeZone(EST); cal.set(HOUR, 1); cal.setTimeZone(PST).
Is cal set to 1 o'clock EST or 1 o'clock PST? Answer: PST. More
generally, a call to setTimeZone() affects calls to set() BEFORE AND
AFTER it up to the next call to complete().
In other words, the sequence
Set Calendar time
Change TimeZone
is interpreted to mean "Use this time in this new time zone", while the sequence
Set Calendar time
Get the time (or some part of it)
Change Time Zone
will be interpreted as "Use this time in the old time zone, then change it".
So, in your case, to get the behavior you are hoping for, you will need to call get(), or any other method that internally calls complete() inside the Calendar, before you change the time zone.
Are you restricted to using TimeZone and Calendar? If not I suggest using the excellent Library JodaTime which makes handling time zones much easier.
Your example would then look like this:
public static void convertTimeZoneJoda(String date, String time, DateTimeZone fromTimezone, DateTimeZone toTimeZone) {
DateTimeFormatter dtf = DateTimeFormat.forPattern("dd/MM/yyyyHH:mm").withZone(fromTimezone);
DateTime dt = dtf.parseDateTime(date+time).withZone(toTimeZone);
System.out.println("Time in " + toTimeZone.getID() + " : " + dt.toString());
}
public static void main(String[] args) {
convertTimeZoneJoda("23/04/2013", "23:00", DateTimeZone.forID("EST5EDT"), DateTimeZone.forID("GMT"));
}
JodaTime also allows for convenient conversation between TimeZone and DateTimeZone.
To add to #Pino's answer, the reason why get() doesn't return an updated time is because setTimeZone() simply doesn't update the fields, just sets areAllFieldsSet to false, which is useless since get() doesn't check for it. If you ask me, this is very poorly coded from SUN's part. Here is the competent code from Calendar:
setTimeZone():
public void setTimeZone(TimeZone value){
zone = value;
sharedZone = false;
areAllFieldsSet = areFieldsSet = false;
}
get():
protected final int internalGet(int field){
return fields[field];
}
This code works for me to convert to UTC:
create a Calendar Object with UTC time zone
Calendar utcTime = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
set the point in time in the UTC Calendar object from the "any time zone" Calendar object
utcTime.setTimeInMillis(myCalendarObjectInSomeOtherTimeZone.getTimeInMillis());
utcTime will now contain the same point in time as myCalendarObjectInSomeOtherTimeZone converted to UTC.

problems with Java daylight savings time

I have a Java app that needs to be cognizant of the time zone. When I take a Unix epoch time and try to convert it into a timestamp to use for an Oracle SQL call, it is getting the correct timezone, but the timezone "useDaylightTime" value is not correct, i.e., it is currently returning "true", when we are NOT in DST (I am in Florida in TZ "America/New_York").
This is running on Red Hat Linux Enterprise 6, and as far as I can tell, it is correctly set up for the timezone, e.g. 'date' returns:
Wed Nov 28 12:30:12 EST 2012
I can also see, using the 'zdump' utility, that the current value for 'isdst' is 0.
My Java version is 1.6.0_31.
I have Googled this and seen the numerous issues this has caused, but many of them simply say to set the TZ manually, but my issue is not the TZ, but the fact that the default TZ has the "isDaylight" set to 'true'. I believe this is causing my query to return data that is one hour off (I can see that it is).
Here is a simple code piece I have run to try and reproduce this in the simplest way possible:
public class TZdefault {
public static void main(String[] args) throws IOException {
long startTime = System.currentTimeMillis()/1000;
Calendar start = Calendar.getInstance();
start.setTimeInMillis(startTime);
start.setTimeZone(TimeZone.getDefault());
System.out.println("Start UTC: " + start + "ms: " + start.getTimeInMillis());
System.out.println("use daylight: " + start.getTimeZone().useDaylightTime());
} // end main
} // end class
One final thing. If in my code I set the TZ to "EST", it of course does return a TZ with 'isDaylight' set to False. But that is not a good solution.
I wanted to add some more detail that I had been hoping to hide.
I have records in an Oracle 11g database that use TIMESTAMP with TIMEZONE fields. I am simply doing JDBC queries where two of the parameters are using BETWEEN a start timestamp and end timestamp.
When I query this table, I am using a prepared statement that is using a Calendar entry, the sole purpose of which was to try and manipulate the timezone. The bottom line is that I am doing a pstmt.setTimestamp() call using the 'getTimeInMillis' method for the start and end time after the "default" timezone was applied. The log output shows that in fact it is putting in the correct milliseconds, but the returned SQL results are clearly off by one hour exactly!
I am still trying to verify that there is not an issue on the data insertion side as well.
But I have a lot of debug information, and it looks like I am asking for the correct time in my JDBC query.
the timezone useDaylightTime value is not correct, i.e., it is currently returning "true", when we are NOT in DST
I think you're confusing useDaylightTime with inDaylightTime. The former tells you whether there is a transition between daylight time and standard time in the future, not which side of that transition you're on. For example, it returns false for Chinese time zones because China does not adjust for daylight savings time, but it returns true for most US time zones because most US states (except Arizona) do observe daylight savings time.
inDaylightTime
public abstract boolean inDaylightTime(Date date)
Queries if the given date is in Daylight Saving Time in this time zone.
vs
useDaylightTime
public abstract boolean useDaylightTime()
Queries if this TimeZone uses Daylight Saving Time.
If an underlying TimeZone implementation subclass supports historical and future Daylight Saving Time schedule changes, this method refers to the last known Daylight Saving Time rule that can be a future prediction and may not be the same as the current rule. Consider calling observesDaylightTime() if the current rule should also be taken into account.
If you want to disable daylight saving calculation, then you must set your timezone to EST. Else otherwise time will be calculated based on default time zone set for AMERICA/NEW_YORK
TimeZone zoneEST = TimeZone.getTimeZone("EST");
System.out.println(zoneEST.getDSTSavings()); //0 hour
System.out.println(zoneEST.getRawOffset()); //5 hour
TimeZone.setDefault(zoneEST);
System.out.println("");
TimeZone zoneNY = TimeZone.getTimeZone("America/New_York");
System.out.println(zoneNY.getDSTSavings()); // 1 hour
System.out.println(zoneNY.getRawOffset()); // 5 hour
I have found a way to ensure the daylight saving is ignored
TimeZone tz = TimeZone.getTimeZone("GMT");
TimeZone.setDefault(tz);
GregorianCalendar calendar;
calendar = new GregorianCalendar();
Set the timezone before you create your GregorianCalendar object
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
public class TimeZoneTest {
public static void main(String[] argv) throws ParseException {
SimpleDateFormat formatter = new SimpleDateFormat("dd-M-yyyy hh:mm:ss a");
String dateInString = "22-01-2015 10:15:55 AM";
Date date = formatter.parse(dateInString);
TimeZone tz = TimeZone.getDefault();
// From TimeZone Asia/Singapore
System.out.println("TimeZone : " + tz.getID() + " - " + tz.getDisplayName());
System.out.println("TimeZone : " + tz);
System.out.println("Date : " + formatter.format(date));
// To TimeZone America/New_York
SimpleDateFormat sdfAmerica = new SimpleDateFormat("dd-M-yyyy hh:mm:ss a");
TimeZone tzInAmerica = TimeZone.getTimeZone("America/New_York");
sdfAmerica.setTimeZone(tzInAmerica);
String sDateInAmerica = sdfAmerica.format(date); // Convert to String first
Date dateInAmerica = formatter.parse(sDateInAmerica);
System.out.println("\nTimeZone : " + tzInAmerica.getID() +
" - " + tzInAmerica.getDisplayName());
System.out.println("TimeZone : " + tzInAmerica);
System.out.println("Date (String) : " + sDateInAmerica);
System.out.println("Date (Object) : " + formatter.format(dateInAmerica));
}
}

Categories