به همراه دات نت 6، دو ساختار دادهی جدید DateOnly و TimeOnly نیز معرفی شدهاند که امکان کار کردن سادهتر با قسمتهای فقط تاریخ و یا فقط زمان DateTime را میسر میکنند. این دو نوع جدید نیز همانند DateTime، از نوع struct هستند و بنابراین value type محسوب میشوند. در فضای نام System قرار گرفتهاند و همچنین با نوعهای date و time مربوط به SQL Server، سازگاری کاملی دارند.
روش استفاده از نوع DateOnly در دات نت 6
نوعهای جدید معرفی شده، بسیار واضح هستند و مقصود از بکارگیری آنها را به خوبی بیان میکنند. برای مثال اگر نیاز بود تاریخی را بدون در نظر گرفتن قسمت زمان آن معرفی کنیم، میتوان از نوع DateOnly استفاده کرد؛ مانند تاریخ تولد، روزهای کاری و امثال آن. تا پیش از این برای معرفی یک چنین تاریخهایی، عموما قسمت زمان DateTime را با 00:00:00.000 مقدار دهی میکردیم؛ اما دیگر نیازی به این نوع تعاریف نیست و میتوان مقصود خود را صریحتر بیان کرد.
روش معرفی نمونهای از آن با معرفی سال، ماه و روز است:
و یا اگر خواستیم یک DateTime موجود را به DateOnly تبدیل کنیم، میتوان به صورت زیر عمل کرد:
همچنین در اینجا نیز همانند DateTime میتوان از متدهای Parse و یا TryParse، برای تبدیل یک رشته به معادل DateOnly آن، کمک گرفت:
در یک چنین حالتی ذکر CultureInfo، دقت کار را افزایش میدهد؛ در غیراینصورت از CultureInfo ترد جاری برنامه استفاده خواهد شد که میتواند در سیستمهای مختلف، متفاوت باشد.
و یا میتوان توسط متد ParseExact، ساختار تاریخ دریافتی را دقیقا مشخص کرد:
در حین نمونه سازی DateOnly، امکان ذکر تقویمهای خاص، مانند PersianCalendar نیز وجود دارد:
در اینجا همچنین متدهایی مانند AddDays، AddMonths و AddYears نیز بر روی date مهیا کار میکنند:
یک نکته: برخلاف DateTime، نوع DateOnly به همراه DateTimeKind مانند Utc و امثال آن نیست و همواره DateTimeKind آن Unspecified است.
روش استفاده از نوع TimeOnly در دات نت 6
نوع و ساختار TimeOnly، قسمت زمان را به نحو صریحی مشخص میکند؛ مانند ساعتی که باید هر روز راس آن، آلارمی به صدا درآید و یا جلسهای تشکیل شود و یا وظیفهای صورت گیرد. سازندهی آن overloadهای قابل توجهی را داشته و میتواند یکی از موارد زیر باشد:
برای نمونه برای نمایش 10:30 صبح، میتوان به صورت زیر عمل کرد:
در اینجا قسمت ساعت، 24 ساعتی تعریف شدهاست. بنابراین برای نمونه، ساعت 1 عصر را باید به صورت 13 قید کرد:
و یا برای مثال میتوان این نمونهها را از هم کم کرد:
خروجی این تفاوت محاسبه شده، بر حسب TimeSpan است:
و یا با استفاده از متد الحاقی ToTimeSpan میتوان یک TimeOnly را به TimeSpan معادلی تبدیل نمود:
برای تبدیل قسمت زمان DateTime به TimeOnly، میتوان از متد FromDateTime به صورت زیر استفاده کرد:
و یا اگر بخواهیم یک DateOnly را به DateTime تبدیل کنیم، میتوان از متد الحاقی ToDateTime به همراه ذکر قسمت زمان آن بر حسب TimeOnly کمک گرفت:
و در این حالت اگر خواستیم بررسی کنیم که آیا زمانی بین دو زمان دیگر واقع شدهاست یا خیر، میتوان از متد IsBetween استفاده نمود:
در اینجا امکان مقایسه این نمونهها، توسط عملگرهایی مانند < نیز وجود دارد:
اگر نیاز به تبدیل رشتهای به TimeOnly بود، میتوان از متد ParseExact به همراه ذکر ساختار مدنظر، استفاده کرد:
عدم پشتیبانی System.Text.Json از نوعهای جدید DateOnly و TimeOnly
فرض کنید رکوردی را به صورت زیر تعریف کردهایم که از نوعهای جدید DateOnly و TimeOnly، تشکیل شدهاست:
اگر سعی کنیم نمونهای از آن را به JSON تبدیل کنیم:
با استثنای زیر مواجه خواهیم شد:
برای رفع این مشکل میتوان ابتدا تبدیلگر ویژهی DateOnly و
و سپس تبدیلگر ویژهی TimeOnly را به صورت زیر تدارک دید:
و به نحو زیر مورد استفاده قرار داد:
روش استفاده از نوع DateOnly در دات نت 6
نوعهای جدید معرفی شده، بسیار واضح هستند و مقصود از بکارگیری آنها را به خوبی بیان میکنند. برای مثال اگر نیاز بود تاریخی را بدون در نظر گرفتن قسمت زمان آن معرفی کنیم، میتوان از نوع DateOnly استفاده کرد؛ مانند تاریخ تولد، روزهای کاری و امثال آن. تا پیش از این برای معرفی یک چنین تاریخهایی، عموما قسمت زمان DateTime را با 00:00:00.000 مقدار دهی میکردیم؛ اما دیگر نیازی به این نوع تعاریف نیست و میتوان مقصود خود را صریحتر بیان کرد.
روش معرفی نمونهای از آن با معرفی سال، ماه و روز است:
var date = new DateOnly(2020, 04, 20);
var currentDate = DateOnly.FromDateTime(DateTime.Now);
همچنین در اینجا نیز همانند DateTime میتوان از متدهای Parse و یا TryParse، برای تبدیل یک رشته به معادل DateOnly آن، کمک گرفت:
if (DateOnly.TryParse("28/09/1984", new CultureInfo("en-US"), DateTimeStyles.None, out var result)) { Console.WriteLine(result); }
و یا میتوان توسط متد ParseExact، ساختار تاریخ دریافتی را دقیقا مشخص کرد:
DateOnly d1 = DateOnly.ParseExact("31 Dec 1980", "dd MMM yyyy", CultureInfo.InvariantCulture); // Custom format Console.WriteLine(d1.ToString("o", CultureInfo.InvariantCulture)); // "1980-12-31" (ISO 8601 format)
در حین نمونه سازی DateOnly، امکان ذکر تقویمهای خاص، مانند PersianCalendar نیز وجود دارد:
var persianCalendar = new PersianCalendar(); DateOnly d2 = new DateOnly(1400, 9, 6, persianCalendar); Console.WriteLine(d2.ToString("d MMMM yyyy", CultureInfo.InvariantCulture));
در اینجا همچنین متدهایی مانند AddDays، AddMonths و AddYears نیز بر روی date مهیا کار میکنند:
var newDate = date.AddDays(1).AddMonths(1).AddYears(1)
یک نکته: برخلاف DateTime، نوع DateOnly به همراه DateTimeKind مانند Utc و امثال آن نیست و همواره DateTimeKind آن Unspecified است.
روش استفاده از نوع TimeOnly در دات نت 6
نوع و ساختار TimeOnly، قسمت زمان را به نحو صریحی مشخص میکند؛ مانند ساعتی که باید هر روز راس آن، آلارمی به صدا درآید و یا جلسهای تشکیل شود و یا وظیفهای صورت گیرد. سازندهی آن overloadهای قابل توجهی را داشته و میتواند یکی از موارد زیر باشد:
public TimeOnly(int hour, int minute) public TimeOnly(int hour, int minute, int second) public TimeOnly(int hour, int minute, int second, int millisecond)
var startTime = new TimeOnly(10, 30);
var endTime = new TimeOnly(13, 00, 00);
و یا برای مثال میتوان این نمونهها را از هم کم کرد:
var diff = endTime - startTime;
Console.WriteLine($"Hours: {diff.TotalHours}");
TimeSpan ts = endTime.ToTimeSpan();
برای تبدیل قسمت زمان DateTime به TimeOnly، میتوان از متد FromDateTime به صورت زیر استفاده کرد:
var currentTime = TimeOnly.FromDateTime(DateTime.Now);
DateTime dt = date.ToDateTime(new TimeOnly(0, 0)); Console.WriteLine(dt);
و در این حالت اگر خواستیم بررسی کنیم که آیا زمانی بین دو زمان دیگر واقع شدهاست یا خیر، میتوان از متد IsBetween استفاده نمود:
var isBetween = currentTime.IsBetween(startTime, endTime); Console.WriteLine($"Current time {(isBetween ? "is" : "is not")} between start and end");
در اینجا امکان مقایسه این نمونهها، توسط عملگرهایی مانند < نیز وجود دارد:
var startTime = new TimeOnly(08, 00); var endTime = new TimeOnly(09, 00); Console.WriteLine($"{startTime < endTime}");
اگر نیاز به تبدیل رشتهای به TimeOnly بود، میتوان از متد ParseExact به همراه ذکر ساختار مدنظر، استفاده کرد:
TimeOnly time = TimeOnly.ParseExact("5:00 pm", "h:mm tt", CultureInfo.InvariantCulture); // Custom format Console.WriteLine(time.ToString("T", CultureInfo.InvariantCulture)); // "17:00:00" (long time format)
فرض کنید رکوردی را به صورت زیر تعریف کردهایم که از نوعهای جدید DateOnly و TimeOnly، تشکیل شدهاست:
public record DataTypeTest(DateOnly Date, TimeOnly Time);
var date = DateOnly.FromDateTime(DateTime.Now); var time = TimeOnly.FromDateTime(DateTime.Now); var test = new DataTypeTest(date, time); var json = JsonSerializer.Serialize(test);
Serialization and deserialization of 'System.DateOnly' instances are not supported.
برای رفع این مشکل میتوان ابتدا تبدیلگر ویژهی DateOnly و
public class DateOnlyConverter : JsonConverter<DateOnly> { private readonly string _serializationFormat; public DateOnlyConverter() : this(null) { } public DateOnlyConverter(string? serializationFormat) { _serializationFormat = serializationFormat ?? "yyyy-MM-dd"; } public override DateOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var value = reader.GetString(); return DateOnly.ParseExact(value!, _serializationFormat, CultureInfo.InvariantCulture); } public override void Write(Utf8JsonWriter writer, DateOnly value, JsonSerializerOptions options) => writer.WriteStringValue(value.ToString(_serializationFormat)); }
public class TimeOnlyConverter : JsonConverter<TimeOnly> { private readonly string _serializationFormat; public TimeOnlyConverter() : this(null) { } public TimeOnlyConverter(string? serializationFormat) { _serializationFormat = serializationFormat ?? "HH:mm:ss.fff"; } public override TimeOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var value = reader.GetString(); return TimeOnly.ParseExact(value!, _serializationFormat, CultureInfo.InvariantCulture); } public override void Write(Utf8JsonWriter writer, TimeOnly value, JsonSerializerOptions options) => writer.WriteStringValue(value.ToString(_serializationFormat)); }
var jsonOptions = new JsonSerializerOptions(JsonSerializerDefaults.Web); jsonOptions.Converters.Add(new DateOnlyConverter()); jsonOptions.Converters.Add(new TimeOnlyConverter()); var json = JsonSerializer.Serialize(test, jsonOptions);