diff -urN -X psidiff.ignore sources/iris/src/xmpp/xmpp-im/xmpp_xmlcommon.cpp work/iris/src/xmpp/xmpp-im/xmpp_xmlcommon.cpp
--- sources/iris/src/xmpp/xmpp-im/xmpp_xmlcommon.cpp	2009-02-13 01:21:21.000000000 +0500
+++ work/iris/src/xmpp/xmpp-im/xmpp_xmlcommon.cpp	2009-03-07 01:53:04.000000000 +0500
@@ -250,6 +250,23 @@
 	return "";
 }
 
+QDomElement queryTag(const QDomElement &e, const QString &element)
+{
+	bool found;
+	QDomElement q = findSubTag(e, element, &found);
+	return q;
+}
+
+QString queryNS(const QDomElement &e, const QString &element)
+{
+	bool found;
+	QDomElement q = findSubTag(e, element, &found);
+	if(found)
+		return q.attribute("xmlns");
+
+	return "";
+}
+
 /**
 	\brief Extracts the error code and description from the stanza element.
 
diff -urN -X psidiff.ignore sources/iris/src/xmpp/xmpp-im/xmpp_xmlcommon.h work/iris/src/xmpp/xmpp-im/xmpp_xmlcommon.h
--- sources/iris/src/xmpp/xmpp-im/xmpp_xmlcommon.h	2009-02-13 01:21:21.000000000 +0500
+++ work/iris/src/xmpp/xmpp-im/xmpp_xmlcommon.h	2009-03-07 01:53:04.000000000 +0500
@@ -68,7 +68,9 @@
 XDomNodeList childElementsByTagNameNS(const QDomElement &e, const QString &nsURI, const QString &localName);
 QDomElement createIQ(QDomDocument *doc, const QString &type, const QString &to, const QString &id);
 QDomElement queryTag(const QDomElement &e);
+QDomElement queryTag(const QDomElement &e, const QString &element);
 QString queryNS(const QDomElement &e);
+QString queryNS(const QDomElement &e, const QString &element);
 void getErrorFromElement(const QDomElement &e, const QString &baseNS, int *code, QString *str);
 QDomElement addCorrectNS(const QDomElement &e);
 
diff -urN -X psidiff.ignore sources/src/entitytimetask.cpp work/src/entitytimetask.cpp
--- sources/src/entitytimetask.cpp	1970-01-01 05:00:00.000000000 +0500
+++ work/src/entitytimetask.cpp	2009-03-07 01:53:04.000000000 +0500
@@ -0,0 +1,123 @@
+/*
+ * entitytimetask.cpp - Entity time fetching task
+ * Copyright (C) 2007  Maciej Niedzielski
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <QTime>
+#include "entitytimetask.h"
+#include "xmpp_xmlcommon.h"
+
+using namespace XMPP;
+
+/**
+ * \class EntityTimeTask
+ * \brief Gets entity time
+ *
+ * This task can be used to get time zone information of an entity.
+ */
+
+
+// convert [+|-]hh:mm to minutes
+static Maybe<int> stringToOffset(const QString &off)
+{
+	QTime t = QTime::fromString(off.mid(1), "hh:mm");
+
+	if (t.isValid() && off[0] == '+' || off[0] == '-') {
+		int m = t.hour() * 60 + t.minute();
+		if (off[0] == '-')
+			m = -m;
+		return m;
+	}
+	else {
+		return Maybe<int>();
+	}
+}
+
+/**
+ * \brief Create new task.
+ */
+EntityTimeTask::EntityTimeTask(Task* parent) : Task(parent)
+{
+}
+
+/**
+ * \brief Queried entity's JID.
+ */
+const Jid & EntityTimeTask::jid() const
+{
+	return jid_;
+}
+
+/**
+ * \brief Prepares the task to get information from JID.
+ */
+void EntityTimeTask::get(const Jid &jid)
+{
+	jid_ = jid;
+	iq_ = createIQ(doc(), "get", jid_.full(), id());
+	QDomElement time = doc()->createElement("time");
+	time.setAttribute("xmlns", "urn:xmpp:time");
+	iq_.appendChild(time);
+}
+
+void EntityTimeTask::onGo()
+{
+	send(iq_);
+}
+
+bool EntityTimeTask::take(const QDomElement &x)
+{
+	if (!iqVerify(x, jid_, id()))
+		return false;
+
+	if (x.attribute("type") == "result") {
+		bool found = false;
+		QDomElement q = queryTag(x, "time");
+		QDomElement tag;
+		tag = findSubTag(q, "utc", &found);
+		if (found)
+			utc_ = tagContent(tag);
+		tag = findSubTag(q, "tzo", &found);
+		if (found) {
+			tzoString_ = tagContent(tag);
+			tzo_ = stringToOffset(tzoString_);
+		}
+		setSuccess();
+	}
+	else {
+		setError(x);
+	}
+
+	return true;
+}
+
+/**
+ * \brief Timezone offset in [+|-]hh:mm format (or empty string if no data).
+ */
+const QString& EntityTimeTask::timezoneOffsetString() const
+{
+	return tzoString_;
+}
+
+/**
+ * \brief Timezone offset in minutes (if available).
+ */
+Maybe<int> EntityTimeTask::timezoneOffset() const
+{
+	return tzo_;
+}
diff -urN -X psidiff.ignore sources/src/entitytimetask.h work/src/entitytimetask.h
--- sources/src/entitytimetask.h	1970-01-01 05:00:00.000000000 +0500
+++ work/src/entitytimetask.h	2009-03-07 01:53:04.000000000 +0500
@@ -0,0 +1,49 @@
+/*
+ * entitytimetask.h - Entity time fetching task
+ * Copyright (C) 2007  Maciej Niedzielski
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef ENTITYTIMETASK_H
+#define ENTITYTIMETASK_H
+
+#include <QDomElement>
+#include "xmpp_task.h"
+#include "xmpp_jid.h"
+#include "utilities/maybe.h"
+
+class EntityTimeTask : public XMPP::Task
+{
+public:
+	EntityTimeTask(Task*);
+
+	void onGo();
+	bool take(const QDomElement &);
+	void get(const XMPP::Jid &jid);
+	const XMPP::Jid & jid() const;
+
+	const QString& timezoneOffsetString() const;
+	Maybe<int> timezoneOffset() const;
+
+private:
+	QDomElement iq_;
+	XMPP::Jid jid_;
+	QString utc_, tzoString_;
+	Maybe<int> tzo_;
+};
+
+#endif
diff -urN -X psidiff.ignore sources/src/infodlg.cpp work/src/infodlg.cpp
--- sources/src/infodlg.cpp	2009-03-07 01:53:03.000000000 +0500
+++ work/src/infodlg.cpp	2009-03-07 01:53:04.000000000 +0500
@@ -39,6 +39,7 @@
 #include "iconset.h"
 #include "common.h"
 #include "lastactivitytask.h"
+#include "entitytimetask.h"
 #include "vcardfactory.h"
 #include "iconwidget.h"
 #include "contactview.h"
@@ -123,7 +124,7 @@
 	updateStatus();
 	foreach(UserListItem* u, d->pa->findRelevant(j)) {
 		foreach(UserResource r, u->userResourceList()) {
-			requestClientVersion(d->jid.withResource(r.name()));
+			requestResourceInfo(d->jid.withResource(r.name()));
 		}
 		if (u->userResourceList().isEmpty() && u->lastAvailable().isNull()) {
 			requestLastActivity();
@@ -596,13 +597,24 @@
 	}
 }
 
-void InfoDlg::requestClientVersion(const Jid& j)
+/**
+ * \brief Requests per-resource information.
+ *
+ * Gets information about client version and time.
+ */
+void InfoDlg::requestResourceInfo(const Jid& j)
 {
 	d->infoRequested += j.full();
+
 	JT_ClientVersion *jcv = new JT_ClientVersion(d->pa->client()->rootTask());
 	connect(jcv, SIGNAL(finished()), SLOT(clientVersionFinished()));
 	jcv->get(j);
 	jcv->go(true);
+
+	EntityTimeTask *jet = new EntityTimeTask(d->pa->client()->rootTask());
+	connect(jet, SIGNAL(finished()), SLOT(entityTimeFinished()));
+	jet->get(j);
+	jet->go(true);
 }
 
 void InfoDlg::clientVersionFinished()
