CodeGym /Các khóa học /Kotlin SELF /Lam quen voi ngay thang

Lam quen voi ngay thang

Kotlin SELF
Mức độ, Bài học
Có sẵn

1. Gioi thieu

Gan nhu ai cung bat dau giong nhau: “ngay thang da den duoi dang van ban — vay thi toi se luu no duoi dang van ban”. Va dung that, nguoi dung nhap 2026-01-06, trong file no cung la chuoi, trong JSON cung la chuoi… vay tai sao khong giu nguyen nhu the trong code? O cho nay nhieu chuong trinh se mim cuoi, gat dau va… am tham chuan bi nhung bat ngo cho ban tren production.

Van de nam o cho chuoi chi la mot tap ky tu. Trinh bien dich Kotlin chi thay String la van ban. No khong biet ben trong la “ngay thang”, khong kiem tra gioi han, khong biet cong “them mot ngay”, khong dam bao so sanh “som hon/muon hon” cho dung. Ket qua la ban co tinh huong: du lieu trong nhu ngay thang, nhung hanh vi lai nhu van ban thong thuong (vi no dung la van ban thong thuong).

Hay phan tich ba nhom van de lon:

  1. so sanh chuoi khong giong so sanh ngay thang;
  2. chuoi khong dam bao tinh hop le cua ngay thang;
  3. dinh dang co the mo ho, va “cung mot” chuoi co the mang y nghia la cac ngay khac nhau.

2. So sanh chuoi: vi sao ngay thang thanh “tu”

Khi ta so sanh chuoi, Kotlin so sanh theo thu tu tu dien (lexicographic): dai khai giong nhu tu trong tu dien — tu trai sang phai theo ky tu. Hanh vi nay duoc tich hop trong String va nhin chung trong thu tu so sanh cac doi tuong co “thu tu tu nhien”. Voi chuoi, thu tu do chinh la lexicographic.

Va day la luc bat dau co “ma thuat” kieu: “tai sao ke toan cua toi lai quyet dinh rang thang 2 som hon thang 1”.

Vi du: hai dinh dang “dep”, mot bug

Trong nhu ngay, nhung duoc so sanh nhu van ban:

fun main() {
    val a = "01.02.2026"
    val b = "12.01.2026"

    println(a < b) // true (nhung theo lich thi 01.02.2026 muon hon 12.01.2026)
}

Vi sao ra true? Vi chuoi "01.02.2026" bat dau bang '0', con "12.01.2026" bat dau bang '1'. Voi chuoi, nhu vay da du: '0' < '1', nen ca chuoi nho hon.

Neu trong du an ban sap xep danh sach su kien theo cac chuoi nhu vay — ban se nhan duoc “mot dong hon loan duoc sap xep dep mat”.

“Nhung toi dung ISO format — o do thi on ma!”

Dung, co mot dinh dang tinh co hop tac tot voi so sanh lexicographic — yyyy-MM-dd (nam-thang-ngay). No thuong co hanh vi “dung” khi so sanh chuoi — nhung dieu do khong bien no thanh kieu du lieu ngay thang.

fun main() {
    val iso1 = "2026-01-06"
    val iso2 = "2026-12-31"

    println(iso1 < iso2) // true (va o day trung voi lich)
}

Vi sao trung? Vi cac phan quan trong hon (nam, sau do thang, sau do ngay) nam o ben trai. Nhung day van la so sanh van ban. Chi can lech nhe khoi dinh dang — va “ma thuat” se bi pha.

Bay nho: “khong co so 0 dan dau” la vo tran

Tinh huong rat hay gap: ai do quyet dinh luu thang la 1 thay vi 01. Ben ngoai thi “co gi khac nhau, con nguoi van hieu ma”. Con sap xep — se khong hieu.

fun main() {
    val d1 = "2026-1-10"
    val d2 = "2026-01-11"

    println(d1 < d2) // false (nhung theo lich thi 10 thang 1 som hon 11 thang 1)
}

Theo lexicographic, "2026-1-10" khong duoc so sanh giong "2026-01-10". Va neu ban nghi “chung toi luc nao cung se viet dung”, thi toi co tin moi: mot ngay nao do nguoi dung se lam. Hoac dong nghiep cua ban. Hoac chinh ban vao toi thu Sau.

3. Tinh hop le: chuoi cho phep “2026-13-40” va khong he nguong

Sau so sanh thuong se den cu dam thu hai: chuoi khong dam bao rang ngay do ton tai.

fun main() {
    val s = "2026-13-40"
    println(s) // 2026-13-40 (trong ra rat tu tin, song rat ngang, nhung khong ton tai)
}

