Jumat, 30 Oktober 2026

Mengeja Angka

Halo! Kali ini, aku mau membahas tentang cara membuat komputer mengeja angka.

Ketika di tempat yang ada sistem antreannya, biasanya terdengar kalimat seperti ini, "Nomor antrean A-15 [a lima belas] harap datang ke loket 1 [satu]," atau seperti ini, "Panggilan bagi penumpang pesawat Garuda Indonesia dengan nomor penerbangan GA-410 [ge a empat satu nol] tujuan Denpasar. Silakan segera menuju pintu keberangkatan 5-A [lima a]."

Nah, apakah setiap kemungkinan bilangan itu direkam satu per satu? Tentu tidak, ya. Ada terlalu banyak kombinasi nomor atau kode yang mungkin sehingga akan memakan waktu, tenaga, dan biaya yang besar kalau harus merekam setiap kemungkinan. Yang dilakukan adalah merekam potongan demi potongan dan kemudian merangkai sesuai nomor atau kode yang diinginkan.

Untuk nama-nama kota/kabupaten tujuan, perekamannya tetap perlu satu per satu, tetapi cukup nama kotanya saja. Kalimat secara utuh juga direkam dengan merumpangkan bagian-bagian nomor penerbangan, tempat tujuan, dan nomor pintu.

Namun, untuk nama huruf dan nama angka, kita cukup merekam 36 kata (10 angka + 26 huruf) untuk menyusun banyak kombinasi nomor penerbangan dan nomor pintu. Bagaimana mengubah dari nomor atau kode tersebut menjadi bunyinya?

Pembahasan di bawah ini hanya ilustrasi. Penerapannya di lapangan bisa berbeda menyesuaikan keadaan dan keperluan.

Membuat Komputer Mengeja Angka

Berikut adalah definisi awal fungsi ejaAngka yang menerima satu parameter, yaitu teks yang berisi deretan angka yang hendak dieja. Alasan teks berupa String adalah agar yang bisa dibaca bisa cukup panjang dan tidak terbatas ukuran tipe data Number (sekitar 253 − 1, sekitar 9 kuadriliun). (Baca tulisanku sebelumnya, "Satuan Word dan Bita dalam Pemrograman" untuk mempelajari lebih lanjut.)

function ejaAngka(teks) {
    // Ini agar teks selalu bertipe data String.
    teks = String(teks);
    // ...
}

Selanjutnya, kita menyiapkan tabel untuk tiap angka.

const namaAngka = {
    "0": "nol",
    "1": "satu",
    "2": "dua",
    "3": "tiga",
    "4": "empat",
    "5": "lima",
    "6": "enam",
    "7": "tujuh",
    "8": "delapan",
    "9": "sembilan",
};

Untuk selanjutnya, kita akan memecah teks menjadi larik berisi tiap karakter, kemudian mengubahnya menjadi nama angka, dan menggabungkannya kembali dengan spasi.

teks = teks
    // Pecah menjadi larik berisi tiap karakter.
    .split("")
    // Ubah menjadi nama angka.
    .map(c => namaAngka[c])
    // Gabung kembali dengan spasi.
    .join(" ");

Berikut adalah kode lengkapnya.

// Eja angka dalam bahasa Indonesia
function ejaAngka(teks) {
    // Ini agar teks selalu bertipe data String.
    teks = String(teks);

    const namaAngka = {
        "0": "nol",
        "1": "satu",
        "2": "dua",
        "3": "tiga",
        "4": "empat",
        "5": "lima",
        "6": "enam",
        "7": "tujuh",
        "8": "delapan",
        "9": "sembilan",
    };

    teks = teks
        // Pecah menjadi larik berisi tiap karakter.
        .split("")
        // Ubah menjadi nama angka.
        .map(c => namaAngka[c])
        // Gabung kembali dengan spasi.
        .join(" ");

    return teks;
}

Contoh hasilnya: Nomor 410 menjadi ???.

