Program Call Duration dengan ADT time
Latihan membuat instruksi pembelajaran yang menggunakan ADT time untuk menyelesaikan program call duration, dalam implementasi bahasa pemrograman Rust.

Menggunakan ADT time
untuk:
Membaca N pasangan waktu (start, end) dari input interaktif.
Memvalidasi input waktu (jam 0–23, menit 0–59, detik 0–59) — jika tidak valid, minta ulang bacaan untuk waktu tersebut.
Untuk setiap pasangan, tentukan waktu mana yang lebih awal; hitung durasi (detik) sebagai
later - earlier
(hasil non-negatif).Tampilkan durasi (detik) untuk tiap record.
Setelah semua record, tampilkan jam mulai paling awal (earliest start) dan jam selesai paling akhir (latest end).
Tujuan Pembelajaran lebih rinci
Mengaplikasikan ADT
time
sebagai building block untuk ADT aplikasi nyata.Mengimplementasikan alur pembacaan data yang robust untuk input pengguna.
Menggunakan konversi
Time
⇄ detik untuk operasi perbandingan dan penghitungan durasi.Menangani dan menampilkan kasus-kasus tidak valid dengan ramah bagi pengguna.
Prasyarat
Telah mengimplementasikan
time.rs
(ADT time) atau setidaknya memahami API-nya:Time::new(h, m, s) -> Time
Time::is_valid(h, m, s) -> bool
Time::read_from_stdin()
atau sejenis (bisa menyesuaikan)Time::to_seconds() -> i32
Time::from_seconds(n: i32) -> Time
Time::display()
Dasar Rust:
mod
,use
,struct
,impl
,fn main()
, I/O sederhana (std::io
).
Desain Program, Alur Sederhana
Baca integer
N
(jumlah record). Jika input tidak valid atauN <= 0
, minta ulang.Inisialisasi variabel untuk menyimpan earliest_start (opsional:
Option<Time>
) dan latest_end (Option<Time>
).Untuk
i
dari 1 sampaiN
:Cetak penanda record
[{i}]
(seperti contoh interaksi).Baca
Time
pertama (loop sampai valid). Tampilkan pesan "Jam tidak valid" jika perlu.Baca
Time
kedua (loop sampai valid).Tentukan mana yang lebih awal (bandingkan
to_seconds()
).Hitung
duration_seconds = later - earlier
(non-negatif).Tampilkan
duration_seconds
.Perbarui earliest_start = min(earliest_start, earlier) dan latest_end = max(latest_end, later).
Setelah semua record:
Tampilkan earliest_start dan latest_end (format
HH:MM:SS
atauH:M:S
sesuai contoh).Selain itu Anda bisa menampilkan total durasi atau statistik lain (opsional).
Kode Implementasi yang mudah dibaca untuk pemula
Di bawah ini dua file: src/time.rs
(ADT Time) dan src/main.rs
(driver mtime). Jika Anda sudah punya time.rs
dari sebelumnya, cukup gunakan main.rs
. Saya sertakan time.rs
lengkap agar artikel ini self-contained.
File: src/time.rs
// src/time.rs
// ADT Time — sederhana dan mudah dimengerti.
// Fields private; API publik untuk konstruksi, validasi, konversi.
use std::io::{self, Write};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Time {
hours: i32, // 0..23
minutes: i32, // 0..59
seconds: i32, // 0..59
}
impl Time {
// Konstruktor sederhana (asumsi pemanggil memberikan nilai valid)
pub fn new(h: i32, m: i32, s: i32) -> Self {
Time { hours: h, minutes: m, seconds: s }
}
// Validator: periksa apakah masing-masing komponen ada pada rentang valid
pub fn is_valid(h: i32, m: i32, s: i32) -> bool {
(0..=23).contains(&h) && (0..=59).contains(&m) && (0..=59).contains(&s)
}
// Baca satu Time dari stdin; ulangi hingga valid.
// Format input: tiga bilangan bulat dipisah spasi, mis. "13 5 9"
// Fungsi ini menampilkan prompt dan pesan kesalahan, cocok untuk interaktif.
pub fn read_interactive(prompt: &str) -> Time {
loop {
print!("{}", prompt);
let _ = io::stdout().flush();
let mut line = String::new();
if io::stdin().read_line(&mut line).is_err() {
eprintln!("Gagal membaca input. Coba lagi.");
continue;
}
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.len() < 3 {
eprintln!("Mohon masukkan tiga bilangan: jam menit detik.");
continue;
}
let h = parts[0].parse::<i32>();
let m = parts[1].parse::<i32>();
let s = parts[2].parse::<i32>();
match (h, m, s) {
(Ok(h), Ok(m), Ok(s)) => {
if Time::is_valid(h, m, s) {
return Time::new(h, m, s);
} else {
eprintln!("Jam tidak valid. Jam 0-23, menit 0-59, detik 0-59.");
// sesuai contoh: jika invalid minta ulang
}
}
_ => {
eprintln!("Parsing gagal. Pastikan memasukkan tiga bilangan bulat.");
}
}
}
}
/// Cetak format H:M:S (tanpa leading zero agar mirip contoh)
pub fn display(&self) {
println!("{}:{}:{}", self.hours, self.minutes, self.seconds);
}
/// Konversi Time ke detik dari 00:00:00
pub fn to_seconds(&self) -> i32 {
self.hours * 3600 + self.minutes * 60 + self.seconds
}
/// Konversi detik ke Time (dalam rentang 0..23 jam)
pub fn from_seconds(mut n: i32) -> Self {
const DAY: i32 = 24 * 3600;
n = ((n % DAY) + DAY) % DAY; // buat positif dalam 0..DAY-1
let h = n / 3600;
let rem = n % 3600;
let m = rem / 60;
let s = rem % 60;
Time::new(h, m, s)
}
// Getter publik (jika diperlukan)
pub fn hours(&self) -> i32 { self.hours }
pub fn minutes(&self) -> i32 { self.minutes }
pub fn seconds(&self) -> i32 { self.seconds }
}
Catatan: fungsi
display()
menampilkan formatH:M:S
tanpa leading zeros agar hasil serupa contoh; Anda bisa menggantinya{:02}
jika ingin dua digit selalu.
File: src/main.rs
// src/main.rs
mod time;
use time::Time;
use std::io::{self, Write};
fn read_positive_int(prompt: &str) -> usize {
loop {
print!("{}", prompt);
let _ = io::stdout().flush();
let mut line = String::new();
if io::stdin().read_line(&mut line).is_err() {
eprintln!("Gagal membaca input. Coba lagi.");
continue;
}
match line.trim().parse::<isize>() {
Ok(n) if n > 0 => return n as usize,
_ => {
eprintln!("Masukkan integer positif (>0).");
}
}
}
}
fn main() {
// Baca banyaknya record N
let n = read_positive_int("Masukkan jumlah record N: ");
// earliest_start dan latest_end sebagai Option<Time>
let mut earliest_start: Option<Time> = None;
let mut latest_end: Option<Time> = None;
for i in 1..=n {
println!("[{}]", i);
// Baca jam mulai (loop sampai valid) — gunakan ADT Time
let prompt1 = "Masukkan jam mulai (jam menit detik): ";
let t1 = Time::read_interactive(prompt1);
// Baca jam selesai (loop sampai valid)
let prompt2 = "Masukkan jam selesai (jam menit detik): ";
let t2 = Time::read_interactive(prompt2);
// Tentukan mana lebih awal
let sec1 = t1.to_seconds();
let sec2 = t2.to_seconds();
let (start, end) = if sec1 <= sec2 { (t1, t2) } else { (t2, t1) };
// Durasi dalam detik (selalu non-negatif)
let duration = end.to_seconds() - start.to_seconds();
println!("{}", duration);
// Update earliest_start
earliest_start = match earliest_start {
None => Some(start),
Some(prev) => {
if start.to_seconds() < prev.to_seconds() { Some(start) } else { Some(prev) }
}
};
// Update latest_end
latest_end = match latest_end {
None => Some(end),
Some(prev) => {
if end.to_seconds() > prev.to_seconds() { Some(end) } else { Some(prev) }
}
};
}
// Setelah semua record, cetak earliest start dan latest end
println!();
if let Some(es) = earliest_start {
es.display();
} else {
println!("Tidak ada data start");
}
if let Some(le) = latest_end {
le.display();
} else {
println!("Tidak ada data end");
}
}
Penjelasan & Komentar Singkat bagian per bagian
Membaca N: fungsi
read_positive_int()
memastikan N > 0 (mengulang bila input salah). Ini sesuai asumsi soal bahwa N > 0 tapi program aman untuk pemula.Membaca Time:
Time::read_interactive()
memaksa pengguna memasukkan tiga angka; bila tidak valid (range) akan menampilkan pesan "Jam tidak valid" dan mengulang — sesuai contoh pada soal.Menentukan earlier/later: gunakan
to_seconds()
untuk membandingkan dua waktu dalam bentuk integer (detik). Dengan cara ini perbandingan mudah dipahami.Durasi:
duration = later - earlier
sehingga non-negatif.Tracking earliest/latest global:
earliest_start
diambil minimal dari semuastart
(jam lebih awal),latest_end
diambil maksimal dari semuaend
(jam paling akhir).Output akhir: menampilkan earliest_start dan latest_end dengan
display()
(formatH:M:S
).
Edge Cases (yang perlu dijelaskan kepada pemula)
Input non-angka — program akan menolak dan meminta ulang. Ini lebih ramah ketimbang panic.
Jam di luar rentang — ditolak dan diminta ulang sesuai soal.
T1 == T2 — durasi 0 detik (diterima).
T1 > T2 — program menukar peran sehingga durasi tetap positif. Ini sesuai soal: “Durasi harus positif, sehingga harus diperiksa mana yang lebih awal.”
N = 0 — fungsi membaca N memaksa input > 0 sehingga tidak terjadi. Namun jika Anda ingin mengizinkan N=0, sesuaikan fungsi pembacaan.
Overflow detik — memakai
i32
cukup untuk rentang jam sehari. Jika ingin menghitung selisih lintas hari, desain harus diubah (tapi soal ini tidak memerlukannya).Format final output — contoh soal menampilkan
1:0:0
bukan01:00:00
;display()
menyesuaikan untuk kemiripan itu. Jika membutuhkan leading zero, ubah formatprintln!("{:02}:{:02}:{:02}", ...)
.
Aktivitas Tambahan & Variasi (latihan untuk pembelajar)
Ubah program agar juga mencetak total durasi semua percakapan (jumlah semua durasi).
Ubah
Time::display()
agar selalu menggunakan dua digit (HH:MM:SS
).Ubah input agar menerima format
HH:MM:SS
(parsing dengansplit(':')
) selain format tiga angka.Simpan semua record ke
Vec<(Time, Time)>
lalu cetak sorted by start time.
Pertanyaan Refleksi dengan clue/jawaban singkat
Mengapa kita memetakan Time ke detik saat membandingkan waktu? Clue: detik = satuan paling kecil yang kita gunakan di ADT; perbandingan integer lebih mudah. Jawaban singkat: agar perbandingan dan penghitungan durasi menjadi operasi integer sederhana.
Kenapa kita meminta ulang pembacaan waktu jika input tidak valid? Clue: soal meminta agar durasi dihitung positif dan input valid. Jawaban singkat: untuk mencegah kalkulasi yang salah atau panic; lebih ramah pengguna.
Apakah solusi ini menangani lintas hari (mis. start 23:00, end 01:00)? Clue: saat ini
from_seconds
memetakan detik dalam satu hari; kita memilih earlier/later berdasarkan waktu pada hari yang sama. Jika percakapan bisa melewati tengah malam, perlu spesifikasi tambahan (mis. tanda hari atau asumsi durasi < 24 jam). Jawaban singkat: tidak secara eksplisit; jika percakapan melewati tengah malam, logika perlu diperluas.
Referensi
Sekolah Teknik Elektro dan Informatika, Institut Teknologi Bandung. Contoh ADT Time (materi perkuliahan dan contoh implementasi).
The Rust Programming Language — https://doc.rust-lang.org/book/ (untuk pemahaman
struct
,impl
, I/O).