Chuoi khong biet rang thang phai tu 1 den 12, rang thang 2 khong phai luc nao cung co 29 ngay, rang 31 thang 6 la chuyen vien tuong. Day khong phai “loi Kotlin”. Day la dung: String khong co nghia vu phai la lich.

“The thi toi tu validate bang split!”

Nghe rat hao hung. Thuc te ban se nhanh chong phat hien “ngay” khong chi la ba con so, ma la ba con so cong them ca dong quy tac.

Day la vi du cuc ky don gian hoa (va co y de thieu) cho kiem tra ISO:

fun isIsoDateRough(s: String): Boolean {
    val parts = s.split("-")
    if (parts.size != 3) return false

    val year = parts[0].toIntOrNull() ?: return false
    val month = parts[1].toIntOrNull() ?: return false
    val day = parts[2].toIntOrNull() ?: return false

    return year >= 1 && month in 1..12 && day in 1..31
}

Ham nay co ich nhu mot bac thang hoc tap, nhung no de lot rat nhieu ngay “bat kha thi”: 2026-02-31, 2026-04-31, 2026-11-00, v.v. De lam dung that su, ban se phai trien khai quy tac lich, nam nhuan, so ngay cua tung thang… va den mot luc ban se nhan ra minh dang phat minh lai lich. Thuong thi day la mot giao dich te.

Vi sao “regex” cung khong cuu duoc

Ban co the viet Regex("""\d{4}-\d{2}-\d{2}"""), va no chi dam bao rang chuoi trong giong 2026-13-40. Nghia la regex kiem tra “co giong ngay khong”, chu khong kiem tra “co phai ngay that khong”.

No giong nhu kiem tra ho chieu theo mau “hai chu cai va sau chu so”: trong co ve dung, nhung khong chac ton tai.

4. Dinh dang: “01.02.2026” — la 1 thang 2 hay 2 thang 1?

Van de lon thu ba — su mo ho dinh dang. Con nguoi (va chuong trinh) phai thong nhat y nghia cac phan cua ngay.

Khi ban thay 01.02.2026, no co the la:

  • 1 thang 2 nam 2026 (dinh dang dd.MM.yyyy, pho bien o chau Au),
  • 2 thang 1 nam 2026 (dinh dang MM.dd.yyyy, pho bien trong doi song o My).

Va dieu kho chiu nhat: ca hai ngay deu hop le, nghia la ban khong bat duoc loi bang cac kiem tra don gian.

fun main() {
    val s = "01.02.2026"
    println(s) // chuoi chi mot, nhung y nghia it nhat hai
}

Thoi quen tot: tach luu tru va hien thi

Ke ca neu tam thoi ban buoc phai luu ngay duoi dang chuoi (vi du, giai doan dau cua du an), van rat huu ich neu thong nhat:

  • ben trong chuong trinh, ngay duoc luu theo mot dinh dang nghiem ngat duy nhat (thuong la ISO yyyy-MM-dd),
  • hien thi cho nguoi dung theo cach quen thuoc (vi du dd.MM.yyyy),
  • dau vao cua nguoi dung duoc chuan hoa (hoac it nhat validate) ngay tu luc nhap.

Day chua phai giai phap cuoi cung (ta van chua dung kieu ngay), nhung da la mot buoc lon huong toi tinh hop ly.

5. Mini-case: chi tieu co ngay mua, nhung “khong sap xep duoc”

Hay tuong tuong ta co mot ung dung console don gian “Expense Tracker”. Ta da biet luu danh sach chi tieu trong MutableList, biet them phan tu va in bao cao. Va roi co mong muon rat logic: them ngay mua hang.

Phien ban mo hinh du lieu cho nguoi moi thuong trong nhu sau:

data class Expense(
    val title: String,
    val amount: Int,
    val date: String // "nguoi dung nhap the nao thi luu the do"
)

Va tiep theo ban muon lam “hien thi chi tieu theo ngay” hoac “sap xep theo ngay”.

Sap xep theo chuoi: chay… cho den khi no khong chay nua

data class Expense(val title: String, val amount: Int, val date: String)

fun main() {
    val expenses = listOf(
        Expense("Ca phe", 250, "12.01.2026"),
        Expense("Di chuyen", 70, "02.02.2026"),
        Expense("Pizza", 900, "05.01.2026"),
    )

    val sorted = expenses.sortedBy { it.date }
    println(sorted.joinToString("\n"))
}

Rat co kha nang ban se nhan duoc thu tu trong co ve “ngau nhien” theo goc nhin lich. Vi sortedBy sap xep theo thu ban dua vao — theo chuoi — va chuoi duoc so sanh lexicographic.

“Thoi duoc, se luu ngay theo ISO string”

