مهرهای زمانی در JSON چه قالبی داشته باشند
جمعبندی: عددهای epoch و رشتههای ISO هر یک مصالحه دارند، اما مخلوط کردنشان همان چیزی است که واقعاً چیزها را میشکند. از ISO 8601 با offset استفاده کنید، UTC ذخیره کنید، در منطقه زمانی کاربر نمایش دهید.

جمعبندی: روی سیم از ISO 8601 با offset صریح استفاده کنید، UTC را با Z انتهایی ذخیره کنید و فقط هنگام نمایش تبدیل کنید؛ اگر بهجایش عدد epoch انتخاب میکنید، ثانیه در برابر میلیثانیه را سراسری تثبیت کنید و واحد را در نام فیلد بگذارید.
چرا 2026-10-30 14:30:00 کافی نیست
آن رشته هیچ منطقهای حمل نمیکند. ۱۴:۳۰ در شانگهای و ۱۴:۳۰ در لندن هشت ساعت فاصله دارند و راهی برای فهمیدن اینکه کدام منظور است نیست. new Date('2026-10-30 14:30:00') در منطقه محلی زمان اجرا تجزیه میشود، پس سرور و مرورگر دو لحظه متفاوت از یک payload استنتاج میکنند — همان نوع باگی که هرگز بازتولید نمیشود.
2026-10-30 خالی اصلاً یک لحظه نیست. برای تولد و دورههای صورتحساب خوب است، برای زمان ایجاد اشتباه.
دو قالب عملی
رشتههای ISO 8601
{
"createdAt": "2026-10-30T14:30:00+08:00",
"updatedAt": "2026-10-30T06:30:00Z"
}
خوانا، وقتی قالب یکدست باشد قابل مرتبسازی، دقت میلیثانیه در صورت نیاز، و خودتوصیف.
عددهای epoch
{ "createdAt": 1793519400 }
فشرده و بدون ابهام، اما ناخوانا، و ثانیه در برابر میلیثانیه خطری دائمی است. اگر epoch انتخاب میکنید، واحد را استاندارد کنید و در نام فیلد بگذارید: createdAtMs بسیار امنتر از createdAt است.
قراردادهایی که ارزش دارد زود تثبیت شوند
| مورد | قرارداد |
|---|---|
| قالب سیم | ISO 8601 با Z یا +08:00 |
| منطقه ذخیرهسازی | UTC، تا DST هرگز وارد ماجرا نشود |
| دقت | میلیثانیه کافی است |
| نامگذاری فیلد | createdAt، updatedAt، expiresAt |
| تاریخهای خالص | فیلد date جدا، مثلاً birthday: "1990-01-01" |
| مدتها | عدد صحیح بههمراه واحد، مثلاً timeoutMs: 3000 |
نمایش
new Intl.DateTimeFormat('en-GB', {
dateStyle: 'medium',
timeStyle: 'short',
timeZone: 'Europe/London',
}).format(new Date('2026-10-30T06:30:00Z'));
وقتی نمایش نباید از دستگاه پیروی کند timeZone را صریح بدهید. تکیه بر پیشفرض مرورگر برای زمانهای کاربر-facing درست و برای گزارشها اشتباه است.
سه باگ تکرارشونده
تاریخهای خالی بهعنوان نیمهشب UTC تجزیه میشوند. بر اساس مشخصات ECMAScript، رشتههای فقط-تاریخ UTC هستند در حالی که رشتههای تاریخ-زمان محلیاند. این ناسازگاری منبع قابل اعتمادی برای باگهای یکروز-اختلاف است.
منطقه پایگاهداده با سریالایزر نمیخواند. datetime در MySQL منطقه ندارد؛ محلی بنویسید، UTC بخوانید و همهچیز به اندازه offset جابهجا میشود.
تغییر ساعت تابستانی. ذخیره زمانهای محلی آینده لحظههایی میسازد که وجود ندارند یا دو بار رخ میدهند. UTC چنین شکافی ندارد.
پرسشهای متداول
چگونه بین ISO 8601 و عدد epoch انتخاب کنم؟
وقتی انسان payload را میخواند، وقتی ترتیب مرتبسازی واژگانی میخواهید یا وقتی مقدار باید منطقه خودش را حمل کند از ISO 8601 استفاده کنید؛ هزینهاش حدود یک دوجین بایت اضافه و یک کتابخانه تجزیه است. وقتی فشردگی و مدیریت ساده بینزبانی مهمترند از عدد epoch استفاده کنید، اما ثانیه در برابر میلیثانیه را سراسری تثبیت کنید و بگویید فیلد چه واحدی دارد.
آیا تاریخ خالی و مهر زمانی باید فیلد یکسان داشته باشند؟
نه. 2026-10-30 خالی یک روز تقویمی بدون زمان و منطقه است، در حالی که فیلد مهر زمانی انتظار میرود لحظه دقیق حمل کند. تولد و دورههای صورتحساب را در فیلد date جدا نگه دارید و برای زمان ایجاد یا بهروزرسانی از فیلد At با ISO 8601 استفاده کنید.