Instant: چیست، برچسب زمانی و کاربرد در توسعه

نویسنده: IT Sectr منتشر شده: 2026-07-13 زمان مطالعه: 9 دقیقه

Instant — کلاس تغییرناپذیر از بسته java.time که یک نقطه روی خط زمانی در UTC با دقت نانوثانیه را نشان می‌دهد. برخلاف LocalDateTime، Instant شامل تاریخ و زمان در قالب قابل خواندن برای انسان نیست — این یک نمایش ماشینی از لحظه است. طبق مشخصات Oracle Java 17 (2024)، Instant برای تبادل ماشینی برچسب‌های زمانی طراحی شده است و مشابه System.currentTimeMillis() است، اما با دقت نانوثانیه.

نکات اصلی

  • Instant — لحظه زمانی در UTC با دقت نانوثانیه، تغییرناپذیر و ایمن در برابر نخ.
  • زمان را به عنوان تعداد ثانیه از مبدأ (1970-01-01T00:00:00Z) به اضافه نانوثانیه ذخیره می‌کند.
  • Instant.now() لحظه فعلی را در UTC بدون توجه به منطقه زمانی دستگاه برمی‌گرداند.
  • برای تبدیل به قالب قابل خواندن برای انسان از atZone(ZoneId) استفاده می‌شود که ZonedDateTime را برمی‌گرداند.
  • Instant — نوع ترجیحی برای ارسال برچسب‌های زمانی در API و سیستم‌های توزیع‌شده.

Instant چیست؟

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 تغییرناپذیر است — همه متدها یک شیء جدید برمی‌گردانند.

Instant در مقابل Date در مقابل Long

قبل از 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

لحظه جاری از طریق 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 برمی‌گرداند.

kotlin
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 به فرمت‌های دیگر

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) با پشتیبانی نانوثانیه استفاده می‌شود.

kotlin
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 — کاملاً مستقل از مناطق زمانی است. Instant.now() در هر دستگاهی در هر نقطه از جهان یک نتیجه را برمی‌گرداند. این با تثبیت زمان در UTC به دست می‌آید.

منطقه زمانی فقط برای نمایش Instant به انسان لازم است. برای این کار از atZone(ZoneId) استفاده می‌شود. ZoneId.systemDefault() منطقه زمانی دستگاه تنظیم‌شده در سیستم عامل را برمی‌گرداند. ZoneOffset.UTC — ثابت برای UTC.

در سیستم‌های توزیع‌شده توصیه می‌شود تمام برچسب‌های زمانی را در Instant (یا OffsetDateTime با ZoneOffset.UTC) ذخیره و منتقل کنید. تبدیل به زمان محلی فقط در سمت کلینت قبل از نمایش به کاربر انجام می‌شود. این از سردرگمی با مناطق زمانی جلوگیری می‌کند.

همگام‌سازی زمان از طریق Instant

در برنامه‌های توزیع‌شده Android همگام‌سازی زمان برای عملکرد صحیح کش کردن، اعلان‌ها و ویرایش مشترک حیاتی است. Instant — انتخاب طبیعی برای این کار به دلیل اتصال به UTC.

هنگام مقایسه برچسب‌های زمانی از دستگاه‌های مختلف باید در نظر داشت که ساعت‌های سیستم ممکن است اختلاف داشته باشند. توصیه می‌شود از زمان سرور به عنوان مرجع استفاده کنید. سرور Instant را در UTC برمی‌گرداند، کلاینت فقط برای محاسبات نسبی با Instant محلی مقایسه می‌کند.

برای محاسبه تفاوت بین دو لحظه از Duration.between(Instant start, Instant end) استفاده می‌شود. این متد Duration را برمی‌گرداند — مدتی که می‌توان به ساعت، دقیقه، ثانیه تبدیل کرد. متدهای isAfter() و isBefore() امکان مقایسه لحظه‌ها را فراهم می‌کنند.

kotlin
fun isCacheExpired(
    cachedAt: Instant,
    ttlMinutes: Long
): Boolean {
    val elapsed = Duration.between(cachedAt, Instant.now())
    return elapsed.toMinutes() >= ttlMinutes
}