Bagaimana dengan Huruf?

Sebenarnya, sih, ini cukup sederhana. Yang perlu dilakukan adalah menambahkan cara baca huruf-huruf ke dalam tabel yang sudah ada itu. Agar lebih ringkas, semua huruf diubah ke dalam bentuk huruf kecil (atau besar, pilih salah satu).

Selain itu, bisa juga ditambahkan pemeriksaan agar karakter yang tidak dikenal cukup ditulis ulang tanpa perubahan. Berikut contohnya.

.map(c => namaKarakter.hasOwnProperty(c) ? namaKarakter[c] : c)

Berikut adalah kode lengkapnya.

// Eja angka dan huruf dalam bahasa Indonesia
function ejaAngkaHuruf(teks) {
    // Ini agar teks selalu bertipe data String.
    teks = String(teks);
    // Teks diubah agar menggunakan huruf kecil.
    teks = teks.toLowerCase();

    const namaKarakter = {
        "0": "nol",
        "1": "satu",
        "2": "dua",
        "3": "tiga",
        "4": "empat",
        "5": "lima",
        "6": "enam",
        "7": "tujuh",
        "8": "delapan",
        "9": "sembilan",
        "a": "a",
        "b": "be",
        "c": "ce",
        "d": "de",
        "e": "e",
        "f": "ef",
        "g": "ge",
        "h": "ha",
        "i": "i",
        "j": "je",
        "k": "ka",
        "l": "el",
        "m": "em",
        "n": "en",
        "o": "o",
        "p": "pe",
        "q": "qi",
        "r": "er",
        "s": "es",
        "t": "te",
        "u": "u",
        "v": "ve",
        "w": "we",
        "x": "ex",
        "y": "ye",
        "z": "zet",
    };

    teks = teks
        // Pecah menjadi larik berisi tiap karakter.
        .split("")
        // Ubah menjadi nama karakter kalau ada.
        .map(c => namaKarakter.hasOwnProperty(c) ? namaKarakter[c] : c)
        // Gabung kembali dengan spasi.
        .join(" ");

    return teks;
}

Contoh hasilnya: Nomor GA-410 menjadi ???.

Penutup

Teknik yang dibahas kali ini hanya substitusi sederhana, yaitu mengubah karakter demi karakter menjadi nama karakternya. Hal ini sudah cukup untuk sebagian kasus. Namun, ada kasus-kasus lain yang perlu lebih kompleks. Mungkin untuk tulisanku selanjutnya, kita bisa membahas tentang cara mengubah 999 menjadi sembilan ratus sembilan puluh sembilan (alih-alih hanya sembilan sembilan sembilan). Perlu metode yang lumayan lebih kompleks daripada yang dibahas kali ini.

Selain itu, bila untuk pengumuman dalam bentuk suara, yang digabungkan adalah audio rekaman. Isi tabelnya berubah menjadi daftar audio rekaman untuk tiap karakter. Yang dibahas kali ini menggunakan teks sebagai contoh.

Sekian dahulu tulisanku kali ini. Semoga bermanfaat!

Minggu, 27 September 2026

Presenting Relative Time

Untuk versi bahasa Indonesia dari entri ini, klik di sini.

Hello! This time, I want to discuss about ways to present relative time.

To present time data, e.g. time of delivery and last modified time, usually we use relative time alongside absolute time. What are the differences?

Absolute time is a time which is based on a specific fixed event (epoch). For example, the 1 CE is based on Isa's/Jesus' birth PBUH. The 1 Hijri is based on the migration of Muhammad PBUH from Mecca to Medina.

Relative time is a time distance or time difference between two events, usually from an event to current time. For example, this post was published a few minutes ago. The October month is coming in 4 days.

One of the advantages of relative time is that there is no need to take time zone into account when understanding the meaning of the displayed date/time. For example, in contents targeting international readers, sometimes on different time zones are displayed to make it easier for readers. If there are supports for updating content dynamically, many will choose to show countdowns alongside its absolute time, one of the ways to present relative time.

