Instant — کلاس تغییرناپذیر از بسته java.time که یک نقطه روی خط زمانی در UTC با دقت نانوثانیه را نشان میدهد. برخلاف LocalDateTime، Instant شامل تاریخ و زمان در قالب قابل خواندن برای انسان نیست — این یک نمایش ماشینی از لحظه است. طبق مشخصات Oracle Java 17 (2024)، Instant برای تبادل ماشینی برچسبهای زمانی طراحی شده است و مشابه System.currentTimeMillis() است، اما با دقت نانوثانیه.
نکات اصلی
Instant — کلاسی است که یک نقطه منفرد روی خط زمانی را مدلسازی میکند. نمایش داخلی آن از دو فیلد تشکیل شده است: long seconds (تعداد ثانیه از 1970-01-01T00:00:00Z) و int nanos (نانوثانیه در ثانیه جاری، از 0 تا 999999999).
محدوده مقادیر Instant — از -31557014167219200 تا 31556889864403199 ثانیه از مبدأ، که تقریباً 292 میلیون سال را در هر دو جهت پوشش میدهد. این برای هر کار عملی از جمله محاسبات نجومی کافی است.
به گفته Baeldung (2024)، Instant پلی بین انواع قابل خواندن برای انسان (LocalDateTime، ZonedDateTime) و فرمتهای ماشینی (timestamp در میلیثانیه) است. Instant برای ثبت رویداد، کش کردن، همگامسازی و تمام کارهایی که لحظه مطلق زمان مهم است استفاده میشود.
کلاس اینترفیسهای Comparable (برای مقایسه لحظهها) و Temporal (برای استفاده در API عمومی java.time) را پیادهسازی میکند. Instant تغییرناپذیر است — همه متدها یک شیء جدید برمیگردانند.
قبل از Java 8 برای کار با لحظههای زمانی از java.util.Date و System.currentTimeMillis() استفاده میشد. هر دو رویکرد دارای معایبی هستند. Date قابل تغییر است، ایمن در برابر نخ نیست، زمان را در میلیثانیه از مبدأ ذخیره میکند، اما نام متدها قدیمی هستند (getYear() برای 2016 مقدار 116 را برمیگرداند).
Long (timestamp ساده) سریع و جمعوجور است، اما پشتیبانی داخلی از نانوثانیه ندارد، به صورت قابل خواندن نمایش داده نمیشود و در اشکالزدایی نیاز به تجزیه دستی دارد. رویکرد Long همچنین نوع داده را تشخیص نمیدهد — برنامهنویس ممکن است مقدار نادرستی را ارسال کند.
Instant همه این مشکلات را حل میکند. تغییرناپذیر است، حاوی اطلاعات صریح درباره دقت (ثانیه + نانوثانیه) است، به فرمت ISO-8601 «2026-07-21T15:00:00Z» سریالایز میشود و API غنی برای تبدیل دارد. به گفته SonarSource (2024)، Instant جایگزین توصیهشده برای Date در تمام پروژههای جدید است.
لحظه جاری از طریق Instant.now() به دست میآید. برخلاف LocalDateTime.now()، Instant.now() همیشه زمان را در UTC برمیگرداند و منطقه زمانی دستگاه را نادیده میگیرد. این آن را برای برچسبهای زمانی سرور ایدهآل میکند.
از مقادیر موجود: Instant.ofEpochSecond(long epochSecond) — از ثانیه از مبدأ، Instant.ofEpochMilli(long epochMilli) — از میلیثانیه، Instant.parse(CharSequence) — از رشته ISO-8601 («2026-07-21T15:00:00Z»).
برای خواندن از getEpochSecond() — تعداد ثانیه از مبدأ، toEpochMilli() — تعداد میلیثانیه، getNano() — نانوثانیه استفاده میشود. متد toString() رشتهای در فرمت ISO-8601 برمیگرداند.
val now = Instant.now()
val fromSeconds = Instant.ofEpochSecond(1784700000)
val fromMillis = Instant.ofEpochMilli(1784700000000)
val parsed = Instant.parse("2026-07-21T15:00:00Z")
val epochSecond = now.getEpochSecond()
val epochMilli = now.toEpochMilli()
val nanos = now.getNano()
Instant به ZonedDateTime از طریق atZone(ZoneId) تبدیل میشود. مثلاً Instant.now().atZone(ZoneId.of("Europe/Moscow")) ZonedDateTime را برای مسکو برمیگرداند. بدون منطقه زمانی تبدیل ممکن نیست — Instant حاوی اطلاعات تقویمی نیست.
به LocalDateTime Instant از طریق atZone(ZoneId).toLocalDateTime() تبدیل میشود. این روش صریح است و اطلاعات را از دست نمیدهد. تبدیل معکوس — LocalDateTime.atZone(ZoneId).toInstant().
برای سازگاری با java.util.Date: Date.from(instant) و date.toInstant(). این یک تبدیل دوطرفه است که دقت را تا میلیثانیه حفظ میکند (Date از نانوثانیه پشتیبانی نمیکند). برای کار با java.sql.Timestamp از Timestamp.from(instant) با پشتیبانی نانوثانیه استفاده میشود.
val instant = Instant.now()
val zoned = instant.atZone(ZoneId.of("Europe/Moscow"))
val localDateTime = instant
.atZone(ZoneId.systemDefault())
.toLocalDateTime()
val oldDate = Date.from(instant)
val backToInstant = oldDate.toInstant()
ویژگی کلیدی Instant — کاملاً مستقل از مناطق زمانی است. Instant.now() در هر دستگاهی در هر نقطه از جهان یک نتیجه را برمیگرداند. این با تثبیت زمان در UTC به دست میآید.
منطقه زمانی فقط برای نمایش Instant به انسان لازم است. برای این کار از atZone(ZoneId) استفاده میشود. ZoneId.systemDefault() منطقه زمانی دستگاه تنظیمشده در سیستم عامل را برمیگرداند. ZoneOffset.UTC — ثابت برای UTC.
در سیستمهای توزیعشده توصیه میشود تمام برچسبهای زمانی را در Instant (یا OffsetDateTime با ZoneOffset.UTC) ذخیره و منتقل کنید. تبدیل به زمان محلی فقط در سمت کلینت قبل از نمایش به کاربر انجام میشود. این از سردرگمی با مناطق زمانی جلوگیری میکند.
در برنامههای توزیعشده Android همگامسازی زمان برای عملکرد صحیح کش کردن، اعلانها و ویرایش مشترک حیاتی است. Instant — انتخاب طبیعی برای این کار به دلیل اتصال به UTC.
هنگام مقایسه برچسبهای زمانی از دستگاههای مختلف باید در نظر داشت که ساعتهای سیستم ممکن است اختلاف داشته باشند. توصیه میشود از زمان سرور به عنوان مرجع استفاده کنید. سرور Instant را در UTC برمیگرداند، کلاینت فقط برای محاسبات نسبی با Instant محلی مقایسه میکند.
برای محاسبه تفاوت بین دو لحظه از Duration.between(Instant start, Instant end) استفاده میشود. این متد Duration را برمیگرداند — مدتی که میتوان به ساعت، دقیقه، ثانیه تبدیل کرد. متدهای isAfter() و isBefore() امکان مقایسه لحظهها را فراهم میکنند.
fun isCacheExpired(
cachedAt: Instant,
ttlMinutes: Long
): Boolean {
val elapsed = Duration.between(cachedAt, Instant.now())
return elapsed.toMinutes() >= ttlMinutes
}
مثال اول — ثبت رویدادها با برچسب زمانی. Instant در پایگاه داده Room ذخیره و به سرور ارسال میشود. برچسب زمانی برای تفسیر یکتا در UTC ثبت میشود.
data class EventLog(
val id: Long = 0,
val eventName: String,
val timestamp: Instant
)
class Converters {
@TypeConverter
fun fromInstant(value: Instant?): Long? {
return value?.toEpochMilli()
}
@TypeConverter
fun toInstant(value: Long?): Instant? {
return value?.let { Instant.ofEpochMilli(it) }
}
}
مثال دوم — تعیین زمان سپریشده از رویداد. از Duration.between برای نمایش «۵ دقیقه پیش»، «۲ ساعت پیش» استفاده میکنیم — فرمتی رایج در پیامرسانها و شبکههای اجتماعی.
fun timeAgo(instant: Instant): String {
val duration = Duration.between(instant, Instant.now())
return when {
duration.toMinutes() < 1 -> "just now"
duration.toHours() < 1 -> "${duration.toMinutes()} min ago"
duration.toDays() < 1 -> "${duration.toHours()} h ago"
else -> "${duration.toDays()} d ago"
}
}
مثال سوم — همگامسازی دادهها بین سرور و کلاینت. از Instant برای ردیابی زمان آخرین بهروزرسانی استفاده میکنیم.
class SyncManager {
private var lastSyncAt: Instant? = null
fun sync() {
val syncStart = Instant.now()
// درخواست سرور با lastSyncAt
lastSyncAt = syncStart
}
fun shouldSync(intervalMinutes: Long): Boolean {
val last = lastSyncAt ?: return true
return Duration.between(last, Instant.now())
.toMinutes() >= intervalMinutes
}
}
اشتباه اول — استفاده از Instant.now().toString() برای نمایش به کاربر. Instant در فرمت UTC «2026-07-21T15:00:00Z» نمایش داده میشود که برای انسان قابل خواندن نیست. همیشه Instant را قبل از نمایش از طریق atZone() به منطقه زمانی محلی تبدیل کنید.
اشتباه دوم — از دست رفتن نانوثانیهها هنگام تبدیل به java.util.Date. Date فقط میلیثانیه را پشتیبانی میکند. اگر Instant نانوثانیه داشته باشد، در Date.from(instant) از بین میروند. برای تعیین صریح دقت از Instant.truncatedTo(ChronoUnit.MILLIS) استفاده کنید.
اشتباه سوم — اشتباه گرفتن toEpochMilli() و getEpochSecond(). toEpochMilli() تعداد میلیثانیه از مبدأ (long) را برمیگرداند، در حالی که getEpochSecond() تعداد ثانیه (long) را. اشتباه گرفتن این متدها میتواند خطای ۱۰۰۰ برابری ایجاد کند.
اشتباه چهارم — فرض بر همگام بودن Instant.now() در همه دستگاهها. ساعتهای سیستم میتوانند دقیقهها و حتی ساعتها تفاوت داشته باشند. برای عملیاتهای حساس به زمان (احراز هویت، پرداختها) از Instant سرور به عنوان منبع حقیقت استفاده کنید.
سؤالات متداول
System.currentTimeMillis() یک long برمیگرداند — تعداد میلیثانیه از مبدأ بدون اتصال به منطقه زمانی. Instant همان عملکرد را ارائه میدهد، اما با دقت نانوثانیه و API غنی برای تبدیل، مقایسه و سازگاری با java.time.
Room مستقیماً از Instant پشتیبانی نمیکند. از TypeConverter استفاده کنید که Instant را به Long (toEpochMilli) و بالعکس (Instant.ofEpochMilli) تبدیل میکند. برای دقت نانوثانیه دو فیلد ذخیره کنید: ثانیههای مبدأ و نانوثانیه.
بله، Instant تغییرناپذیر است و equals() و hashCode() را به درستی پیادهسازی میکند. دو Instant با مقدار یکسان برابر خواهند بود. این آن را به کلیدی قابل اعتماد برای HashMap و سایر مجموعهها تبدیل میکند، برخلاف java.util.Date قابل تغییر.
از Duration.between(start, end) برای به دست آوردن Duration یا ChronoUnit.SECONDS.between(start, end) برای تفاوت بر حسب ثانیه (long) استفاده کنید. Duration متدهای toMinutes()، toHours()، toDays() و toNanos() را ارائه میدهد.
Instant به عنوان یک نقطه مطلق روی خط زمانی طراحی شده است. بدون مشخص کردن منطقه زمانی یا UTC تجزیه امکانپذیر نیست، زیرا Instant حاوی اطلاعات تقویمی نیست. پسوند «Z» به معنای آفست صفر (UTC) است و برای فرمت ISO-8601 الزامی است.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید