डेट और टाइम टेस्टिंग
डेट और टाइम टेस्टिंग यह जाँचती है कि सॉफ़्टवेयर उन पलों पर सही चलता है जिन्हें कैलेंडर और घड़ी ख़ास बना देते हैं: जब trial या token expire होता है, subscription renew होता है, scheduled job चलता है, यूज़र 18 साल का होता है, या कोई महीना, साल या daylight saving का दौर ख़त्म होता है। घड़ी भी किसी form field जैसा input है, बस उसे कोई टाइप नहीं करता, इसलिए ज़्यादातर टीमें सिर्फ़ वही एक value टेस्ट कर पाती हैं जो टेस्ट चलाने वाले दिन घड़ी में थी। Future date testing — जिसे date simulation या time-travel testing भी कहते हैं — इस input को नियंत्रित बनाती है: आप घड़ी को, या डेटा के भीतर की तारीख़ों को, उन पलों पर ले जाते हैं जो मायने रखते हैं, और हर पल पर expected state जाँचते हैं।
यह गाइड बताती है कि कोड और टेस्ट environments में समय को नियंत्रित कैसे बनाएँ, घड़ी खिसकाने और टेस्ट डेटा की तारीख़ें खिसकाने में से कब क्या चुनें, कैलेंडर, time zone और timestamp के कौन-से edge cases कवर करें, और Playwright, Jest, Vitest, Java, Python और .NET में समय को freeze कैसे करें। आख़िर में 30 दिन के trial का worked example और copy करने लायक checklist है। हर locale में तारीख़, संख्या और मुद्रा कैसे दिखती है, यह roadmap का lesson "तारीख़, संख्या, मुद्रा और टाइम ज़ोन" सिखाता है; यह गाइड समय के साथ व्यवहार के बारे में है।
डेट और टाइम टेस्टिंग में क्या आता है
हर वह नियम जिसमें कोई अवधि, deadline या कैलेंडर की इकाई आती है, कहीं न कहीं घड़ी पढ़ता है, और उसके defects वहीं छिपे होते हैं। घड़ी एक छिपा हुआ input है: टेस्ट जिस दिन लिखा जाता है उस दिन pass होता है, और failure 30 दिन बाद, 29 फ़रवरी को, घड़ियाँ बदलने वाली रात को या 1 जनवरी को दिखता है, जब कोई देख नहीं रहा होता। समय पर निर्भर defects इसलिए भी बच निकलते हैं कि हर environment की अपनी घड़ी और zone है — रियाद में एक laptop, UTC में CI runner, कहीं और production server — और expected result इस पर निर्भर है कि टेस्ट कब चला, इसलिए ऐसे कई checks में कोई तय expected value होती ही नहीं।
डेट और टाइम टेस्टिंग कोई अलग test level नहीं है। यह test conditions — समय के पलों — का एक परिवार है जो UI टेस्ट, API टेस्ट, batch jobs और reports पर लागू होता है। Technique है boundary value analysis: हर time rule की boundaries होती हैं, और दिलचस्प values हर boundary के ठीक पहले, ठीक उस पर और ठीक बाद होती हैं। घड़ी वहाँ input है जहाँ प्रोडक्ट में ये हों:
- Expiry — trials, passwords, sessions, tokens, coupons, quotes, OTP codes, signed URLs और certificates।
- Renewal और billing — subscriptions, invoices, grace periods, dunning, late fees और रोज़ जुड़ने वाला ब्याज।
- Schedules — cron jobs, reminders, notifications, reports, और data retention या purge jobs जो कुछ दिनों बाद डेटा हटाते हैं।
- Eligibility — उम्र की सीमा (आज 18 या कल?), probation period, cooling-off और refund window, warranty period।
- Reporting — "आज", "इस हफ़्ते", "इस महीने", fiscal periods और दिन के हिसाब से group होने वाली हर चीज़, जो zone पर निर्भर है।
- क्रम और इतिहास — audit trails, "latest" records, created_at से sorting और time-ordered identifiers।
समय को नियंत्रित बनाएँ: clock inject करें
Testability के लिए सबसे क़ीमती बदलाव है कोड के भीतर गहराई में "now" पढ़ना बंद करना। जब business logic सीधे Date.now(), Instant.now(), DateTime.UtcNow या datetime.now() बुलाता है, तो उसे किसी दूसरे पल पर टेस्ट करने का अकेला तरीका पूरी मशीन की घड़ी बदलना है। जब logic को एक clock मिलती है — एक छोटा interface जो मौजूदा instant लौटाता है — तो टेस्ट 2026-03-02T06:59:59Z पर सेट fixed clock दे सकता है और production system clock देता है। यही clock injection है। Java में Java 8 से java.time.Clock है, Clock.fixed और Clock.offset के साथ, और उसका documentation कहता है कि जिस भी method को मौजूदा instant चाहिए उसे Clock pass करें; .NET 8 ने System.TimeProvider जोड़ा, और टेस्ट के लिए FakeTimeProvider; JavaScript और Python में एक छोटा function या parameter यही काम करता है।
End-to-end और manual टेस्टिंग में यही विचार एक स्तर ऊपर जाता है: test-only time-travel setting। Non-production build configuration से, या सिर्फ़ टेस्ट environments में मौजूद admin endpoint से, clock offset या fixed instant पढ़ता है, ताकि टेस्टर server की घड़ी छुए बिना कह सके "यह account 2 मार्च 2026 में जी रहा है"। तब ऐप को यही clock हर जगह इस्तेमाल करनी होगी — web layer, background jobs, और database queries जो NOW() या CURRENT_TIMESTAMP बुलाती हैं — वरना उसके हिस्से इस पर असहमत होंगे कि आज कौन-सा दिन है।
मशीन या process की घड़ी बदलना (environment-level clock shifting) अब भी काम का है, legacy systems और उन binaries के लिए जिन्हें आप बदल नहीं सकते, लेकिन इसके side effects हैं जिनकी योजना टेस्ट को बनानी होगी:
- TLS: certificate की validity local घड़ी से जाँची जाती है, इसलिए certificate की expiry के बाद, या उसकी शुरुआत से पहले, ले जाई गई घड़ी HTTPS calls को ऐसे certificate errors से fail करती है जिनका टेस्ट हो रहे feature से कोई लेना-देना नहीं।
- Tokens और signatures: असली घड़ी वाली service का जारी किया JWT, OAuth token या signed URL खिसकी घड़ी वाले component को expired, या अभी तक अमान्य, लगता है।
- Time sync: NTP clients (chrony, systemd-timesyncd, Windows Time service) घड़ी को असली समय पर लौटा देते हैं, कभी-कभी टेस्ट के बीच में; टेस्ट वाली मशीन पर उन्हें बंद करें और बाद में फिर चालू करें।
- Shared environments: system clock server पर सबकी है, इसलिए एक टेस्टर का time travel बाक़ी सबके टेस्ट तोड़ देता है। Containers host की wall clock साझा करते हैं, क्योंकि Linux time namespaces सिर्फ़ monotonic और boot-time clocks को virtualise करते हैं, CLOCK_REALTIME को नहीं।
- Future में छूटा डेटा: घड़ी आगे रहते बने records घड़ी लौटने के बाद भी future timestamps रखते हैं, जिससे sorting, "latest" queries और time-ordered IDs टूटते हैं; घड़ी के साथ डेटा भी reset करें।
- Catch-up runs: घड़ी को कई दिन आगे ले जाने पर scheduler, अपनी बनावट के हिसाब से, छूटे सारे jobs एक साथ चला सकता है या सब छोड़ सकता है।
Future date testing: boundaries और expected states
Future date testing, जिसे forward date testing भी कहते हैं, जाँचती है कि समय आगे बढ़ने पर सिस्टम क्या करता है: trial ख़त्म हो गया, invoice overdue है, यूज़र अब 18 का है, retention period बीत गया। तरीका हर नियम के लिए एक ही है: नियम को एक boundary instant के रूप में लिखें, उसके आसपास के पल गिनें, और कुछ भी चलाने से पहले हर पल की expected state लिख लें।
Precision तय करती है कि "ठीक पहले" कितना पहले है। अगर सिस्टम seconds स्टोर करता है, तो boundary से 1 सेकंड पहले, ठीक उस पर और 1 सेकंड बाद टेस्ट करें; अगर milliseconds स्टोर करता है, तो 1 ms पहले भी मायने रखता है। Storage ख़ुद किसी पल को boundary पर ला सकता है: MySQL कम fractional digits वाले column में fractional seconds वाली value डालने पर उसे round करता है, और DATETIME व TIMESTAMP columns की डिफ़ॉल्ट precision 0 है, इसलिए 06:59:59.700 पर हुई कोई event 07:00:00 के रूप में, ठीक boundary पर, स्टोर होती है और मान ली जाती है कि वह trial ख़त्म होने के बाद हुई। Boundary से ठीक पहले .499 और .500 पर ख़त्म होने वाली values टेस्ट करें।
फिर product owner के साथ inclusive बनाम exclusive तय करें, क्योंकि "2 मार्च तक valid" के कम से कम तीन मतलब हैं: 2 मार्च की शुरुआत तक, 2 मार्च के अंत तक, या उस instant तक जब purchase 30 दिन पुरानी हो — और किस zone में? Standards यह चुनाव साफ़ करते हैं: JWT को तभी reject करना है जब मौजूदा समय exp से पहले न रहे, और nbf से आगे accept करना है (RFC 7519)। चुना गया नियम acceptance criteria में comparison के रूप में लिखें, जैसे active while now < end।
- हर boundary के पहले, उस पर और बाद: −1 s, exact, +1 s (milliseconds स्टोर हों तो −1 ms)।
- लंबे अंतराल: यूज़र 400 दिन बाद लौटता है; कोई job एक हफ़्ते नहीं चला; record बनते समय reminder की तारीख़ पहले ही बीत चुकी थी।
- घटनाओं का क्रम: एक ही दिन renewal और cancellation; expiry के एक सेकंड बाद आया payment; window के आख़िरी दिन माँगा गया refund।
- Derived values: "days left" counters, progress bars और "कल ख़त्म होगा" कहने वाले emails, यूज़र के zone में 23:59 और 00:00 पर जाँचे गए।
- Background effects: boundary पर account requests के लिए expired है, लेकिन उसे downgrade करने वाला nightly job बाद में चलता है; बीच में यूज़र क्या देखता है, जाँचें।
- Input के रूप में पुरानी तारीख़ें: back-dated records, historical डेटा का import, और ऐसी start date जो end date को आज से पहले कर दे।
आज़माएँ: डेट और टाइम टेस्ट सिम्युलेटर आज़माएँ: बाउंड्री वैल्यू एनालिसिस आज़माएँ: तारीख़ कैलकुलेटर
Date simulation बनाम date shifting: घड़ी खिसकाएँ या डेटा
सिस्टम को future में ले जाने के दो तरीके हैं। Date simulation, यानी time travel, वह घड़ी खिसकाता है जिसे ऐप पढ़ता है — injected clock, time-travel setting या fake system clock से — और डेटा जैसा है वैसा छोड़ देता है। Date shifting डेटा खिसकाता है: टेस्ट data set की हर तारीख़ एक ही offset से खिसकाई जाती है, या किसी anchor के सापेक्ष लिखी जाती है, जैसे "आज से 29 दिन पहले", ताकि तारीख़ों के बीच के रिश्ते — purchase के 29 दिन, renewal तक 3 दिन — suite जब भी चले, असली घड़ी के साथ सही रहें।
Shifting अंतराल बचाता है, कैलेंडर में जगह नहीं। हर तारीख़ 30 दिन आगे खिसकाएँ तो 31 जनवरी की purchase 2 मार्च पर आ जाती है: trial ख़त्म होने तक के 29 दिन बचे रहते हैं, लेकिन महीने का अंत, हफ़्ते का दिन और 29 फ़रवरी नहीं बचते, जब तक आप offset सोच-समझकर न चुनें (7 का गुणज हफ़्ते के दिन बचाता है; पूरे साल महीना और तारीख़ बचाते हैं, पर न हफ़्ते के दिन, न leap days)। हर derived field को उसके source के साथ खिसकना होगा: उम्र, due dates, ISO week numbers, fiscal periods, partition keys, और तारीख़ पर बना कोई भी hash या signature।
- Date simulation तब इस्तेमाल करें जब व्यवहार किसी ख़ास कैलेंडर पल से जुड़ा हो: महीने का अंत, 29 फ़रवरी, साल का अंत, DST बदलाव का दिन, tz नियम का बदलाव, 2038 की boundary, और हर वह चीज़ जिसका expected result सटीक तारीख़ पर निर्भर है।
- Date shifting relative नियमों के लिए इस्तेमाल करें, shared environments और parallel runs में: "purchase के 30 दिन बाद expire", "renewal से 3 दिन पहले reminder", "90 दिन बाद archive"। कोई घड़ी नहीं छुई जाती, इसलिए TLS, tokens, दूसरे टेस्टर और NTP अप्रभावित रहते हैं।
- दोनों मिलाएँ: regression suite के ज़्यादातर हिस्से के लिए shifted डेटा, और ख़ास कैलेंडर पलों पर pinned simulated-clock टेस्ट का छोटा set।
- दोनों तरीकों में बाहरी सिस्टम अपनी घड़ी रखते हैं: payment provider का sandbox, identity provider और mail server आपकी simulated तारीख़ या shifted डेटा साझा नहीं करेंगे।
-- Date shifting (PostgreSQL): move a whole data set 30 days forward,
-- so every relative age stays the same; shift derived columns with it
UPDATE subscriptions
SET signed_up_at = signed_up_at + INTERVAL '30 days',
trial_end = trial_end + INTERVAL '30 days',
renews_on = renews_on + 30; -- renews_on is a DATE: + 30 adds days
// Relative fixtures instead of fixed dates (JavaScript)
const DAY = 24 * 60 * 60 * 1000;
const now = clock.now(); // injected clock, milliseconds since the epoch
const account = { signedUpAt: new Date(now - 29 * DAY) }; // 29 days ago
// expected: trial active, "1 day left"कैलेंडर के edge cases: महीने का अंत, leap year और हफ़्ते
महीनों का हिसाब वह जगह है जहाँ libraries असहमत होती हैं। 31 जनवरी में एक महीना जोड़ने का कोई एक सही जवाब नहीं, और हर library एक चुनती है। JavaScript का Date overflow करता है: 31 जनवरी 2026 पर setMonth 31 फ़रवरी माँगता है, जो आगे खिसककर 3 मार्च 2026 बन जाता है (MDN का अपना उदाहरण 31 जनवरी 2016 को 2 मार्च 2016 बनाता है)। Java का LocalDate.plusMonths आख़िरी valid दिन पर clamp करता है, इसलिए 2026-01-31 में एक महीना जोड़ने पर 2026-02-28 मिलता है, और Temporal API का add डिफ़ॉल्ट रूप से इसी तरह clamp करता है (overflow: "constrain"), या overflow: "reject" के साथ RangeError फेंकता है। 31 तारीख़ वाले "monthly" renewal में दोनों व्यवहार खिसकते हैं: overflow हर renewal को अगले महीने में धकेलता है (3 मार्च, 3 अप्रैल, 3 मई), और पिछले renewal पर लगाया गया clamping 28 तारीख़ पर आ टिकता है (28 फ़रवरी, 28 मार्च, 28 अप्रैल)। महीने के अंत तभी बचते हैं जब हर renewal मूल तारीख़ से निकाला जाए, यानी start + n months (28 फ़रवरी, 31 मार्च, 30 अप्रैल)। Requirement को बताना होगा कि कौन-सा नियम लागू है, और टेस्ट में 29, 30 और 31 तारीख़ के साथ लगातार कई renewals होने चाहिए; Date and Time Test Simulator ये schedules साथ-साथ दिखाता है।
"30 दिन" और "1 महीना" अलग नियम हैं: 31 जनवरी 2026 से 30 दिन 2 मार्च को ख़त्म होते हैं, एक clamped महीना 28 फ़रवरी को। Leap years ग्रेगोरियन नियम मानते हैं: 4 से विभाज्य साल leap year है, सिवाय शताब्दी वर्षों के, जो तभी leap year हैं जब 400 से विभाज्य हों — 2028 और 2000 leap years हैं, 2100 नहीं। 29 फ़रवरी को जन्मे व्यक्ति का जन्मदिन हर चार साल में ही आता है, और प्रोडक्ट को तय करना होगा कि बाक़ी सालों में वह 28 फ़रवरी को 18 का होगा या 1 मार्च को।
हफ़्तों का अपना कैलेंडर है। ISO 8601 में हफ़्ता सोमवार से शुरू होता है और week 1 वह हफ़्ता है जिसमें साल का पहला गुरुवार हो, इसलिए साल में 52 या 53 हफ़्ते होते हैं और जनवरी के शुरुआती दिन पिछले साल के हो सकते हैं: 2026 में 53 ISO weeks हैं, और 1 जनवरी 2027 2026 के week 53 में पड़ता है। Formatting patterns यहाँ फिसलते हैं: Java के DateTimeFormatter में YYYY week-based year है और yyyy कैलेंडर साल, इसलिए pattern YYYY-MM-dd 31 दिसंबर 2026 को en-US locale में, जिसका हफ़्ता रविवार से शुरू होता है, 2027-12-31 छापता है।
One rule, three libraries: add 1 month to 31 January 2026
JavaScript Date d = new Date(2026, 0, 31); d.setMonth(d.getMonth() + 1)
-> 3 March 2026 (31 February overflows into March)
Java LocalDate.of(2026, 1, 31).plusMonths(1)
-> 2026-02-28 (clamped to the last valid day)
Temporal Temporal.PlainDate.from("2026-01-31").add({ months: 1 })
-> 2026-02-28 (overflow: "constrain", the default)
...add({ months: 1 }, { overflow: "reject" })
-> RangeError
"30 days" instead of "1 month"
-> 2026-03-02
Monthly renewal from 31 January 2026, next three renewals
overflow, from the previous renewal (JS setMonth) 2026-03-03, 2026-04-03, 2026-05-03
clamp, from the previous renewal (Java plusMonths) 2026-02-28, 2026-03-28, 2026-04-28
clamp, from the original date (start + n months) 2026-02-28, 2026-03-31, 2026-04-30
Dates every date test data set should contain
2026-01-29 .. 2026-01-31 monthly rules on the 29th, 30th and 31st
2026-02-28, 2026-03-01 end of a short February
2028-02-29, 2028-03-01 leap day and the day after
2100-02-28 2100 is not a leap year (no 29 February)
2026-12-31, 2027-01-01 year end; both fall in ISO week 2026-W53
2025-12-29 already in ISO week 2026-W01
2026-12-27 .. 2026-12-31 Java pattern YYYY-MM-dd prints 2027 in en-USआज़माएँ: डेट और टाइम टेस्ट सिम्युलेटर आज़माएँ: तारीख़ टेस्ट डेटा जनरेटर आज़माएँ: तारीख़ कैलकुलेटर
Time zones, DST और tz database के बदलाव
Instants को UTC में या स्पष्ट offset के साथ स्टोर करें, और यूज़र का zone IANA नाम से रखें, जैसे Europe/Berlin या Asia/Riyadh, किसी fixed offset से नहीं: offset एक पल का तथ्य है, zone नियमों का set है जो समय के साथ बदलता है। Local time में बदलाव सिर्फ़ किनारों पर करें। समय कैसे दिखाया जाए, यह roadmap lesson "तारीख़, संख्या, मुद्रा और टाइम ज़ोन" में है; समय के साथ व्यवहार में defects तीन जगहों से आते हैं।
पहला: daylight saving time (DST) ऐसे local times बनाता है जो होते ही नहीं, और ऐसे जो दो बार होते हैं। Europe/Berlin में 29 मार्च 2026 को घड़ियाँ 02:00 से 03:00 पर कूदती हैं, इसलिए 02:30 होता ही नहीं (gap); 25 अक्टूबर 2026 को वे 03:00 से 02:00 पर लौटती हैं, इसलिए 02:30 दो बार होता है, 00:30Z और 01:30Z पर (overlap)। America/New_York में बदलाव 8 मार्च 2026 (gap 02:00–03:00) और 1 नवंबर 2026 (overlap 01:00–02:00) को हैं। Libraries इन मामलों को नियम से सुलझाती हैं: Java का ZonedDateTime और Temporal का डिफ़ॉल्ट "compatible" disambiguation gap वाले समय को आगे खिसकाते हैं (02:30 बन जाता है 03:30) और overlap में पहले वाला offset चुनते हैं, और Temporal को दोनों को reject करने को भी कहा जा सकता है।
Local समय 02:30 पर scheduled daily job का दोनों दिनों के लिए घोषित व्यवहार होना चाहिए — 03:00 पर चले, 03:30 पर चले, छूट जाए, या दो बार की जगह एक बार चले। cronie, कई Linux distributions का cron daemon, तीन घंटे से कम के clock बदलाव पर छूटे interval के jobs बदलाव के तुरंत बाद चलाता है और दोहराए गए interval में उन्हें दो बार चलाने से बचता है; दूसरे schedulers अलग व्यवहार करते हैं, इसलिए सिर्फ़ expression नहीं, job टेस्ट करें। अवधियाँ भी बदलती हैं: Europe/London में 10 मार्च 2026 10:00 से 30 कैलेंडर दिन 9 अप्रैल को 10:00 BST पर ख़त्म होते हैं, लेकिन 720 घंटे 11:00 BST पर, क्योंकि 29 मार्च को घड़ियाँ आगे हुई थीं।
दूसरा: बिना DST वाले zones यह सब छिपा देते हैं। Asia/Riyadh पूरे साल UTC+03:00 है और उसमें कोई बदलाव नहीं, इसलिए रियाद की टीम gap या overlap तब तक नहीं देखती जब तक वह ऐसा zone टेस्ट न करे जिसमें ये हों। ऐसे offsets भी जोड़ें जो पूरे घंटे नहीं हैं — Asia/Kolkata +05:30 और Asia/Kathmandu +05:45 — ताकि offset को पूरे घंटों के integer में स्टोर करने वाला कोड पकड़ा जाए।
तीसरा: नियम ख़ुद बदलते हैं, और tz database (tzdata) हर बदलाव नई release में दर्ज करता है। मेक्सिको ने 2022 के बाद DST मानना बंद कर दिया, US border के पास के इलाक़ों को छोड़कर (tzdata 2022f), इसलिए America/Mexico_City 30 अक्टूबर 2022 से पूरे साल UTC−06:00 है। पराग्वे ने 6 अक्टूबर 2024 को घड़ियाँ आगे करने के बाद उन्हें बदलना बंद कर दिया और UTC−03:00 पर रहता है (tzdata 2025a, जनवरी 2025), इसलिए America/Asuncion पूरे 2026 में −03 है। मैनिटोबा (America/Winnipeg) 31 अक्टूबर 2026 को स्थायी −05 पर जाता है (tzdata 2026e, 29 सितंबर 2026)। पुराना डेटा रखने वाला runtime पुराने नियमों से हिसाब लगाता है: अपने bundled tzdata 2024a वाला JDK 22.0.2 2026-07-01T12:00Z को Asunción में 09:00 −03:00 की जगह 08:00 −04:00 बनाता है। जिस tzdata version को आप deploy करते हैं उसी पर टेस्ट करें, हर runtime में — operating system, JVM, Node.js और ICU, browser, database — और ऐसा टेस्ट जोड़ें जो हाल में बदले zone का conversion ग़लत होने पर fail हो।
Daylight saving changes in 2026: what a 02:30 local time becomes
Zone Day Clocks 02:30 local time
Europe/Berlin 29 Mar 02:00 -> 03:00 does not exist (gap); java.time and Temporal
"compatible" resolve it to 03:30 +02:00
Europe/Berlin 25 Oct 03:00 -> 02:00 happens twice (overlap):
00:30Z at +02:00 and 01:30Z at +01:00
America/New_York 8 Mar 02:00 -> 03:00 does not exist; resolved to 03:30 -04:00
America/New_York 1 Nov 02:00 -> 01:00 happens once; 01:00-01:59 happens twice
Europe/London 29 Mar 01:00 -> 02:00 normal; 01:00-01:59 does not exist
Asia/Riyadh none +03:00 all year always exists, exactly once
30 calendar days vs 720 hours across a change (Europe/London)
2026-03-10 10:00 GMT + 30 days = 2026-04-09 10:00 BST (09:00Z)
2026-03-10 10:00 GMT + 720 h = 2026-04-09 11:00 BST (10:00Z)
Zones whose rules changed recently (tzdata release that records it)
America/Mexico_City -06:00 all year since 2022-10-30 (2022f)
2026-07-01T12:00Z -> 06:00; older data: 07:00 -05:00
America/Asuncion -03:00 all year since 2024-10-06 (2025a)
2026-07-01T12:00Z -> 09:00; older data (e.g. JDK 22.0.2
with tzdata 2024a): 08:00 -04:00
America/Winnipeg permanent -05:00 from 2026-10-31 (2026e), no fall-back on 1 Nov
2026-12-01T12:00Z -> 07:00; older data: 06:00 -06:00आज़माएँ: Timezone कन्वर्टर आज़माएँ: Cron एक्सप्रेशन एक्सप्लेनर
Timestamps: epoch की इकाइयाँ, 2038, leap seconds और formats
Unix time 1970-01-01T00:00:00Z से seconds गिनता है; JavaScript और कई APIs milliseconds गिनते हैं। इकाई की गड़बड़ी पहचानना आसान है अगर आप उसका निशान जानते हों: 1772434800 (seconds में 2 मार्च 2026, 07:00Z) को milliseconds मानकर पढ़ें तो 21 जनवरी 1970 बनता है, और वही instant milliseconds में seconds मानकर पढ़ें तो साल 58136 में पहुँचता है। JWT के exp, nbf और iat जैसे claims seconds में NumericDate values हैं (RFC 7519), इसलिए milliseconds वाला exp रखने वाला token दसियों हज़ार साल valid लगता है।
साल 2038 की समस्या signed 32-bit seconds की गिनती की सीमा है: सबसे बड़ी value 2147483647 है, यानी 2038-01-19T03:14:07Z, और एक सेकंड बाद counter −2147483648 पर, यानी 1901-12-13T20:45:52Z पर, पलट जाता है। टेस्ट के लिए यह दूर की समस्या नहीं: 15 साल का contract, mortgage, certificate या "कभी expire नहीं" वाली sentinel तारीख़ इसे आज ही पार करती है। उदाहरण के लिए MySQL का TIMESTAMP type 2038-01-19 03:14:07 UTC पर ख़त्म होता है, जबकि DATETIME 9999-12-31 तक जाता है। Future तारीख़ स्टोर करने वाले हर field में 19 जनवरी 2038 के बाद की तारीख़ टेस्ट करें, हर layer से होकर — API, database, exports और mobile apps।
Unix time में leap seconds होते ही नहीं: POSIX हर दिन को ठीक 86400 seconds मानता है, JavaScript का Date उन्हें नज़रअंदाज़ करता है, और RFC 3339 seconds की value 60 सिर्फ़ वहीं allow करता है जहाँ सच में leap second होता है। उन्हें ऐप के भीतर टेस्ट करने की कोशिश न करें; इसकी जगह जाँचें कि :60 वाला input requirement के अनुसार reject या handle होता है, और अवधि wall clock की दो readings के अंतर से नहीं, monotonic clock से नापी जाती है।
Formats: instants का आदान-प्रदान RFC 3339 में करें, जो ISO 8601 का internet profile है — 2026-03-02T07:00:00Z या 2026-03-02T10:00:00+03:00, हमेशा Z या offset के साथ। 03/04/2026 जैसी string अमेरिका में 4 मार्च है और ज़्यादातर दूसरी जगहों पर 3 अप्रैल, और JavaScript का Date सिर्फ़ अपने ISO 8601-आधारित format की गारंटी देता है; बाक़ी strings को engine जैसे चाहे parse करता है। ISO strings में भी जाल है: JavaScript में सिर्फ़ तारीख़ वाली string, जैसे 2026-03-04, UTC मानी जाती है, लेकिन बिना offset की date-time, जैसे 2026-03-04T00:00, local time। PostgreSQL में timestamp without time zone input के offset को चुपचाप नज़रअंदाज़ करता है, जबकि timestamp with time zone उसे UTC में बदलता है।
One instant, five encodings
RFC 3339, UTC 2026-03-02T07:00:00Z
RFC 3339, Riyadh 2026-03-02T10:00:00+03:00
Unix seconds 1772434800
Unix milliseconds 1772434800000
JWT "exp" (seconds) 1772434800
Unit mix-ups and what they look like
seconds read as ms 1772434800 -> 1970-01-21T12:20:34.800Z
ms read as seconds 1772434800000 -> a date in the year 58136
Signed 32-bit seconds (the 2038 problem)
2147483647 2038-01-19T03:14:07Z largest value
-2147483648 1901-12-13T20:45:52Z one second later, after wrap-around
MySQL TIMESTAMP up to 2038-01-19 03:14:07 UTC
MySQL DATETIME up to 9999-12-31 23:59:59
Ambiguous or surprising input
03/04/2026 4 March (en-US) or 3 April (en-GB)
2026-03-04 JavaScript: midnight UTC (date-only form)
2026-03-04T00:00 JavaScript: midnight local time (date-time, no offset)
2026-03-04 10:00+03 PostgreSQL timestamp without time zone: offset ignoredआज़माएँ: Unix टाइमस्टैम्प कन्वर्टर आज़माएँ: ISO 8601 कन्वर्टर आज़माएँ: JWT एक्सपायरेशन चेकर
Playwright, Jest, Java, Python और .NET में समय freeze या shift कैसे करें
हर प्रमुख stack में टेस्ट के दौरान समय नियंत्रित करने का supported तरीका है। उसे उसी स्तर पर इस्तेमाल करें जहाँ फ़ैसला होता है: browser की घड़ी server को नहीं खिसकाती, और server-side fake वह नहीं बदलता जो browser दिखाता है।
- Playwright (browser): page.clock पेज के भीतर Date, timers और performance को fake करता है। setFixedTime, Date.now() और new Date() को freeze करता है जबकि timers चलते रहते हैं; install({ time }) को हर दूसरे clock call से पहले आना होगा, और वही fastForward, runFor, pauseAt और resume चालू करता है; setSystemTime timers चलाए बिना समय बदलता है, घड़ी के बदलाव को simulate करने के लिए। Browser को किसी ख़ास zone में चलाने के लिए इसे timezoneId option के साथ जोड़ें।
- Jest और Vitest (JavaScript unit tests): useFakeTimers() Date और timer functions को बदल देता है; setSystemTime(date) timers चलाए बिना मौजूदा समय सेट करता है; advanceTimersByTime(ms) timers चलाता है; useRealTimers() टेस्ट के बाद सब कुछ लौटा देता है। Vitest का vi.setSystemTime fake timers के बिना भी काम करता है, और तब सिर्फ़ Date को mock करता है।
- Java: java.time.Clock pass करें; frozen पल के लिए Clock.fixed(instant, zone) और future या past में चलाने के लिए Clock.offset(baseClock, duration) इस्तेमाल करें।
- Python: time-machine (time_machine.travel(destination, tick=False)) या freezegun (@freeze_time("2026-03-02")) टेस्ट के दौरान datetime और time को patch करते हैं; time-machine का traveller टेस्ट के बीच shift() या move_to() कर सकता है।
- .NET: TimeProvider पर निर्भर रहें (.NET 8 और बाद में built-in, पुराने targets के लिए Microsoft.Bcl.TimeProvider package से) और टेस्ट में Microsoft.Extensions.TimeProvider.Testing package का FakeTimeProvider इस्तेमाल करें। Advance(TimeSpan) और SetUtcNow समय को सिर्फ़ आगे ले जाते हैं (SetUtcNow पहले का समय स्वीकार नहीं करता), जबकि AdjustTime timers पर असर डाले बिना समय बदलता है।
- न बदले जा सकने वाले binaries: libfaketime Linux पर एक process के time calls को LD_PRELOAD या faketime wrapper से intercept करता है, FAKETIME="+30d" जैसे offset या किसी absolute तारीख़ के साथ। यह statically linked binaries के साथ काम नहीं करता, और Go जैसे runtimes इसे bypass कर सकते हैं।
// isTrialActive, is_trial_active and TrialService stand for the code under test.
// Playwright Test (page.goto("/account") resolves against baseURL in playwright.config).
// This fakes the browser clock only: the server must agree on "now".
import { test, expect } from "@playwright/test";
test.use({ timezoneId: "Asia/Riyadh" });
for (const [now, plan] of [["2026-03-02T06:59:59Z", "Trial"], ["2026-03-02T07:00:00Z", "Expired"]]) {
test(`plan shown at ${now}`, async ({ page }) => {
await page.clock.setFixedTime(new Date(now));
await page.goto("/account");
await expect(page.getByTestId("plan")).toHaveText(plan);
});
}
// Jest test file, globals (Vitest: vi.useFakeTimers, vi.setSystemTime, vi.useRealTimers)
afterEach(() => jest.useRealTimers());
test("trial ends exactly at trialEnd", () => {
jest.useFakeTimers();
const trialEnd = new Date("2026-03-02T07:00:00Z");
jest.setSystemTime(new Date("2026-03-02T06:59:59Z"));
expect(isTrialActive(trialEnd)).toBe(true);
jest.setSystemTime(trialEnd);
expect(isTrialActive(trialEnd)).toBe(false);
});
// Java + JUnit 5: production code receives a Clock instead of calling Instant.now()
import java.time.*;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class TrialTest {
static boolean isTrialActive(Instant end, Clock clock) { return clock.instant().isBefore(end); }
@Test
void trialEndsExactlyAtEnd() {
Instant end = Instant.parse("2026-03-02T07:00:00Z");
Clock before = Clock.fixed(end.minusSeconds(1), ZoneId.of("Asia/Riyadh"));
assertTrue(isTrialActive(end, before));
assertFalse(isTrialActive(end, Clock.offset(before, Duration.ofSeconds(1))));
}
}
# Python (pytest body): time-machine
import datetime as dt
import time_machine
END = dt.datetime(2026, 3, 2, 7, 0, tzinfo=dt.timezone.utc)
with time_machine.travel(END - dt.timedelta(seconds=1), tick=False) as traveller:
assert is_trial_active(END)
traveller.shift(dt.timedelta(seconds=1))
assert not is_trial_active(END)
// .NET 8+ test body (xUnit): TimeProvider in production, FakeTimeProvider in tests
// (package Microsoft.Extensions.TimeProvider.Testing, namespace Microsoft.Extensions.Time.Testing)
var end = new DateTimeOffset(2026, 3, 2, 7, 0, 0, TimeSpan.Zero);
var time = new FakeTimeProvider(end.AddSeconds(-1));
var trials = new TrialService(time); // reads time.GetUtcNow()
Assert.True(trials.IsActive(end));
time.Advance(TimeSpan.FromSeconds(1));
Assert.False(trials.IsActive(end));
# libfaketime (Linux): fake the clock of one process only
TZ=UTC faketime "2026-03-02 06:59:59" ./expire-trials
LD_PRELOAD=/path/to/libfaketime.so.1 FAKETIME="+30d" ./expire-trialsWorked example: 30 दिन का trial, 30 दिन इंतज़ार किए बिना
नियम, Daftar Invoices नाम के एक काल्पनिक प्रोडक्ट के लिए: "Free trial sign-up से 30 दिन चलता है। यह तब तक active है जब तक मौजूदा समय trial_end से पहले है। trial_end से 3 दिन पहले reminder email जाता है। Asia/Riyadh समय 00:05 पर चलने वाला nightly job उन accounts को downgrade करता है जिनका trial ख़त्म हो चुका है।" एक यूज़र 31 जनवरी 2026 को रियाद में 10:00 बजे sign up करता है।
पहले expected values निकालें। Sign-up 2026-01-31T10:00:00+03:00 है, यानी 2026-01-31T07:00:00Z या Unix time 1769842800। फ़रवरी 2026 में 28 दिन हैं, इसलिए 30 दिन बाद रियाद समय के अनुसार 2 मार्च 2026 10:00 है: trial_end = 2026-03-02T07:00:00Z, Unix time 1772434800। Asia/Riyadh में DST नहीं, इसलिए यहाँ 30 कैलेंडर दिन और 720 घंटे एक ही instant देते हैं; 29 मार्च के पार Europe/London में नहीं देते। Reminder 2026-02-27T07:00:00Z (रियाद में 10:00) पर due है। Trial को ख़त्म देखने वाला पहला nightly run रियाद समय 3 मार्च 2026 00:05 पर, यानी 2026-03-02T21:05:00Z पर, होता है।
फिर पल चुनें और हर पल की expected state लिखें, घड़ी को हर पल पर सेट करें (या account का डेटा खिसकाएँ ताकि वह उतना पुराना हो जाए), और हर वह जगह जाँचें जहाँ नियम दिखता है: API, account page, email, job और database। यही तालिका 31 जनवरी 2028 के sign-up के लिए दोहराएँ: तब फ़रवरी में 29 दिन हैं, इसलिए trial_end रियाद समय 1 मार्च 2028 10:00 होगा।
Daftar Invoices (fictional): 30-day trial, sign-up 2026-01-31 10:00 Asia/Riyadh
Rule: active while now < trial_end; trial_end = sign-up + 30 days
Moment (UTC) Riyadh (+03:00) Expected
2026-01-31T07:00:00Z 31 Jan 10:00:00 active, "30 days left", trial_end 2026-03-02T07:00:00Z
2026-02-27T06:59:59Z 27 Feb 09:59:59 no reminder yet
2026-02-27T07:00:00Z 27 Feb 10:00:00 reminder sent once: "Your trial ends on 2 March 2026"
2026-03-01T20:59:59Z 1 Mar 23:59:59 active, "ends tomorrow"
2026-03-01T21:00:00Z 2 Mar 00:00:00 active, "ends today"
2026-03-02T06:59:59Z 2 Mar 09:59:59 active (last second)
2026-03-02T07:00:00Z 2 Mar 10:00:00 ended: API refuses premium features (exclusive end)
2026-03-02T07:00:01Z 2 Mar 10:00:01 ended
2026-03-02T21:05:00Z 3 Mar 00:05:00 nightly job downgrades the account
(same job run twice) second run changes nothing (idempotent)
Wrong implementations the table catches
+1 month, clamped (Java plusMonths) ends 2026-02-28 10:00 Riyadh 2 days early
+1 month, overflow (JS setMonth) ends 2026-03-03 10:00 Riyadh 1 day late
sign-up day counted as day 1 ends 2026-03-01 10:00 Riyadh 1 day early
date-only end compared with UTC date ends 2026-03-02 03:00 Riyadh 7 hours early
Leap-year variant: sign-up 2028-01-31 10:00 Riyadh -> trial_end 2028-03-01T07:00:00Zआज़माएँ: डेट और टाइम टेस्ट सिम्युलेटर आज़माएँ: तारीख़ अंतर कैलकुलेटर आज़माएँ: Timezone कन्वर्टर
डेट और टाइम टेस्ट checklist और आम गलतियाँ
Checklist को test plan या किसी story के acceptance criteria में copy करें और जो लागू न हो उसे काट दें। नीचे की गलतियाँ वे हैं जो समय पर निर्भर defects को production तक पहुँचने देती हैं:
- असली घड़ी से assert करना: टेस्ट के भीतर new Date() से निकाली गई expected values आज pass होती हैं और आधी रात, महीने के अंत या दूसरे zone में fail — यही समय से जुड़ा क्लासिक flaky टेस्ट है।
- सिर्फ़ "now" टेस्ट करना: टेस्ट में बना और एक सेकंड बाद जाँचा गया trial कभी अपने अंत तक नहीं पहुँचता; घड़ी सेट करें, या डेटा को boundary तक खिसकाएँ।
- सिर्फ़ एक घड़ी खिसकाना: browser मार्च में, API जनवरी में, database असली समय में — और नतीजा इनमें से किसी को नहीं दर्शाता।
- Zone की जगह offset स्टोर करना: बर्लिन के यूज़र के लिए सेव किया +01:00 आधे साल ग़लत रहता है।
- Library के महीने वाले हिसाब पर उसका documentation पढ़े बिना भरोसा: overflow और clamping अलग renewal dates देते हैं।
- Reset भूल जाना: future-dated records और shared server पर खिसकी छोड़ी गई घड़ी दूसरों के टेस्ट कई दिन तोड़ते हैं।
- CI सिर्फ़ UTC में, या सिर्फ़ रियाद समय में चलाना: कम से कम एक job DST वाले zone में और एक +05:30 offset के साथ चलाएँ (test runner के लिए TZ, browser के लिए timezoneId)।
Date and time test checklist
Rules
[ ] Every time rule written as a comparison with an instant (active while now < end)
[ ] Inclusive or exclusive end agreed; the zone that defines "a day" named
[ ] "30 days" vs "1 month" vs "end of day" decided for each rule
Control of time
[ ] Code reads time from an injected clock / TimeProvider, not from "now" directly
[ ] Server, jobs and database use the same clock in test environments
[ ] Clock shifting plan covers TLS, tokens, NTP, other users and data clean-up
Moments
[ ] -1 s / exact / +1 s around every boundary (-1 ms if milliseconds are stored)
[ ] 23:59:59 and 00:00:00 in the user's zone and in UTC
[ ] The 28th, 29th, 30th and 31st; 29 February 2028; 28 February 2100
[ ] 31 December and 1 January; ISO week 53 (2026); YYYY vs yyyy in patterns
[ ] DST gap and overlap days in a DST zone (Europe/Berlin, America/New_York)
[ ] +05:30 and +05:45 offsets; a zone without DST (Asia/Riyadh)
[ ] A date after 2038-01-19T03:14:07Z in every stored future date
Data and formats
[ ] Instants in RFC 3339 with Z or an offset; zones stored as IANA names
[ ] Seconds vs milliseconds checked at every API boundary (JWT exp is in seconds)
[ ] Column types: timestamptz vs timestamp; MySQL TIMESTAMP range; precision
[ ] tzdata version known for every runtime; recently changed zones tested
[ ] Jobs idempotent; missed and repeated runs handledअक्सर पूछे जाने वाले सवाल
Future date testing क्या है?
Future date testing (forward date testing) यह जाँचती है कि समय आगे बढ़ने पर सॉफ़्टवेयर कैसे व्यवहार करता है: expired trials और tokens, overdue invoices, renewals, उम्र की सीमा और retention periods। इंतज़ार करने की जगह टेस्टर उस घड़ी को future के किसी पल पर सेट करते हैं जिसे ऐप पढ़ता है — या टेस्ट डेटा की तारीख़ें खिसकाते हैं — और हर boundary पर state की तुलना expected result से करते हैं।
Date और time simulation testing क्या है?
यह simulated घड़ी के साथ टेस्टिंग है: ऐप समय ऐसी clock से पढ़ता है जिसे टेस्ट नियंत्रित करता है — कोड में fixed या offset clock, टेस्ट environment में time-travel setting, fake browser clock या fake process clock — ताकि कोई भी पल माँगने पर टेस्ट हो सके, महीने के अंत, leap days और DST बदलावों समेत।
घड़ी खिसकाऊँ या टेस्ट डेटा?
"purchase के 30 दिन बाद" जैसे relative नियमों के लिए डेटा खिसकाएँ — हर तारीख़ एक ही offset से, या आज के सापेक्ष लिखें — ख़ासकर shared environments में। ख़ास कैलेंडर पलों से जुड़े व्यवहार के लिए घड़ी खिसकाएँ, जैसे 29 फ़रवरी, साल का अंत, DST बदलाव का दिन या tz नियम का बदलाव, जहाँ सटीक तारीख़ मायने रखती है। ज़्यादातर suites को दोनों चाहिए।
30 दिन में expire होने वाली चीज़ बिना इंतज़ार कैसे टेस्ट करें?
नियम से expiry instant निकालें, फिर नियंत्रित घड़ी (Clock.fixed, FakeTimeProvider, jest.setSystemTime, page.clock.setFixedTime, time-machine) या 30 दिन पहले बने डेटा के साथ उससे 1 सेकंड पहले, ठीक उस पर और 1 सेकंड बाद जाँच चलाएँ। पहले तय करें कि अंत inclusive है या exclusive, और "दिन" किस zone से तय होता है।
Time zones और daylight saving time कैसे टेस्ट करें?
Instants UTC में या offset के साथ और zones IANA नामों से स्टोर करें, फिर कम से कम एक DST वाले zone, एक बिना DST वाले zone और एक आधे घंटे वाले offset के साथ टेस्ट चलाएँ। Gap (Europe/Berlin में घड़ी आगे होने वाले दिन 02:30 होता ही नहीं), overlap (अक्टूबर में 02:30 दो बार होता है), उनके भीतर scheduled jobs, और हर runtime का tzdata version कवर करें।
साल 2038 की समस्या क्या है?
जो सिस्टम Unix time को seconds की signed 32-bit संख्या में स्टोर करते हैं, वे सिर्फ़ 2147483647 तक गिन सकते हैं, यानी 2038-01-19T03:14:07Z; एक सेकंड बाद value दिसंबर 1901 की किसी तारीख़ पर पलट जाती है। उस पल के बाद की कोई भी स्टोर की गई future तारीख़ — लंबा contract, certificate, दूर की sentinel तारीख़ — इसे आज ही उजागर करती है, और MySQL का TIMESTAMP type भी, जिसकी range ठीक उसी पल पर ख़त्म होती है।
संदर्भ
- Time Zone Databaseदुनिया के time zone और उनके ऐतिहासिक offset व daylight-saving नियमों का tz डेटाबेस, जो साल में कई बार अपडेट होता है।
- Time Zone Database — NEWSहर tzdata version के release notes, जिनमें time zone नियमों का हर बदलाव दर्ज है — जैसे मेक्सिको का ज़्यादातर DST ख़त्म करना (2022f), पराग्वे का स्थायी -03 (2025a) और मैनिटोबा का स्थायी -05 (2026e)।
- RFC 3339: Date and Time on the Internet: TimestampsISO 8601 का इंटरनेट टाइमस्टैम्प प्रोफ़ाइल (2026-09-23T10:15:00Z) और उसके ऑफ़सेट नियम।
- ISO 8601 — Date and time formatतारीख़, समय, अवधि और अंतराल को स्पष्ट रूप से लिखने का अंतरराष्ट्रीय मानक।
- Seconds Since the Epoch — POSIX.1-2017 Base Definitions, section 4.16Unix time की POSIX परिभाषा: UTC समय से Epoch के बाद के seconds निकालने का सूत्र, जिसमें हर दिन ठीक 86400 seconds का है (इसलिए leap seconds नहीं गिने जाते)।
- RFC 7519: JSON Web Token (JWT)JWT फ़ॉर्मेट — हेडर, पेलोड, सिग्नेचर — और रजिस्टर्ड क्लेम iss, sub, aud, exp, nbf, iat और jti को परिभाषित करता है।
- Date — JavaScript reference — MDNJavaScript समय को Unix epoch से मिलीसेकंड के रूप में कैसे दर्शाता है और ISO 8601 स्ट्रिंग कैसे पार्स करता है।
- Temporal.ZonedDateTime — MDNJavaScript में IANA time zone के भीतर date-time, और DST के gaps व overlaps कैसे सुलझाए जाते हैं (disambiguation option: compatible, earlier, later, reject)।
- Clock — PlaywrightPlaywright टेस्ट हो रहे browser में समय कैसे नियंत्रित करता है: install, setFixedTime, setSystemTime, fastForward, runFor और pauseAt के साथ page.clock, और वह किन time APIs को override करता है।
- The DATE, DATETIME, and TIMESTAMP Types — MySQL 8.4 Reference ManualMySQL के date और time types की range — TIMESTAMP 2038-01-19 03:14:07 UTC पर, DATETIME 9999-12-31 पर ख़त्म होता है — और storage के समय TIMESTAMP का time zone conversion।
- PostgreSQL Documentation: SQL SyntaxPostgreSQL में SQL की लेक्सिकल संरचना: आइडेंटिफ़ायर, क्वोटिंग, स्ट्रिंग कॉन्स्टेंट और एस्केपिंग।
इस गाइड में बताए गए टूल्स
Future date और time zone टेस्ट प्लान करें: expiry, deadline, renewal, उम्र या schedule के नियम के लिए टेस्ट करने के पल और हर पल का अपेक्षित नतीजा पाएँ।
किसी तारीख़ में दिन, सप्ताह, महीने, वर्ष, घंटे और मिनट जोड़ें या घटाएँ।
दो तारीख़ों के बीच सटीक अंतर वर्ष, महीने, दिन, घंटे और कुल इकाइयों में निकालें।
तारीख़ और समय को अलग-अलग timezone के बीच बदलें (रियाद, अम्मान, दिल्ली, UTC, न्यूयॉर्क, लंदन…)।
Unix epoch सेकंड को पढ़ने योग्य तारीख़ में बदलें और मौजूदा Unix समय पाएँ।
तारीख़ों के डेटासेट जनरेट करें: किसी रेंज में रैंडम तारीख़ें, leap day, महीने के अंत, DST किनारे और कई फ़ॉर्मेट।
equivalence class और अपेक्षित परिणामों के साथ पूरा boundary value analysis (2-value और 3-value BVA) करें।
जाँचें कि कोई JWT एक्सपायर हो चुका है, अभी वैध नहीं है, या एक्सपायरी में कितना समय बचा है।