@@ -622,6 +634,23 @@
 	}
 }
 
+void InfoDlg::entityTimeFinished()
+{
+	EntityTimeTask *j = (EntityTimeTask *)sender();
+	if(j->success()) {
+		foreach(UserListItem* u, d->pa->findRelevant(j->jid())) {
+			UserResourceList::Iterator rit = u->userResourceList().find(j->jid().resource());
+			bool found = (rit == u->userResourceList().end()) ? false: true;
+			if(!found)
+				continue;
+
+			(*rit).setTimezone(j->timezoneOffset());
+			d->pa->contactProfile()->updateEntry(*u);
+			updateStatus();
+		}
+	}
+}
+
 void InfoDlg::requestLastActivity()
 {
 	LastActivityTask *jla = new LastActivityTask(d->jid.bare(),d->pa->client()->rootTask());
@@ -647,7 +676,7 @@
 {
 	if (d->jid.compare(j,false)) {
 		if (!d->infoRequested.contains(j.withResource(r.name()).full()))
-			requestClientVersion(j.withResource(r.name()));
+			requestResourceInfo(j.withResource(r.name()));
 	}
 }
 
diff -urN -X psidiff.ignore sources/src/infodlg.h work/src/infodlg.h
--- sources/src/infodlg.h	2009-03-07 01:53:03.000000000 +0500
+++ work/src/infodlg.h	2009-03-07 01:53:04.000000000 +0500
@@ -58,6 +58,7 @@
 	void contactUnavailable(const Jid &, const Resource &);
 	void contactUpdated(const Jid &);
 	void clientVersionFinished();
+	void entityTimeFinished();
 	void requestLastActivityFinished();
 	void jt_finished();
 	void doSubmit();
@@ -79,7 +80,7 @@
 	bool edited();
 	void setEdited(bool);
 	void setPreviewPhoto(const QString& str);
-	void requestClientVersion(const XMPP::Jid& j);
+	void requestResourceInfo(const XMPP::Jid& j);
 	void requestLastActivity();
 };
 
diff -urN -X psidiff.ignore sources/src/psiaccount.cpp work/src/psiaccount.cpp
--- sources/src/psiaccount.cpp	2009-03-07 01:53:03.000000000 +0500
+++ work/src/psiaccount.cpp	2009-03-07 01:53:04.000000000 +0500
@@ -120,6 +120,7 @@
 #include "rc.h"
 #include "tabdlg.h"
 #include "proxy.h"
+#include "timeserver.h"
 #include "psicontactlist.h"
 #include "tabmanager.h"
 #include "fileutil.h"
@@ -806,6 +807,9 @@
 	d->httpAuthManager = new HttpAuthManager(d->client->rootTask());
 	connect(d->httpAuthManager, SIGNAL(confirmationRequest(const PsiHttpAuthRequest &)), SLOT(incomingHttpAuthRequest(const PsiHttpAuthRequest &)));
 
+	// Time server
+	new TimeServer(d->client->rootTask());
+
 	// Initialize Adhoc Commands server
 	d->ahcManager = new AHCServerManager(this);
 	d->rcSetStatusServer = 0;
diff -urN -X psidiff.ignore sources/src/src.pri work/src/src.pri
--- sources/src/src.pri	2009-03-07 01:53:03.000000000 +0500
+++ work/src/src.pri	2009-03-07 01:53:04.000000000 +0500
@@ -186,6 +186,8 @@
 	$$PWD/xdata_widget.h \
 	$$PWD/statuspreset.h \
 	$$PWD/lastactivitytask.h \
+	$$PWD/entitytimetask.h \
+	$$PWD/timeserver.h \
 	$$PWD/mucmanager.h \
 	$$PWD/mucjoindlg.h \
 	$$PWD/mucconfigdlg.h \
@@ -305,6 +307,8 @@
 	$$PWD/psiactionlist.cpp \
 	$$PWD/xdata_widget.cpp \
 	$$PWD/lastactivitytask.cpp \
+	$$PWD/entitytimetask.cpp \
+	$$PWD/timeserver.cpp \
 	$$PWD/statuspreset.cpp \
 	$$PWD/mucmanager.cpp \
 	$$PWD/mucjoindlg.cpp \
diff -urN -X psidiff.ignore sources/src/systeminfo.cpp work/src/systeminfo.cpp
--- sources/src/systeminfo.cpp	2009-03-07 01:25:53.000000000 +0500
+++ work/src/systeminfo.cpp	2009-03-07 01:53:04.000000000 +0500
@@ -178,7 +178,7 @@
 		if(s.at(0) == '+')
 			s.remove(0,1);
 		s.truncate(s.length()-2);
