CodeGym /Các khóa học /C# SELF /Các kiểu cơ bản để làm việc với thời gian

Các kiểu cơ bản để làm việc với thời gian

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

1. Làm quen với kiểu DateTime

Ngày và giờ là một viên gạch cơ bản của bất kỳ logic kinh doanh nào, đến mức không thể bỏ qua nó ngay cả trong những dự án đơn giản nhất. Thường xuyên cần biết khi nào user đăng ký, khi nào gửi mail, xử lý lịch trình chuyến bay, múi giờ — ở đâu cũng có ngày tháng! Nhưng thực tế cho thấy, làm việc với ngày và giờ là một trong những chủ đề "khó nhằn" nhất trong lập trình. 😅

C# cung cấp hai kiểu đặc biệt quan trọng để làm việc với thời gian: DateTimeDateTimeOffset. Nói ngắn gọn — một cái cho các tình huống đơn giản, cái còn lại cho các bài toán liên quan đến múi giờ và lưu trữ thời gian chính xác.

DateTime là gì?

Kiểu DateTime là một struct được định nghĩa trong namespace System. Nó dùng để lưu trữ ngày và giờ với độ chính xác đến 100 nano giây (1/10 tỷ giây).

  • Kiểu giá trị (struct), tức là copy theo giá trị.
  • Lưu cả ngày lẫn giờ.
  • Có thể lưu chỉ ngày (2025-05-24) hoặc ngày kèm giờ (2025-05-24 15:17:42).
  • "Không biết" về múi giờ, trừ khi bạn chỉ rõ.

2. Tạo DateTime

Có vài cách, mình sẽ nói mấy cái chính.

1. Ngày và giờ hiện tại

DateTime now = DateTime.Now;           // Giờ local của máy tính
DateTime utcNow = DateTime.UtcNow;     // Giờ theo Greenwich (UTC)
  • Now — là "giống như trên máy của bạn", tính theo múi giờ local của Windows.
  • UtcNow — "universal", tức là giờ phối hợp quốc tế. Rất tiện để lưu thời gian, ví dụ trong database hoặc các hệ thống chạy toàn cầu.

2. Constructors

// Năm, tháng, ngày
var date1 = new DateTime(2025, 5, 24);

// Năm, tháng, ngày, giờ, phút, giây
var date2 = new DateTime(2025, 5, 24, 12, 0, 0);

// Năm, tháng, ngày, giờ, phút, giây, mili giây
var date3 = new DateTime(2025, 5, 24, 12, 0, 0, 500);

Tháng không bắt đầu từ 0 đâu nhé! Tức là tháng 1 là 1, tháng 12 là 12.

3. Parse từ chuỗi

Rất thường ngày đến dưới dạng chuỗi! Ví dụ, user nhập ngày bằng tay.

string userInput = "2025-05-24 18:30";
DateTime parsed = DateTime.Parse(userInput); // Có thể ném exception!

Để parse an toàn — dùng TryParse:

string input = "2025-05-24 18:30";
if (DateTime.TryParse(input, out DateTime result))
{
    Console.WriteLine($"Chuyển đổi thành công: {result}");
}
else
{
    Console.WriteLine("Không thể chuyển chuỗi thành ngày.");
}

4. Lấy ngày "không có giờ"

DateTime today = DateTime.Today; // Ngày hiện tại, giờ = 00:00:00

Lấy các thành phần của ngày và giờ

Tất cả property có thể lấy rất nhanh:

var dt = new DateTime(2025, 5, 24, 15, 17, 42);

int year = dt.Year;         // 2025
int month = dt.Month;       // 5 (tháng 5)
int day = dt.Day;           // 24
int hour = dt.Hour;         // 15
int minute = dt.Minute;     // 17
int second = dt.Second;     // 42
DayOfWeek dow = dt.DayOfWeek; // Thứ sáu

3. Làm việc với kiểu DateTime

Cộng và trừ ngày giờ

Thời gian trong lập trình trôi nhanh lắm, để khỏi phải tính "còn bao nhiêu tiếng đến deadline", dùng mấy method của class này:

DateTime now = DateTime.Now;
DateTime tomorrow = now.AddDays(1); // Ngày mai
DateTime afterFiveHours = now.AddHours(5); // Sau 5 tiếng
DateTime inTenMinutes = now.AddMinutes(10); // Sau 10 phút

Hiệu của hai ngày: TimeSpan

Hiệu giữa hai ngày là một giá trị riêng, kiểu TimeSpan:

DateTime deadline = new DateTime(2026, 6, 1);
DateTime now = DateTime.Now;

TimeSpan untilDeadline = deadline - now;

Console.WriteLine($"Còn lại ngày đến deadline: {untilDeadline.Days}");
Console.WriteLine($"Còn lại giờ đến deadline: {untilDeadline.TotalHours:N2}");

TimeSpan — là "khoảng cách", không phải "ngày/giờ cụ thể"!

Kiểm tra ngày trong tương lai/quá khứ

if (deadline > now)
    Console.WriteLine("Deadline vẫn còn phía trước!");
else
    Console.WriteLine("Deadline qua rồi! Nộp bài gấp!");

4. Dưới "nắp ca-pô" ngày tháng được lưu thế nào

Để tiện cho việc cộng trừ ngày tháng, dev quyết định lưu tất cả ngày tháng dưới dạng số giây đã trôi qua từ đầu kỷ nguyên của chúng ta. Giá trị giới hạn:

Thuộc tính Giá trị
Ngày nhỏ nhất
DateTime.MinValue
= 0001-01-01 00:00:00
Ngày lớn nhất
DateTime.MaxValue
= 9999-12-31 23:59:59.9999999
Đơn vị đo 1 tick = 100 nano giây

DateTime không chỉ lưu số giây, mà là số "tick" (ticks) kể từ một ngày đặc biệt — 1 tháng 1 năm 0001 (ừ thì, dev không biết khủng long là gì, lịch của họ nghiêm ngặt lắm). Một tick = 100 nano giây.

5. Kiểu DateTimeOffset

Đôi khi DateTime là chưa đủ. Lấy ví dụ kinh điển: công ty hoạt động ở hai quốc gia, mỗi nơi một múi giờ, mà tất cả cuộc họp đều lên lịch "theo giờ của tôi". Làm sao bây giờ?

Thời gian lưu bằng DateTime thường là "trừu tượng": chỉ là một dãy số và tick, trừ khi bạn chỉ rõ nó thuộc múi giờ nào. Ví dụ, nếu bạn nhận được "2025-05-24 15:00", bạn không thể chắc — đó là giờ Nairobi? Baghdad? Berlin? (mà nếu còn daylight saving nữa thì xác định luôn!).

Vì vậy, cho các bài toán cần thời điểm chính xác, không chỉ là "ngày trên lịch", có thêm kiểu thứ hai — DateTimeOffset. Tạo DateTimeOffset với offset (ví dụ, Nairobi +3 so với UTC):


var localTime = new DateTimeOffset(2025, 5, 24, 15, 0, 0, TimeSpan.FromHours(3));

Dùng DateTimeOffset nếu:

  • Cần biết thời điểm chính xác (ví dụ, cho event trong log, gắn với giờ server).
  • App làm việc với user ở nhiều múi giờ khác nhau.
  • Cần lưu/truyền thời gian kèm múi giờ (ví dụ, cho giao dịch, event, lịch biểu).

6. Làm sao tạo DateTimeOffset?

  1. Thời điểm hiện tại
    DateTimeOffset now = DateTimeOffset.Now; // Giờ local + offset
    DateTimeOffset utc = DateTimeOffset.UtcNow; // UTC (offset 0)
    
  2. Tạo rõ ràng
    // Năm, tháng, ngày, giờ, phút, giây, offset (ví dụ, +2 tiếng)
    DateTimeOffset custom = new DateTimeOffset(2025, 5, 24, 20, 0, 0, TimeSpan.FromHours(2));
    
  3. Chuyển từ DateTime
    DateTime dt = DateTime.Now;
    DateTimeOffset dto = new DateTimeOffset(dt); // Offset local sẽ tự động lấy
    

Lấy các thành phần

DateTimeOffset dto = DateTimeOffset.Now;
var datePart = dto.Date;      // Chỉ ngày
var timePart = dto.TimeOfDay; // Chỉ giờ
var offset = dto.Offset;      // Offset so với UTC, ví dụ, 03:00:00

Chuyển đổi giữa các múi giờ

Ma thuật của offset:

// Lấy ngày ở UTC
var now = DateTimeOffset.Now;
var utc = now.ToUniversalTime();
Console.WriteLine(utc); // Giờ Greenwich

// Chuyển sang múi giờ khác
var berlinTime = utc.ToOffset(TimeSpan.FromHours(2));
Console.WriteLine(berlinTime); // Cho Berlin (CET +2)

7. Khi nào dùng DateTime, khi nào dùng DateTimeOffset?

Triết lý chọn lựa

Khi nói về cái gì đó đơn giản và local — ví dụ, ngày sinh nhật, ngày đăng bài, hoặc deadline khảo sát đến 15:00, thường chỉ cần DateTime. Trong mấy trường hợp này, thời gian tuyệt đối không quan trọng, chỉ cần biết sự kiện xảy ra vào ngày giờ nào, không cần quan tâm múi giờ.

Nhưng nếu cần ghi nhận thời điểm chính xác kèm múi giờ, ví dụ, trong log, giao dịch hoặc hệ thống phân tán — nên dùng DateTimeOffset. Nó sẽ không làm bạn rối khi truyền thời gian giữa các hệ thống, nhất là khi chúng ở các múi giờ khác nhau.

Tình huống Kiểu Lý do
Sinh nhật
DateTime
Chỉ cần ngày, không quan tâm múi giờ (thường là vậy)
Thời gian đăng nhập
DateTimeOffset
Cần biết thời điểm chính xác và offset
Hạn sử dụng sản phẩm
DateTime
Ngày hết hạn, không quan tâm múi giờ
Thời gian event trong log
DateTimeOffset
Nên luôn lưu kèm offset
2
Nhiệm vụ
C# SELF, mức độ, bài học
Đã khóa
Hiển thị ngày và giờ hiện tại theo nhiều định dạng khác nhau
Hiển thị ngày và giờ hiện tại theo nhiều định dạng khác nhau
2
Nhiệm vụ
C# SELF, mức độ, bài học
Đã khóa
Cộng và trừ thời gian
Cộng và trừ thời gian
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION