Practical .NET · Lesson 3

Dates and culture

Parse dates with an exact format and the invariant culture.

A calendar date is not an instant with a hidden time zone

DateOnly represents a date in the calendar without attaching a time of day or UTC offset. It is appropriate for an invoice issue date or another domain value whose meaning is the day itself. A timestamp for an actual event is a different requirement. Choosing DateOnly avoids accidentally treating an invented midnight as a universal instant and then shifting its date when a time-zone conversion is applied later.

A machine date format also needs an explicit grammar. ParseExact with yyyy-MM-dd and InvariantCulture reads year, month, and day in the documented order. It does not guess whether a slash-separated value is month-first or day-first according to the phone's region. Output uses the same explicit pattern. Calendar arithmetic and textual formatting are separate operations: AddDays changes the date value, while ToString chooses how that date is represented for this report.

Move thirty calendar days from the issue date

The prepared issue date is November 3, 2026. Adding thirty days reaches December 3, 2026. The first WriteLine formats that due date as 2026-12-03. The next reads DayOfWeek, whose enum value is Thursday, and prints Thursday. This spelling comes from the enum member's name rather than from a localized long-date formatter. The example deliberately avoids the current clock and the device's local time zone, so those settings cannot move its prepared issue date.

The final expression subtracts the issue date's DayNumber from the due date's DayNumber and prints 30. These are calendar-day positions, so their difference confirms the AddDays argument. It is not a stopwatch measurement and does not calculate elapsed hours across a real time-zone transition. Also do not count both endpoints inclusively and expect the same quantity: adding thirty day transitions is not the same question as counting all labeled dates including both the starting and ending date.

var due = issued.AddDays(30);

Console.WriteLine(due.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture));
Console.WriteLine(due.DayOfWeek);
Console.WriteLine(due.DayNumber - issued.DayNumber);

Keep parsing, calendar rules, and scheduling rules distinct

ParseExact rejects text that does not meet the requested format or represent a valid date; it is not a broad natural-language date parser. AddDays handles month and leap-year boundaries but does not implement working-day rules, holiday calendars, or business closing times. A requirement such as thirty business days needs a separate policy. If an event must occur at a particular local clock time, supply that time and the intended zone rather than silently treating the DateOnly value as a complete scheduling instruction.

Check a nearby date and a leap-year boundary

First change the issue text to 2026-11-04 while retaining AddDays thirty. Predict 2026-12-04, Friday, and a DayNumber difference of thirty. Hint: shifting the issue date by one day shifts this fixed calendar-day due date by one day as well. Then test a malformed text format separately and identify the parsing failure instead of interpreting it as an error in AddDays or a regional calendar conversion.

Next use 2024-02-28 and add one day. Predict 2024-02-29, Thursday, and a difference of one, because that year is a leap year. Repeat with February 28, 2023 and predict March 1. Hint: keep the format pattern fixed while changing only valid calendar inputs. Finally sketch a business-day calculator that skips weekends and explain why its rule needs additional iteration or calendar data instead of replacing the input thirty with an unexplained constant.

Choose the type that matches the question

State whether a new requirement concerns a calendar date, a local appointment, an instant, or an elapsed duration before selecting its date/time type. For this sample, preserve the issue date, due date, weekday, and day difference as separate observations. The exact unchanged output is three lines, and the result does not depend on today's date. Use controlled inputs around a month and leap-year boundary when testing a similar calendar calculation.

  • The issue date is parsed from the exact year-month-day grammar.
  • Thirty calendar-day transitions produce 2026-12-03.
  • The remaining original lines are Thursday and 30.
  • No current clock or local-zone conversion is involved.

A working example

using System.Globalization;

var issued = DateOnly.ParseExact("2026-11-03", "yyyy-MM-dd", CultureInfo.InvariantCulture);
var due = issued.AddDays(30);

Console.WriteLine(due.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture));
Console.WriteLine(due.DayOfWeek);
Console.WriteLine(due.DayNumber - issued.DayNumber);

Expected output

2026-12-03
Thursday
30
Try this
Use one exact pattern for reading and for writing.

Further reading