مثال‌های عملی در Android

مثال اول — ثبت رویدادها با برچسب زمانی. Instant در پایگاه داده Room ذخیره و به سرور ارسال می‌شود. برچسب زمانی برای تفسیر یکتا در UTC ثبت می‌شود.

kotlin
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 برای نمایش «۵ دقیقه پیش»، «۲ ساعت پیش» استفاده می‌کنیم — فرمتی رایج در پیام‌رسان‌ها و شبکه‌های اجتماعی.

kotlin
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 برای ردیابی زمان آخرین به‌روزرسانی استفاده می‌کنیم.

kotlin
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 سرور به عنوان منبع حقیقت استفاده کنید.

سؤالات متداول

Instant چه تفاوتی با System.currentTimeMillis() دارد؟

System.currentTimeMillis() یک long برمی‌گرداند — تعداد میلی‌ثانیه از مبدأ بدون اتصال به منطقه زمانی. Instant همان عملکرد را ارائه می‌دهد، اما با دقت نانوثانیه و API غنی برای تبدیل، مقایسه و سازگاری با java.time.

چگونه Instant را در Room Database ذخیره کنیم؟

Room مستقیماً از Instant پشتیبانی نمی‌کند. از TypeConverter استفاده کنید که Instant را به Long (toEpochMilli) و بالعکس (Instant.ofEpochMilli) تبدیل می‌کند. برای دقت نانوثانیه دو فیلد ذخیره کنید: ثانیه‌های مبدأ و نانوثانیه.

آیا می‌توان از Instant به عنوان کلید در HashMap استفاده کرد؟

بله، Instant تغییرناپذیر است و equals() و hashCode() را به درستی پیاده‌سازی می‌کند. دو Instant با مقدار یکسان برابر خواهند بود. این آن را به کلیدی قابل اعتماد برای HashMap و سایر مجموعه‌ها تبدیل می‌کند، برخلاف java.util.Date قابل تغییر.

چگونه تفاوت بین دو Instant را به دست آوریم؟

از Duration.between(start, end) برای به دست آوردن Duration یا ChronoUnit.SECONDS.between(start, end) برای تفاوت بر حسب ثانیه (long) استفاده کنید. Duration متدهای toMinutes()، toHours()، toDays() و toNanos() را ارائه می‌دهد.

چرا Instant.parse() به پسوند Z یا آفست نیاز دارد؟

Instant به عنوان یک نقطه مطلق روی خط زمانی طراحی شده است. بدون مشخص کردن منطقه زمانی یا UTC تجزیه امکان‌پذیر نیست، زیرا Instant حاوی اطلاعات تقویمی نیست. پسوند «Z» به معنای آفست صفر (UTC) است و برای فرمت ISO-8601 الزامی است.

خلاصه

  • Instant — کلاس تغییرناپذیر برای لحظه مطلق زمان در UTC با دقت نانوثانیه، ترجیحی برای برچسب‌های زمانی در سیستم‌های توزیع‌شده.
  • برخلاف java.util.Date، Instant تغییرناپذیر، ایمن در برابر نخ و دارای دقت نانوثانیه است. برخلاف Long — به صراحت تایپ شده و با سایر مقادیر عددی اشتباه گرفته نمی‌شود.
  • برای نمایش به کاربر Instant از طریق atZone(ZoneId) به ZonedDateTime تبدیل می‌شود، در غیر این صورت زمان در UTC نشان داده می‌شود.
  • برای ارسال در API از Instant.toString() (ISO-8601) یا برای سازگاری با فرمت‌های قدیمی از toEpochMilli() استفاده کنید.
  • هنگام کار با Room Instant را به عنوان Long از طریق TypeConverter با متد toEpochMilli() ذخیره کنید.
  • برای محاسبه زمان سپری‌شده از Duration.between() استفاده کنید که نانوثانیه‌ها را به درستی پردازش می‌کند.
  • به همگام‌سازی ساعت دستگاه‌ها تکیه نکنید — برای عملیات حساس از Instant سرور به عنوان منبع حقیقت استفاده کنید.

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید