ZonedDateTime java.time پیکیج سے ایک ناقابل تبدیلی (immutable) کلاس ہے جو ٹائم زون کی معلومات (ZoneId) کے ساتھ تاریخ اور وقت کو ذخیرہ کرتی ہے۔ LocalDateTime کے برعکس، ZonedDateTime ٹائم لائن پر ایک لمحے کی منفرد طور پر شناخت کرتی ہے۔ Oracle Java 17 (2024) تصریحات کے مطابق، یہ کلاس IANA Time Zone Database کے زون قواعد کے ذریعے ڈے لائٹ سیونگ ٹائم (DST) کی منتقلی کو درست طریقے سے ہینڈل کرتی ہے۔
اہم نکات
ZonedDateTime java.time پیکیج کی اہم کلاسوں میں سے ایک ہے، جو مکمل ٹائم زون کی معلومات کے ساتھ تاریخ اور وقت کی نمائندگی کرتی ہے۔ یہ تین اجزاء کو یکجا کرتی ہے: LocalDateTime (تاریخ اور وقت)، ZoneId (زون شناخت کنندہ)، اور ZoneOffset (UTC سے نسبتہ فرق)۔
LocalDateTime کے برعکس، جو ٹائم زون کی پابندی کے بغیر صرف وال کلاک ٹائم (wall-clock time) ذخیرہ کرتا ہے، ZonedDateTime ایک لمحے کی منفرد طور پر شناخت کرتی ہے۔ مختلف ٹائم زون میں دو ایک جیسی LocalDateTime مثالیں وقت میں مختلف لمحات کی نمائندگی کرتی ہیں۔ دو ایک جیسی ZonedDateTime مثالیں ایک ہی لمحے کی نمائندگی کرتی ہیں۔
یہ کلاس مکمل طور پر ناقابل تبدیلی اور تھریڈ سیف (thread-safe) ہے۔ تمام ریاضیاتی کارروائیاں ایک نیا آبجیکٹ لوٹاتی ہیں۔ ZonedDateTime ChronoZonedDateTime انٹرفیس کو لاگو کرتی ہے اور جہاں بھی Java میں زونل وقت کی پروسیسنگ کی ضرورت ہو وہاں استعمال کی جا سکتی ہے۔
Oracle Java 17 تصریحات کے مطابق، ZonedDateTime IANA Time Zone Database کے کسی بھی زون کے ساتھ کام کرنے کی حمایت کرتی ہے، جس میں 600 سے زیادہ ٹائم زون شامل ہیں۔
بنیادی فرق — ZonedDateTime میں ٹائم زون شامل ہے، جبکہ LocalDateTime میں شامل نہیں ہے۔ یہ بنیادی فرق ہر کلاس کے استعمال کے دائرہ کار کا تعین کرتا ہے۔
LocalDateTime مقامی واقعات کے لیے استعمال ہوتی ہے: کنسرٹ کا وقت، کلاس کا شیڈول، تاریخ پیدائش۔ اگر ماسکو میں 15:00 بجے کوئی واقعہ ہوتا ہے، تو LocalDateTime بغیر کسی پابندی کے 15:00 ریکارڈ کرے گا۔ اگر آپ سرور کو نیویارک منتقل کرتے ہیں، تو وقت 15:00 ہی رہے گا — لیکن یہ ایک مختلف طبعی لمحہ ہوگا۔
ZonedDateTime عالمی ڈیٹا کے لیے استعمال ہوتی ہے: سرور لاگز، API میں ٹائم سٹیمپ، بین الاقوامی میٹنگیں۔ اگر ایک میٹنگ 15:00 MSK پر مقرر ہے، تو ZonedDateTime وقت اور زون دونوں کو محفوظ رکھے گی۔ نیویارک میں، یہ 8:00 EST کے طور پر درست طریقے سے ظاہر ہوگا۔ Baeldung (2024) کے مطابق، LocalDateTime اور ZonedDateTime کے درمیان انتخاب تاریخوں کے ساتھ کام کرتے وقت سب سے عام آرکیٹیکچرل فیصلہ ہے۔
عملی اصول: اگر ڈیٹا ایک علاقے کے لیے ذخیرہ کیا جاتا ہے — LocalDateTime استعمال کریں۔ اگر ڈیٹا ٹائم زون کی حدود کو عبور کرتا ہے — ZonedDateTime استعمال کریں۔ اگر آپ کو ایک مطلق لمحہ پیش کرنے کی ضرورت ہے — Instant استعمال کریں۔
java.time میں ٹائم زون ZoneId کلاس کے ذریعے پیش کیا جاتا ہے۔ ZoneId «براعظم/علاقہ» فارمیٹ میں ایک زون شناخت کنندہ ہے، مثال کے طور پر «Europe/Moscow»، «America/New_York»، «Asia/Tokyo»۔ ZoneId جامد طریقہ of(String zoneId) یا سسٹم ڈیفالٹ ٹائم زون کے ذریعے حاصل کیا جاتا ہے۔
ZoneId دو اقسام میں تقسیم ہوتا ہے: fixed offset (مقررہ فرق، مثلاً «+03:00») اور region-based (علاقائی زون، مثلاً «Europe/London»)۔ علاقائی زون میں ڈے لائٹ سیونگ ٹائم منتقلی کے قواعد اور تاریخی تبدیلیاں شامل ہیں۔ Fixed offset صرف ایک مقررہ فرق ہے۔
کسی مخصوص لمحے پر ZoneId کا موجودہ فرق حاصل کرنے کے لیے، getRules() طریقہ استعمال کریں، جو ZoneRules لوٹاتا ہے۔ ZoneRules میں کسی دیے گئے زون کے لیے تمام منتقلی اور فرق شامل ہیں۔ یہ درست DST ہینڈلنگ کے لیے کلیدی طریقہ کار ہے۔
تمام ٹائم زون tzdata فائلوں (IANA Time Zone Database) کے ذریعے JDK کے ساتھ فراہم کیے جاتے ہیں اور باقاعدگی سے اپ ڈیٹ ہوتے ہیں۔ Android پر، tzdata ورژن Google Play Services کے ذریعے سسٹم اپ ڈیٹس پر منحصر ہے۔
ZonedDateTime کو کئی طریقوں سے بنایا جا سکتا ہے۔ سب سے آسان now() ہے، جو سسٹم ٹائم زون میں موجودہ وقت لوٹاتا ہے۔ now(ZoneId) متغیر آپ کو مخصوص زون میں موجودہ وقت حاصل کرنے دیتا ہے۔
of(LocalDateTime, ZoneId) طریقہ مقامی وقت اور زون سے ZonedDateTime بناتا ہے۔ of(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond, ZoneId zone) متغیر اجزاء سے بناتا ہے۔
LocalDateTime کو atZone(ZoneId) طریقہ کے ذریعے ZonedDateTime میں تبدیل کیا جا سکتا ہے۔ Instant — Instant.atZone(ZoneId) کے ذریعے۔ Date — Date.toInstant().atZone(ZoneId) کے ذریعے۔
val moscowZone = ZoneId.of("Europe/Moscow")
val nowInMoscow = ZonedDateTime.now(moscowZone)
val fromComponents = ZonedDateTime.of(
2026, 7, 21, 15, 30, 0, 0, moscowZone
)
val fromLocal = LocalDateTime.now().atZone(moscowZone)
val fromInstant = Instant.now().atZone(moscowZone)
تبدیلی کا بنیادی طریقہ withZoneSameInstant(ZoneId) ہے۔ یہ اسی لمحے کو برقرار رکھتے ہوئے ZonedDateTime کو دوسرے ٹائم زون میں تبدیل کرتا ہے۔ مثال کے طور پر، 15:00 MSK → 8:00 EST۔ withZoneSameLocal(ZoneId) طریقہ مقامی وقت کو برقرار رکھتے ہوئے زون تبدیل کرتا ہے — یہ ایک مختلف لمحہ دیتا ہے۔
UTC سے نسبتہ فرق حاصل کرنے کے لیے، getOffset() طریقہ استعمال کریں، جو ZoneOffset لوٹاتا ہے۔ ZoneOffset ZoneId کا ایک ذیلی طبقہ ہے جو «+HH:mm» یا «-HH:mm» فارمیٹ میں ایک مقررہ فرق پیش کرتا ہے۔
Instant میں تبدیلی toInstant() طریقہ کے ذریعے کی جاتی ہے۔ Instant وقت میں ایک مطلق لمحہ ہے، جو ٹائم زون سے آزاد ہے۔ الٹی تبدیلی Instant.atZone(ZoneId) ہے۔
val moscow = ZonedDateTime.of(
2026, 7, 21, 15, 0, 0, 0,
ZoneId.of("Europe/Moscow")
)
val newYork = moscow.withZoneSameInstant(
ZoneId.of("America/New_York")
)
val utcInstant = moscow.toInstant()
val backToMoscow = utcInstant.atZone(ZoneId.of("Europe/Moscow"))
ڈے لائٹ سیونگ ٹائم کی منتقلی دو مسائل پیدا کرتی ہے: خلا (gaps) اور اوورلیپ (overlaps)۔ خلا بہار میں پیدا ہوتا ہے جب گھڑیاں آگے بڑھائی جاتی ہیں — ایک مخصوص وقت موجود نہیں ہوتا۔ اوورلیپ خزاں میں پیدا ہوتا ہے جب گھڑیاں پیچھے ہٹائی جاتی ہیں — ایک ہی وقت دو بار ہوتا ہے۔
ZonedDateTime حل (resolve) حکمت عملی کے ذریعے ان حالات کو ہینڈل کرتی ہے۔ خلا کے دوران آبجیکٹ بناتے وقت، java.time خود بخود وقت کو فرق کی مقدار میں منتقل کر دیتا ہے۔ اوورلیپ کے دوران بناتے وقت، پہلا آپشن (منتقلی سے پہلے) منتخب کیا جاتا ہے۔ اس رویے کو withZoneSameInstant کے ذریعے تبدیل کیا جا سکتا ہے۔
آپ zone.getRules().isDaylightSavings(instant) کے ذریعے جانچ سکتے ہیں کہ آیا کوئی وقت DST میں ہے۔ getOffset() طریقہ کسی مخصوص لمحے کے لیے حقیقی فرق دکھاتا ہے، اور getRules().getDaylightSavings(instant) ملی سیکنڈ میں DST ایڈجسٹمنٹ کی مقدار دکھاتا ہے۔
fun checkDST(zdt: ZonedDateTime) {
val rules = zdt.getZone().getRules()
val instant = zdt.toInstant()
if (rules.isDaylightSavings(instant)) {
val dstAmount = rules.getDaylightSavings(instant)
Log.d("ڈے لائٹ سیونگ ٹائم", "DST فرق: $dstAmount")
}
}
ZonedDateTime کو فارمیٹ کرنے کے لیے DateTimeFormatter استعمال کریں۔ معیاری ISO فارمیٹ میں تاریخ، وقت اور فرق شامل ہے: «2026-07-21T15:30:00+03:00[Europe/Moscow]»۔ پہلے سے طے شدہ فارمیٹس: ISO_ZONED_DATE_TIME, ISO_OFFSET_DATE_TIME, ISO_INSTANT۔
مقامی آؤٹ پٹ کے لیے، DateTimeFormatter.ofLocalizedDateTime(FormatStyle) استعمال کریں۔ FormatStyle SHORT, MEDIUM, LONG, FULL ہو سکتا ہے۔ LONG میں زون کا نام («MSK») شامل ہے، FULL میں پورا نام («Moscow Standard Time») شامل ہے۔
اہم: ZonedDateTime کے ساتھ ایک سٹرنگ پارس کرتے وقت، فارمیٹ میں زون یا فرق کی معلومات شامل ہونی چاہیے۔ اگر زون مخصوص نہیں ہے، تو LocalDateTime.parse() اور پھر atZone() استعمال کریں۔
val zdt = ZonedDateTime.now(ZoneId.of("Europe/Moscow"))
val iso = zdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME)
val custom = DateTimeFormatter
.ofPattern("dd.MM.yyyy HH:mm z")
val formatted = zdt.format(custom)
val parsed = ZonedDateTime.parse(
"2026-07-21T15:30:00+03:00",
DateTimeFormatter.ISO_OFFSET_DATE_TIME
)
پہلی مثال — صارف کو اس کے مقامی ٹائم زون میں میٹنگ کا وقت دکھانا۔ سرور UTC میں ZonedDateTime بھیجتا ہے، کلائنٹ ڈیوائس کے مقامی ٹائم زون میں تبدیل کرتا ہے۔
fun displayMeetingTime(
serverUtc: ZonedDateTime
): String {
val deviceZone = ZoneId.systemDefault()
val localTime = serverUtc.withZoneSameInstant(deviceZone)
val formatter = DateTimeFormatter
.ofPattern("dd.MM.yyyy HH:mm z")
return localTime.format(formatter)
}
دوسری مثال — ٹائم زون کو مدنظر رکھتے ہوئے اگلے ایونٹ تک کا وقت حساب کرنا۔ ہم سرور ٹائم کے لیے ZonedDateTime اور فرق حساب کرنے کے لیے Duration.between() استعمال کرتے ہیں۔
fun timeUntilEvent(eventTime: ZonedDateTime): String {
val now = ZonedDateTime.now()
val duration = Duration.between(now, eventTime)
val hours = duration.toHours()
val minutes = duration.toMinutes() % 60
return "Remaining $hours h $minutes min"
}
تیسری مثال — Retrofit API کے ساتھ کام کرنا۔ سرور زون کے ساتھ ISO-8601 سٹرنگ لوٹاتا ہے۔ ہم ZonedDateTime میں تبدیل کرنے کے لیے ایک کسٹم ڈی سیریلائزر استعمال کرتے ہیں۔
data class EventResponse(
@JsonAdapter(ZonedDateTimeAdapter::class)
val eventTime: ZonedDateTime
)
class ZonedDateTimeAdapter : JsonAdapter<ZonedDateTime>() {
override fun fromJson(reader: JsonReader): ZonedDateTime? {
return ZonedDateTime.parse(
reader.nextString()
)
}
}
پہلی غلطی — سرور کوڈ میں ZoneId.systemDefault() استعمال کرنا۔ سرور کا ٹائم زون کلائنٹ سے مختلف ہو سکتا ہے، اور سرور پر سسٹم زون استعمال کرنے سے غلط حساب کتاب ہوتا ہے۔ ہمیشہ زون واضح طور پر بتائیں یا UTC کو بطور حوالہ استعمال کریں۔
دوسری غلطی — مدت حساب کرتے وقت DST کو نظر انداز کرنا۔ Duration.between() منتقلی کو درست طریقے سے ہینڈل کرتا ہے، لیکن اگر آپ دستی طور پر ٹائم سٹیمپ منہا کرتے ہیں، تو ڈے لائٹ سیونگ ٹائم 1 گھنٹے کی غلطی کا سبب بن سکتا ہے۔ دستی حساب کے بجائے ChronoUnit.HOURS.between() استعمال کریں۔
تیسری غلطی — withZoneSameInstant اور withZoneSameLocal میں الجھن۔ پہلا لمحے کو برقرار رکھتے ہوئے زون تبدیل کرتا ہے — وقت منتقل ہوتا ہے۔ دوسرا مقامی وقت کو برقرار رکھتے ہوئے زون تبدیل کرتا ہے — لمحہ بدل جاتا ہے۔ SonarSource (2024) کے مطابق، غلط طریقہ کا انتخاب سب سے عام غلطیوں میں سے ایک ہے۔
چوتھی غلطی — یہ فرض کرنا کہ ڈیوائس کا ٹائم زون ہمیشہ صارف کے ٹائم زون جیسا ہوتا ہے۔ صارف سفر کر رہا ہو سکتا ہے اور توقع کرتا ہے کہ ایپ موجودہ زون کے بجائے اس کے «گھر» کے ٹائم زون میں وقت دکھائے۔ اس صورت میں، انٹرفیس کے ذریعے زون کا انتخاب فراہم کریں۔
اکثر پوچھے گئے سوالات
ZonedDateTime میں ایک علاقائی زون شناخت کنندہ (مثلاً، «Europe/Moscow») شامل ہے اور DST کو ہینڈل کرتا ہے۔ OffsetDateTime علاقائی قواعد کے بغیر صرف ایک مقررہ فرق (+03:00) ذخیرہ کرتا ہے۔ ڈیٹا بیس ذخیرہ کرنے کے لیے، OffsetDateTime کی سفارش کی جاتی ہے۔
ZonedDateTime.now(ZoneOffset.UTC) یا Instant.now().atZone(ZoneOffset.UTC) استعمال کریں۔ دونوں آپشن صفر فرق کے ساتھ موجودہ لمحہ لوٹاتے ہیں۔ ایک سادہ ٹائم سٹیمپ کے لیے، زون کی پابندی کے بغیر Instant.now() استعمال کریں۔
ہاں، لیکن ایک کسٹم اڈاپٹر کی ضرورت ہے۔ Gson ڈیفالٹ طور پر ZonedDateTime کو سپورٹ نہیں کرتا۔ Moshi Rfc3339DateJsonAdapter کے ذریعے اسے سپورٹ کرتا ہے۔ Jackson کے لیے Kotlinx Serialization یا JavaTimeModule لائبریری استعمال کرنے کی سفارش کی جاتی ہے۔
java.time خود بخود وقت کو فرق کی مقدار میں آگے منتقل کر دیتا ہے۔ مثال کے طور پر، اگر 02:30 موجود نہیں ہے جب گھڑیاں 03:00 پر آگے بڑھائی جاتی ہیں، تو ZonedDateTime 03:30 پر ایک آبجیکٹ بنائے گی۔ آپ ZoneRules.getTransition(instant) کے ذریعے خلا کی جانچ کر سکتے ہیں۔
JDBC 4.2 OffsetDateTime کو سپورٹ کرتا ہے لیکن ZonedDateTime کو براہ راست سپورٹ نہیں کرتا۔ ZonedDateTime میں ایک علاقائی زون شامل ہے جس کا SQL میں کوئی متبادل نہیں ہے۔ OffsetDateTime یا Instant ذخیرہ کرنے اور زون کو علیحدہ کالم میں ذخیرہ کرنے کی سفارش کی جاتی ہے۔
خلاصہ
ہم ایک موبائل ایپلیکیشن ٹرنکی تیار کریں گے
IT Sectr 2017 سے اسٹارٹ اپس اور کاروبار کے لیے iOS اور Android ایپلیکیشنز بناتا ہے۔ ہم آپ کو مشورہ دیں گے اور بہترین حل تجویز کریں گے۔
مزید پڑھیں