-		timezone_offset_ = s.toInt();
+		timezone_offset_ = s.toInt() * 60;	// FIX-ME: should really read the offset in minutes
 	}
 	strcpy(fmt, "%Z");
 	strftime(str, 256, fmt, localtime(&x));
@@ -207,14 +207,12 @@
 
 #if defined(Q_WS_WIN)
 	TIME_ZONE_INFORMATION i;
-	//GetTimeZoneInformation(&i);
-	//timezone_offset_ = (-i.Bias) / 60;
 	memset(&i, 0, sizeof(i));
 	bool inDST = (GetTimeZoneInformation(&i) == TIME_ZONE_ID_DAYLIGHT);
 	int bias = i.Bias;
 	if(inDST)
 		bias += i.DaylightBias;
-	timezone_offset_ = (-bias) / 60;
+	timezone_offset_ = -bias;
 	timezone_str_ = "";
 	for(int n = 0; n < 32; ++n) {
 		int w = inDST ? i.DaylightName[n] : i.StandardName[n];
@@ -255,3 +253,13 @@
 }
 
 SystemInfo* SystemInfo::instance_ = NULL;
+
+/**
+ * \fn int SystemInfo::timezoneOffset()
+ * \brief Local timezone offset in minutes.
+ */
+
+/**
+ * \fn const QString& SystemInfo::timezoneString() const
+ * \brief Local timezone name.
+ */
diff -urN -X psidiff.ignore sources/src/timeserver.cpp work/src/timeserver.cpp
--- sources/src/timeserver.cpp	1970-01-01 05:00:00.000000000 +0500
+++ work/src/timeserver.cpp	2009-03-07 01:53:04.000000000 +0500
@@ -0,0 +1,84 @@
+/*
+ * timeserver.cpp - Entity time server
+ * Copyright (C) 2001, 2002, 2007  Justin Karneges, Maciej Niedzielski
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "timeserver.h"
+#include "systeminfo.h"
+#include "xmpp_xmlcommon.h"
+#include <QDateTime>
+
+using namespace XMPP;
+
+
+/**
+ * \class TimeServer
+ * \brief Server current time
+ *
+ * This serving task answers XEP-0202 and XEP-0090 queries
+ */
+
+TimeServer::TimeServer(Task *parent)
+:Task(parent)
+{
+}
+
+TimeServer::~TimeServer()
+{
+}
+
+bool TimeServer::take(const QDomElement &e)
+{
+	if (e.tagName() != "iq" || e.attribute("type") != "get")
+		return false;
+
+	QString ns = queryNS(e, "time");
+	QString ns_deprecated = queryNS(e);
+	if (ns == "urn:xmpp:time") {
+		QDomElement iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id"));
+		QDomElement time = doc()->createElement("time");
+		time.setAttribute("xmlns", ns);
+		iq.appendChild(time);
+
+		QDateTime local = QDateTime::currentDateTime();
+		int off = SystemInfo::instance()->timezoneOffset();
+		QTime t = QTime(0, 0).addSecs(qAbs(off)*60);
+		QString tzo = (off < 0 ? "-" : "+") + t.toString("HH:mm");
+		time.appendChild(textTag(doc(), "tzo", tzo));
+		time.appendChild(textTag(doc(), "utc", local.toUTC().toString(Qt::ISODate) + "Z"));
+
+		send(iq);
+		return true;
+	}
+	else if (ns_deprecated == "jabber:iq:time") {
+		QDomElement iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id"));
+		QDomElement query = doc()->createElement("query");
+		query.setAttribute("xmlns", "jabber:iq:time");
+		iq.appendChild(query);
+
+		QDateTime local = QDateTime::currentDateTime();
+		QString str = SystemInfo::instance()->timezoneString();
+		query.appendChild(textTag(doc(), "utc", TS2stamp(local.toUTC())));
+		query.appendChild(textTag(doc(), "tz", str));
+		query.appendChild(textTag(doc(), "display", QString("%1 %2").arg(local.toString()).arg(str)));
+
+		send(iq);
+		return true;
+	}
+	return false;
+}
diff -urN -X psidiff.ignore sources/src/timeserver.h work/src/timeserver.h
--- sources/src/timeserver.h	1970-01-01 05:00:00.000000000 +0500
+++ work/src/timeserver.h	2009-03-07 01:53:04.000000000 +0500
@@ -0,0 +1,35 @@
+/*
+ * timeserver.h - Entity time server
+ * Copyright (C) 2007  Maciej Niedzielski
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef TIMESERVER_H
+#define TIMESERVER_H
+
+#include "xmpp_task.h"
+
+class TimeServer : public XMPP::Task
+{
+	Q_OBJECT
+public:
+	TimeServer(Task *);
+	~TimeServer();
+	bool take(const QDomElement &);
+};
+
+#endif
diff -urN -X psidiff.ignore sources/src/userlist.cpp work/src/userlist.cpp
--- sources/src/userlist.cpp	2009-03-07 01:25:53.000000000 +0500
+++ work/src/userlist.cpp	2009-03-07 01:53:04.000000000 +0500
@@ -102,6 +102,43 @@
 	}
 }
 
+/**
+ * \brief Timezone offset in minutes (if available).
+ */
+Maybe<int> UserResource::timezoneOffset() const
+{
+	return v_tzo;
+}
+
+/**
+ * \brief Timezone offset as string (or empty string if no data).
+ *
+ * String is formatted as "UTC[+|-]h[:mm]".
+ */
+const QString& UserResource::timezoneOffsetString() const
+{
+	return v_tzoString;
+}
+
+/**
+ * \brief Set timezone offset (in minutes).
+ */
+void UserResource::setTimezone(Maybe<int> off)
+{
+	v_tzo = off;
+
+	if (off.hasValue()) {
+		QTime t = QTime(0, 0).addSecs(abs(off.value())*60);
+		QString u = QString("UTC") + (off.value() < 0 ? "-" : "+");
+		u += QString::number(t.hour());
+		if (t.minute())
+			u += QString(":%1").arg(t.minute());
+		v_tzoString = u;
+	}
+	else
+		v_tzoString = "";
+}
+
 const QString & UserResource::publicKeyID() const
 {
 	return v_keyID;
@@ -562,6 +599,12 @@
 			if (!r.geoLocation().isNull())
 				str += QString("<div style='white-space:pre'>") + QObject::tr("Geolocation") + ": " + QString::number(r.geoLocation().lat().value()) + "/" + QString::number(r.geoLocation().lon().value()) + "</div>";
 
+			// Entity Time
+			if (r.timezoneOffset().hasValue()) {
+				QDateTime dt = QDateTime::currentDateTime().toUTC().addSecs(r.timezoneOffset().value()*60);
+				str += QString("<br><nobr>") + QObject::tr("Time") + QString(": %1 (%2)").arg(dt.toString(Qt::TextDate)).arg(r.timezoneOffsetString()) + "</nobr>";
+			}
+
 			// client
 			if(!r.versionString().isEmpty() && PsiOptions::instance()->getOption("options.ui.contactlist.tooltip.client-version").toBool()) {
 				QString ver = r.versionString();
diff -urN -X psidiff.ignore sources/src/userlist.h work/src/userlist.h
--- sources/src/userlist.h	2009-03-07 01:25:53.000000000 +0500
+++ work/src/userlist.h	2009-03-07 01:53:04.000000000 +0500
@@ -31,6 +31,7 @@
 #include "mood.h"
 #include "geolocation.h"
 #include "physicallocation.h"
+#include "utilities/maybe.h"
 
 class AvatarFactory;
 namespace XMPP {
@@ -52,6 +53,10 @@
 	const QString& clientOS() const;
 	void setClient(const QString& name, const QString& version, const QString& os);
 
+	Maybe<int> timezoneOffset() const;
+	const QString& timezoneOffsetString() const;
+	void setTimezone(Maybe<int> tzo);
+
 	const QString & publicKeyID() const;
 	int pgpVerifyStatus() const;
 	QDateTime sigTimestamp() const;
@@ -68,6 +73,8 @@
 
 private:
 	QString v_ver, v_clientName, v_clientVersion, v_clientOS, v_keyID;
+	Maybe<int> v_tzo;
+	QString v_tzoString;
 	QString v_tune;
 	GeoLocation v_geoLocation;
 	PhysicalLocation v_physicalLocation;