Cach nay tot hon dd.MM.yyyy, vi ISO thuong sap xep dung khi xem nhu text. Hay “cai thien” model, nhung van giu chuoi:

data class Expense(val title: String, val amount: Int, val isoDate: String)

fun main() {
    val expenses = listOf(
        Expense("Ca phe", 250, "2026-01-12"),
        Expense("Di chuyen", 70, "2026-02-02"),
        Expense("Pizza", 900, "2026-01-05"),
    )

    val sorted = expenses.sortedBy { it.isoDate }
    println(sorted.map { it.isoDate + " " + it.title }.joinToString("\n"))
    // 2026-01-05 Pizza
    // 2026-01-12 Ca phe
    // 2026-02-02 Di chuyen
}

Trong nhu ban da thang. Nhung can tu nhan thang than: ta chi don gian chon mot dinh dang “che” duoc van de so sanh. Tinh hop le van khong duoc dam bao, phep toan “+ 1 ngay” van phai tu lam, va “thoi diem su kien” (neu can) se lap tuc tao ra nhung noi dau moi.

6. Vi sao chuoi la mo hinh noi bo te

O phan nay nen cham lai mot chut va dong goi mot y tuong se theo ban ca su nghiep: kieu du lieu noi bo cua chuong trinh phai phan anh y nghia cua du lieu. Dau vao va dau ra gan nhu luon la chuoi, nhung ben trong chuong trinh nen la cac thuc the co y nghia.

Chuoi tien o ba noi:

  • nguoi dung nhap (readln() luon cho String);
  • xuat ra man hinh (con nguoi can van ban);
  • luu tru/trao doi (file, JSON, giao thuc mang).

Nhung ngay khi ban bat dau lam “logic” — so sanh, sap xep, tinh toan, kiem tra dung/sai — chuoi se thanh cai bay.

Một bang nho: ngay duoi dang chuoi te o diem nao

Nhiem vu Neu ngay la
String
Dieu gi xay ra sai
So sanh “som/muon”
a < b
so sanh lexicographic, phu thuoc dinh dang
Kiem tra tinh dung
split
,
Regex
de bo sot ngay khong the ton tai
Cong “+ 1 ngay” tu lam can quy tac lich, thang dai ngan khac nhau
Ho tro nhieu dinh dang “neu co dau cham thi mot kieu, neu co dau gach thi kieu khac” code phong to va de vo
Xu ly loi dinh dang exception/
null
de “lam sap” chuong trinh neu khong xu ly can than

Cach bug hay sinh ra: “chuoi da ro ri vao logic”

De ghi nho, hay nhin no nhu mot dong chay du lieu. Gan nhu luon luon loi xuat hien khi chuoi (lop dong goi) tro thanh logic noi bo.

flowchart TD
    A[Nguoi dung nhap ngay duoi dang chuoi] --> B[Ta luu String 'nguyen trang']
    B --> C[Sap xep / so sanh / tinh toan]
    C --> D[Bi bug: thu tu sai hoac ngay khong ton tai]
    D --> E[Bat dau sua bang cac cach chua chay: split, regex, if-else]
    E --> C

Vong lap kin: cang nhieu “chua chay”, code cang phuc tap va cang de phat sinh bug moi.

7. Nen lam gi ngay bay gio neu chua co kieu ngay

Quan trong: hom nay ta chua dua vao cac kieu ngay/gio. Nhung ta co the ngay bay gio dua ra mot vai quyet dinh co tinh ky luat, de du an khong bien thanh “dia nguc parser lich” dai nam nghin dong.

Tap trung hoa quy tac dinh dang: mot ham chuan hoa duy nhat

Hay lam mot util nho: no nhan vao chuoi, co gang dua ve ISO yyyy-MM-dd, neu khong duoc thi tra ve null. Day chua phai validate lich “dung 100%”, nhung da la thuc hanh tot: quy tac nam mot cho, contract ro rang.

fun normalizeIsoDateOrNull(input: String): String? {
    val s = input.trim()
    val parts = s.split("-")
    if (parts.size != 3) return null

    val year = parts[0].toIntOrNull() ?: return null
    val month = parts[1].toIntOrNull() ?: return null
    val day = parts[2].toIntOrNull() ?: return null

    if (month !in 1..12) return null
    if (day !in 1..31) return null

    val mm = month.toString().padStart(2, '0')
    val dd = day.toString().padStart(2, '0')
    return "${year}-$mm-$dd"
}

Dung, van con don gian hoa “ngay toi da 31”. Nhung y tuong rat co ich: chuan hoa dinh dang va tra ve null thay vi lam chuong trinh crash.

Dung pattern “doc → chuan bi → kiem tra → luu”

