suideveloper
All questions
Objects

How do I get the current time in Sui Move?

November 10, 2026

To read the current time in Sui Move, add a &Clock parameter to your function and call clock.timestamp_ms(), which returns Unix time in milliseconds as a u64. Clock is a system shared object at address 0x6. You pass it in as an argument, and you can only take it by immutable reference. If you only need a coarse timestamp and want to stay on the owned-object fast path, use ctx.epoch_timestamp_ms(), which returns the time the current epoch started.

Using sui::clock::Clock

Move
module example::timer;

use sui::clock::Clock;
use sui::event;

public struct TimeEvent has copy, drop {
    timestamp_ms: u64,
}

entry fun now(clock: &Clock) {
    event::emit(TimeEvent { timestamp_ms: clock.timestamp_ms() });
}

The same pattern works in public functions, for example a deadline check:

Move
const EExpired: u64 = 0;

public fun assert_not_expired(deadline_ms: u64, clock: &Clock) {
    assert!(clock.timestamp_ms() < deadline_ms, EExpired);
}

A system transaction updates the clock at every consensus commit, which the docs put at roughly every quarter second. Within one transaction, every call to timestamp_ms returns the same value. Across transactions, the value never goes backwards.

Passing the clock from a transaction

From the CLI, pass 0x6:

Terminal
sui client call --package <PACKAGE_ID> --module timer --function now --args 0x6

From TypeScript (@mysten/sui v2), use the tx.object.clock() helper:

TypeScript
import { Transaction } from '@mysten/sui/transactions'

const tx = new Transaction()
tx.moveCall({
  target: `${PACKAGE_ID}::timer::now`,
  arguments: [tx.object.clock()],
})

Epoch timestamps: the fast-path alternative

Because Clock is a shared object, any transaction that uses it goes through consensus. If you don't need sub-second precision, TxContext has the start time of the current epoch:

Move
public fun epoch_started_at(ctx: &TxContext): u64 {
    ctx.epoch_timestamp_ms()
}

This value only changes at epoch boundaries, roughly every 24 hours on mainnet (see Sui epochs). It suits things like "claimable after N epochs". It doesn't suit auctions or anything that needs minute-level precision.

Testing time-dependent code

You can't use the real 0x6 object in unit tests. The framework has test-only helpers instead:

Move
#[test]
fun deadline_passes() {
    let mut ctx = tx_context::dummy();
    let mut clock = sui::clock::create_for_testing(&mut ctx);

    clock.set_for_testing(1_000);
    assert!(clock.timestamp_ms() == 1_000);
    clock.increment_for_testing(500);
    assert!(clock.timestamp_ms() == 1_500);

    clock.destroy_for_testing();
}

For epoch-based logic, test_scenario::later_epoch(scenario, delta_ms, sender) moves the epoch forward and adds delta_ms to the epoch timestamp. See Move unit tests for the test setup.

Gotchas

  • Taking &mut Clock or Clock by value doesn't work. The docs say packages with entry functions that do this fail to publish, and validators won't sign transactions that pass the clock any other way.
  • Every transaction that touches 0x6 becomes a consensus transaction. See shared vs owned objects for what that costs.
  • Timestamps are milliseconds, not seconds. Keep deadlines in ms as well.
  • Don't use the timestamp as a source of randomness. Use sui::random.

Sources

More on objects