Ways to Present Relative Time

There are many ways to present relative times. For example, the ones on websites or applications usually use minute unit as their smallest unit. The next ones are hours, days, weeks or months, and so on. The next unit is used after it reaches 1 unit, i.e. the hour unit after 60 minutes, the day unit after 24 hours, and so on.

It does not have to be that way. For relative time, I prefer if the next unit is used after it reaches 1.5 or 2 of the unit. For example, the hour unit after 90 or 120 minutes, the day unit after 36 or 48 hours, and so on.

Another thing is that there are those who round downward (floor), round upward (ceil), or round to the nearest (round). I choose to round downward.

The problem is that most libraries (1) use one unit as the limit for the next unit, (2) use weird limits in my opinion, or (3) round upward.

Because of that, this time, I want to make my own version as I want. What I want is a version which uses second as its smallest unit, uses the next unit after reaching 2 of the unit, and rounds downward.

Relative Time Conversion Function

I am going to use JavaScript language. One of the reasons is that this code can be tested in this very page.

Below is a basic definition of relativeTime function which accepts two parameters which are time of the event and epoch for calculating the relative time. The epoch parameter is optional and it defaults to current time at call.

function relativeTime(time, epoch) {
    if (epoch == undefined) {
        epoch = new Date();
    }
    // The diff value is in milliseconds.
    let diff = time.getTime() - epoch.getTime();
    // ...
}

The diff variable is for determining which text to be shown.

The first case is when the absolute value of diff is smaller than 1 the smallest unit being used.

// If diff is below 1 second,
if (Math.abs(diff) < 1000) {
    return "now";
}

For the next step, we get the sign of diff. After that, we make diff be always positive by using absolute function to make determining which unit to be used easier.

// A negative diff value means that the event is in the past.
const past = diff < 0;
diff = Math.abs(diff); // Absolute value

The next parts are repeated based on units used. There are three main steps: (1) converting diff to the next unit, (2) checking whether is below or equal to 2 of the unit, and (3) presenting based on past vs. non-past.

diff /= 1000; // Milliseconds to seconds
if (diff <= 2 * 60) {
    return past ? // "ago" vs. "in"
        Math.floor(diff) + " seconds ago" :
        "in " + Math.floor(diff) + " seconds";
}
// The above code is repeated for minutes, hours, and days.

Below is the complete code.

// Relative time in English
function relativeTime(time, epoch) {
    if (epoch == undefined) {
        epoch = new Date();
    }
    // The diff value is in milliseconds.
    let diff = time.getTime() - epoch.getTime();
    // If diff is below 1 second,
    if (Math.abs(diff) < 1000) {
        return "now";
    }
    // A negative diff value means that the event is in the past.
    const past = diff < 0;
    diff = Math.abs(diff); // Absolute value
    // Seconds //
    diff /= 1000; // Milliseconds to seconds
    if (diff <= 2 * 60) {
        return past ?
            Math.floor(diff) + " seconds ago" :
            "in " + Math.floor(diff) + " seconds";
    }
    // Minutes //
    diff /= 60; // Seconds to minutes
    if (diff <= 2 * 60) {
        return past ?
            Math.floor(diff) + " minutes ago" :
            "in " + Math.floor(diff) + " minutes";
    }
    // Hours //
    diff /= 60; // Minutes to hours
    if (diff <= 2 * 24) {
        return past ?
            Math.floor(diff) + " hours ago" :
            "in " + Math.floor(diff) + " hours";
    }
    // Days //
    diff /= 24; // Hours to days
    return past ?
        Math.floor(diff) + " days ago" :
        "in " + Math.floor(diff) + " days";
}

Example result: The publication of this post is when?

Final Words

Even though we can create our own relative function ourselves, we may not be able to apply it for applications created by other parties. Because of that, I hope that every application has a way to see absolute time alongside relative time.