Pattern nay ban da dung cho so. Voi chuoi ngay thang no cung ap dung duoc:

fun main() {
    print("Nhap ngay mua hang (yyyy-M-d): ")
    val raw = readln()

    val iso = normalizeIsoDateOrNull(raw)
    if (iso == null) {
        println("Ngay khong hop le: '$raw'") // thong diep ro rang
        return
    }

    println("Luu ngay theo dinh dang ISO: $iso") // vi du: 2026-01-06
}

Ta chua “chua” lich 100%, nhung ta dung luu “bat cu thu gi”.

Ngay tren lich va thoi diem trong thoi gian la hai thuc the khac nhau

Trong giao tiep hang ngay, “ngay/gio” co ve nhu mot. Trong lap trinh, day la it nhat hai thuc the khac nhau:

  • ngay tren lich: “6 thang 1 nam 2026” (khong co gio trong ngay);
  • thoi diem: “2026-01-06 10:15:30” (doi khi con co ca mui gio).

Va neu ban luu tat ca bang chuoi, ban dang tron lan cac y nghia nay. Roi ban se ngac nhien vi sao “su kien luc 10:00” tu nhien thanh “gio khac” tren may khac, hoac vi sao “dau ngay” khong the tinh dung bang cach cong them giay don thuan.

Hom nay muc tieu cua ta khong phai giai quyet tron ven, ma la thay ro vi sao String lam mo hinh noi bo la con duong bi that bai. Trong cac bai sau, ta se tiep can giai phap bang cac kieu ngay va thoi gian, noi trinh bien dich se bat dau giup ban, thay vi im lang dung nhin “chay nha”.

8. Loi thuong gap khi lam viec voi ngay duoi dang chuoi

Loi so 1: so sanh ngay bang toan tu <, >, sorted() theo chuoi “nguoi dung nhap the nao”.
Dieu nay gan nhu luon tao ra thu tu lexicographic, phu thuoc dinh dang va rat de vo khi thieu so 0 dan dau, doi dau phan cach, hoac tron nhieu phong cach. Ke ca “co ve chay” tren du lieu test, do chi la may man mong manh, khong phai bao dam.

Loi so 2: coi ISO string la “ngay thang day du” va lam toan hoc tren no.
ISO format thuc su tien cho luu tru va trao doi, va thuong “sap xep dung” khi xem nhu text. Nhung ban van chua the lam an toan “+ 1 ngay” hoac “chenh lech giua hai ngay” neu khong tu viet tay mot nua cai lich.

Loi so 3: validate ngay bang regex hoac split va nghi rang the la on.
Kiem tra hinh thuc (\d{4}-\d{2}-\d{2}) khong kiem tra y nghia. Rat de chap nhan 2026-13-40 hoac 2026-02-31 nhu “ngay binh thuong” neu chi kiem tra so chu so va dau phan cach.

Loi so 4: tron dinh dang trong cung mot du an ma khong co contract ro rang.
Khi mot phan code mong doi dd.MM.yyyy, phan khac mong doi yyyy-MM-dd, va phan khac thi “nhan sao luu vay”, chuong trinh se bat dau “thinh thoang chay dung”. Day la che do te nhat: khong crash ngay, ma am tham lam hong du lieu va bao cao.

Loi so 5: rai rac format khap code thay vi gom lai 1–2 ham.
Neu o 10 noi ban viet "$day.$month.$year" va o 5 noi ban parse bang split("."), thi chi can yeu cau doi (vi du muon dd/MM/yyyy) la ban se co mot chuyen “safari” vui ve trong du an. Ham format va chuan hoa tap trung la thoi quen don gian, giup tiet kiem hang gio.

Loi so 6: khong xu ly “dau vao xau” va hy vong nguoi dung luon nhap dung.
Moi parse va moi validate deu co the ket thuc bang loi. Day khong phai “truong hop hiem”, ma la mot nhanh chay binh thuong. Neu ban khong tinh den no trong logic (qua null, runCatching, try/catch), chuong trinh se hoac crash, hoac luu rac — va ca hai deu buon.

1
Nhiệm vụ
Kotlin SELF, mức độ, bài học
Đã khóa
Ngày gây tranh cãi
Ngày gây tranh cãi
1
Nhiệm vụ
Kotlin SELF, mức độ, bài học
Đã khóa
Lịch chuẩn
Lịch chuẩn
1
Nhiệm vụ
Kotlin SELF, mức độ, bài học
Đã khóa
Ngày tương tự
Ngày tương tự
1
Nhiệm vụ
Kotlin SELF, mức độ, bài học
Đã khóa
Ngày du khách
Ngày du khách
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION