Chuyển đến nội dung chính

.NET 6: DateOnly và TimeOnly

Trước C# 10, để lưu trữ data thời gian, bạn sẽ dùng DateTime. Đây là kiểu vừa lưu trữ được Date vừa lưu trữ được Time.

Vấn đề xảy ra là có rất nhiều kiểu dữ liệu liên quan tới thời gian nhưng không sử dụng tới Time như: birthday, anniversary …

Và cũng có nhiều trường hợp bạn muốn lưu trữ tới Time mà không muốn kèm Date phía trước. 

Ví dụ như dữ liệu chứng khoán, nếu data trong ngày quá lớn, mà bạn lưu trữ hàng triệu record với 1 date hơi dư thừa, chắc chắn sẽ làm chậm trong quá trình xử lý hoặc tốn không gian lưu trữ.

Vấn đề này đã được giải quyết. .NET 6/C# 10 đã giới thiệu 2 cấu trúc mới là DateOnly và TimeOnly.

DateOnly

public DateOnly (int year, int month, int day);

VD:

var dateOnly = new DateOnly(2022, 1, 1);
Lưu ý: Đối tượng DateOnly không chứa Timezone.

TimeOnly

public TimeOnly (int hour, int minute);
VD:
TimeOnly time = TimeOnly.MinValue;
Console.WriteLine(time); //Outputs 12:00 AM
Giả sử bạn khai báo biến startTime = 11PM và cộng thêm 2h.
TimeOnly startTime = TimeOnly.Parse("11:00 PM");
var hoursWorked = 2;
var endTime = startTime.AddHours(hoursWorked);
Console.WriteLine(endTime); //Outputs 1:00 AM
Đối với TimeSpan, bạn sẽ ra kết quả 1 ngày 1h Ngoài ra có 1 hàm vô cùng hữu ích IsBetween(), để xem 1 đối tượng TimeOnly có nằm trong khoảng thời gian xác định hay không.
TimeOnly startTime = TimeOnly.Parse("11:00 PM");
var hoursWorked = 2;
var endTime = startTime.AddHours(hoursWorked);
var isBetween = TimeOnly.Parse("12:00 AM").IsBetween(startTime, endTime); //Returns true. 

Sử dụng DateOnly và TimeOnly với SQL Server

Theo dự kiến, tới .NET 7, Micorosoft sẽ hỗ trợ binding DateOnly và TimeOnly với SQL Server. 

Như mình ví dụ lúc đầu, sẽ rất tốn thời gian và dung lượng để lưu trữ DateTime xuống SQL Server khi đối tượng đó chỉ cần Date hoặc Time. 

Để giải quyết vấn đề đó, mình sử dụng thư viện TinyHelpers: 

https://github.com/marcominerva/TinyHelpers

Để sử dụng thư viện, đầu tiên bạn cần cài đặt TinyHelpers từ Nuget. Sau đó trong DbContext, bạn override lại như ví dụ sau:

// using TinyHelpers.EntityFrameworkCore.Extensions;
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Post>(builder =>
    {           
        // Date is a DateOnly property (.NET 6)
        builder.Property(x => x.Date).HasDateOnlyConversion();
    
        // Time is a TimeOnly property (.NET 6)
        builder.Property(x => x.Time).HasTimeOnlyConversion();
   
        // Comments is a complex type, this Converter will automatically JSON-de/serialize it
        // in a string column.
        builder.Property(x => x.Comments).HasJsonConversion();
    });
}

Ví dụ

Bạn viết chương trình để lưu thông tin người dùng và thời gian ngủ-thức dậy của người đó hàng ngày. 


Tạo 2 bảng People và WakeupTime sử dụng DateOnly và TimeOnly 

  • Birthday: DateOnly 
  • BedTime: TimeOnly 
  • WakeupTime: TimeOnly 
Khai báo entity Person
public class Person
{
	public int Id { get; set; }
	public string Name { get; set; }
	public DateOnly Birthday { get; set; }
	public List<WakeUpTime> WakeUpTimes { get; set; }
}
Khai báo entity WakeupTime
public class WakeUpTime
{
	public int Id { get; set; }
	public int PersonId { get; set; }
	public TimeOnly WakeupTime { get; set; }
	public TimeOnly BedTime { get; set; }
	[ForeignKey("PersonId")]
	public virtual Person Person { get; set; }
	public DateTime CreatedDate { get; set; }
	public DateTime LastModifiedDate { get; set; }
}
Trong DbContext, bạn nhớ sử dụng hàm converter()
public class WatchContext: DbContext
{
	public WatchContext(DbContextOptions<WatchContext> options)
		: base(options)
	{ }
	public DbSet<Person> People { get; set; }
	public DbSet<WakeUpTime> WakeUpTime { get; set; }

	// using TinyHelpers.EntityFrameworkCore.Extensions;
	protected override void OnModelCreating(ModelBuilder modelBuilder)
	{
		modelBuilder.Entity<Person>(builder =>
		{
			// Date is a DateOnly property (.NET 6)
			builder.Property(x => x.Birthday).HasDateOnlyConversion();
		});
		modelBuilder.Entity<WakeUpTime>(builder =>
		{
			// Date is a TimeOnly  property (.NET 6)
			builder.Property(x => x.BedTime).HasTimeOnlyConversion();
			builder.Property(x => x.WakeupTime).HasTimeOnlyConversion();
		});
	}
}
MainProgram.cs:
static void DemoConnectDatabase()
{
    DemoWakeupTime();
    GetPerson();
}

static void DemoWakeupTime()
{
    var name = "Franklin Delano Roosevelt";
    var factory = new WatchContextFactory();
    using (var context = factory.CreateDbContext(null))
    {
        var findPerson = context.People.FirstOrDefault(t => t.Name == name);
        if (findPerson == null)
        {
            findPerson = new Person
            {
                Name = "Franklin Delano Roosevelt",
                Birthday = new DateOnly(1989, 01, 10)
            };

            context.People.Add(findPerson);
            context.SaveChanges();
            var wakeUpTime = new WakeUpTime
            {
                PersonId = findPerson.Id,
                BedTime = new TimeOnly(21, 0),
                WakeupTime = new TimeOnly(6, 0),
                CreatedDate = DateTime.Now,
                LastModifiedDate = DateTime.Now,
            };
            context.WakeUpTime.Add(wakeUpTime);
            context.SaveChanges();
        }
    }
}

static void GetPerson()
{
    var factory = new WatchContextFactory();
    using (var context = factory.CreateDbContext(null))
    {
        var findPerson = context.People.FirstOrDefault();
        
        if (findPerson != null)
        {
            Console.WriteLine($"Name: {findPerson.Name}");
            var wakeupTime = context.WakeUpTime.Where(t=>t.PersonId == findPerson.Id).ToList();
            var badSleep = new BadSleepApprover();
            var goodSleep = new GoodSleepApprover();
            var notGoodSleep = new NotGoodSleepApprover();
            badSleep.Next(goodSleep);
            goodSleep.Next(notGoodSleep);
            foreach (var item in wakeupTime)
            {
                var sleepTime = item.WakeupTime - item.BedTime;
                badSleep.HandleRequest(sleepTime);
            }
        }
    }
}

Link download: https://www.mediafire.com/file/nn9fev6xwxllb1q

Chúc các bạn thành công!!!

Nhận xét

Bài đăng phổ biến từ blog này

[ASP.NET MVC] Authentication và Authorize

Một trong những vấn đề bảo mật cơ bản nhất là đảm bảo những người dùng hợp lệ truy cập vào hệ thống. ASP.NET đưa ra 2 khái niệm: Authentication và Authorize Authentication xác nhận bạn là ai. Ví dụ: Bạn có thể đăng nhập vào hệ thống bằng username và password hoặc bằng ssh. Authorization xác nhận những gì bạn có thể làm. Ví dụ: Bạn được phép truy cập vào website, đăng thông tin lên diễn đàn nhưng bạn không được phép truy cập vào trang mod và admin.

ASP.NET MVC: Cơ bản về Validation

Validation (chứng thực) là một tính năng quan trọng trong ASP.NET MVC và được phát triển trong một thời gian dài. Validation vắng mặt trong phiên bản đầu tiên của asp.net mvc và thật khó để tích hợp 1 framework validation của một bên thứ 3 vì không có khả năng mở rộng. ASP.NET MVC2 đã hỗ trợ framework validation do Microsoft phát triển, tên là Data Annotations. Và trong phiên bản 3, framework validation đã hỗ trợ tốt hơn việc xác thực phía máy khách, và đây là một xu hướng của việc phát triển ứng dụng web ngày nay.

Tổng hợp một số kiến thức lập trình về Amibroker

Giới thiệu về Amibroker Amibroker theo developer Tomasz Janeczko được xây dựng dựa trên ngôn ngữ C. Vì vậy bộ code Amibroker Formula Language sử dụng có syntax khá tương đồng với C, ví dụ như câu lệnh #include để import hay cách gói các object, hàm trong các block {} và kết thúc câu lệnh bằng dấu “;”. AFL trong Amibroker là ngôn ngữ xử lý mảng (an array processing language). Nó hoạt động dựa trên các mảng (các dòng/vector) số liệu, khá giống với cách hoạt động của spreadsheet trên excel.