Basically, I prefer to be able to see exact values instead of approximation if possible. Even this blog also chooses to show publication time using absolute time. Relative times usually shown usually lose their precisions. For example, absolute time is usually precise until seconds, meanwhile relative time usually loses its precision over time (minutes ago, hours ago, and so on).

That's all I write this time. Hope this helps!

Menyajikan Waktu Relatif

For English version from this entry, click here.

Halo! Kali ini, aku mau membahas tentang cara menyajikan waktu relatif.

Ketika ingin menyajikan data waktu, misal waktu pengiriman dan waktu terakhir diubah, biasanya kita menggunakan waktu relatif bersamaan dengan waktu absolut/mutlak. Apa bedanya?

Waktu absolut/mutlak adalah waktu yang didasarkan pada kejadian tertentu yang bersifat tetap (epoch). Misalnya, tahun 1 Masehi didasarkan pada kejadian kelahiran Nabi Isa a.s. Tahun 1 Hijriah didasarkan pada kejadian hijrah Nabi Muhammad saw. dari Makkah ke Madinah.

Waktu relatif adalah jarak waktu dari dua kejadian, biasanya dari suatu kejadian ke waktu saat ini. Misalnya, tulisan ini diterbitkan sekian menit yang lalu. Bulan Oktober akan tiba dalam 4 hari.

Salah satu keunggulan waktu relatif adalah tidak perlu memperhitungkan zona waktu saat memahami maksud dari tanggal/waktu yang ditampilkan. Misalnya, dalam konten-konten dengan target internasional, ditampilkan beberapa waktu dalam beberapa zona waktu untuk memudahkan para pembaca. Bila ada dukungan untuk pembaruan konten secara dinamis, banyak yang memilih untuk menampilkan hitung mundur juga selain waktu absolut/mutlak, salah satu cara menyajikan waktu relatif.

Cara Menyajikan Waktu Relatif

Ada banyak cara menyajikan waktu relatif. Sebagai contoh, yang ada di situs web atau aplikasi itu biasanya pakai satuan menit sebagai satuan terkecilnya. Selanjutnya adalah satuan jam, hari, pekan atau bulan, dan seterusnya. Satuan berikutnya dipakai setelah mencapai sebanyak satu satuannya, yaitu satuan jam setelah 60 menit, satuan hari setelah 24 jam, dan seterusnya.

Namun, ada juga cara lainnya. Untuk waktu relatif, aku lebih suka bila satuan berikutnya baru dipakai setelah mencapai sebanyak 1,5 atau 2 satuannya. Misalnya, satuan jam setelah 90 atau 120 menit, satuan hari setelah 36 atau 48 jam, dan seterusnya.

Selain itu, ada juga yang membulatkan ke bawah (floor), membulatkan ke atas (ceil), atau membulatkan ke terdekat (round). Aku memilih untuk membulatkan ke bawah.

Hanya saja, kebanyakan pustaka (library) yang ada itu (1) menggunakan batas satu satuan untuk berikutnya, (2) menggunakan batas-batas yang aneh menurutku, atau (3) membulatkan ke atas.

Oleh karena itu, kali ini, aku ingin membuat versiku sesuai keinginanku. Yang kuinginkan adalah versi yang menggunakan detik sebagai satuan terkecil, menggunakan satuan berikutnya setelah mencapai 2 satuannya, dan membulatkan ke bawah.

Fungsi Konversi Waktu Relatif

Aku akan menggunakan bahasa JavaScript. Salah satu alasannya adalah agar ini bisa dicoba langsung di halaman ini juga.

Berikut adalah definisi awal fungsi waktuRelatifID yang menerima dua parameter, yaitu waktu yang dimaksud dan acuan dalam menyajikan waktu relatif. Nilai acuan bersifat opsional dan bernilai bawaan waktu saat dipanggil.

function waktuRelatifID(waktu, acuan) {
    if (acuan == undefined) {
        acuan = new Date();
    }
    // Nilai beda dalam milidetik.
    let beda = waktu.getTime() - acuan.getTime();
    // ...
}

Nilai beda itu yang akan digunakan dalam menentukan yang akan ditampilkan.

Kasus pertama adalah bila nilai mutlak beda lebih kecil daripada satu satuan terkecil yang akan digunakan.

// Jika beda di bawah 1 detik,
if (Math.abs(beda) < 1000) {
    return "sekarang";
}

Untuk selanjutnya, kita akan mengambil tanda dari beda. Setelah itu, kita buat beda menjadi selalu positif dengan fungsi nilai mutlak untuk memudahkan dalam menentukan satuan yang akan digunakan.

// Nilai beda negatif berarti kejadiannya lampau.
const lampau = beda < 0;
beda = Math.abs(beda); // Nilai mutlak

Bagian-bagian berikutnya berulang mengikuti satuan-satuan yang digunakan. Ada tiga bagian utama: (1) konversi beda ke satuan berikutnya, (2) pemeriksaan nilai 2 satuannya, dan (3) menyajikan berdasarkan lampau vs. tidak lampau.

beda /= 1000; // Milidetik ke detik
if (beda <= 2 * 60) {
    return lampau ? // "yang lalu" vs. "dalam"
        Math.floor(beda) + " detik yang lalu" :
        "dalam " + Math.floor(beda) + " detik";
}
// Kode di atas diulangi untuk menit, jam, dan hari.

Berikut adalah kode lengkapnya.

// Waktu relatif dalam bahasa Indonesia
function waktuRelatifID(waktu, acuan) {
    if (acuan == undefined) {
        acuan = new Date();
    }
    // Nilai beda dalam milidetik.
    let beda = waktu.getTime() - acuan.getTime();
    // Jika beda di bawah 1 detik,
    if (Math.abs(beda) < 1000) {
        return "sekarang";
    }
    // Nilai beda negatif berarti kejadiannya lampau.
    const lampau = beda < 0;
    beda = Math.abs(beda); // Nilai mutlak
    // Detik //
    beda /= 1000; // Milidetik ke detik
    if (beda <= 2 * 60) {
        return lampau ?
            Math.floor(beda) + " detik yang lalu" :
            "dalam " + Math.floor(beda) + " detik";
    }
    // Menit //
    beda /= 60; // Detik ke menit
    if (beda <= 2 * 60) {
        return lampau ?
            Math.floor(beda) + " menit yang lalu" :
            "dalam " + Math.floor(beda) + " menit";
    }
    // Jam //
    beda /= 60; // Menit ke jam
    if (beda <= 2 * 24) {
        return lampau ?
            Math.floor(beda) + " jam yang lalu" :
            "dalam " + Math.floor(beda) + " jam";
    }
    // Hari //
    beda /= 24; // Jam ke hari
    return lampau ?
        Math.floor(beda) + " hari yang lalu" :
        "dalam " + Math.floor(beda) + " hari";
}

Contoh hasilnya: Tulisan ini diterbitkan kapan?

Penutup

Meskipun bisa membuat fungsi relatif sendiri, kita belum tentu bisa menerapkannya untuk aplikasi yang dibuat oleh pihak lain. Oleh karena itu, aku berharap bahwa tiap aplikasi memiliki cara tertentu untuk bisa melihat waktu absolut/mutlak selain melihat waktu relatif.

Pada dasarnya, aku lebih memilih bisa melihat nilai pasti daripada perkiraan jika memungkinkan. Blog ini pun juga memilih untuk menyajikan waktu penerbitan dengan waktu absolut/mutlak. Waktu relatif yang banyak disajikan itu biasanya kehilangan presisinya. Misalnya, waktu absolut/mutlak biasanya memiliki presisi hingga detik, sedangkan waktu relatif pada umumnya makin kurang presisi seiring waktu (menit yang lalu, jam yang lalu, dst.).

Cukup sekian tulisanku kali ini. Semoga bermanfaat!