A full-stack sample project demonstrating best practices for handling timestamps across timezones in modern web applications.
This project solves a common problem in distributed applications: how to correctly store and display timestamps when users are in different timezones.
The solution demonstrates:
- Storing all timestamps as UTC on the backend (.NET 8 Web API)
- Dynamically displaying timestamps in the user's local timezone on the frontend (Angular 20)
- Proper timezone conversion without data loss
- RESTful API design with proper date handling
- UTC Storage: All timestamps stored as UTC in the database/API
- Automatic Timezone Conversion: Frontend dynamically converts UTC to user's local time
- Timezone-Aware Display: Shows times in user-friendly formats with timezone information
- RESTful API: Clean .NET 8 Web API with proper ISO 8601 date serialization
- Modern Frontend: Built with Angular 20 using standalone components
- Type Safety: Full TypeScript implementation with proper date type handling
βββββββββββββββββββββββββββββββββββββββ
β Angular 20 Frontend β
β β
β β’ Receives UTC timestamps β
β β’ Converts to local timezone β
β β’ Displays in user-friendly format β
ββββββββββββββββ¬βββββββββββββββββββββββ
β HTTP/REST
β JSON (ISO 8601)
ββββββββββββββββΌβββββββββββββββββββββββ
β .NET 8 Web API β
β β
β β’ Stores UTC timestamps β
β β’ Returns ISO 8601 formatted dates β
β β’ Timezone-agnostic data layer β
βββββββββββββββββββββββββββββββββββββββ
- .NET 8 - Latest LTS version
- ASP.NET Core Web API - RESTful API framework
- C# 12 - Modern language features
- Entity Framework Core (if applicable)
- Angular 20 - Latest Angular framework
- TypeScript 5.0 - Type-safe JavaScript
- RxJS - Reactive programming
- Angular Standalone Components - Modern Angular architecture
- .NET 8 SDK
- Node.js (v18 or higher)
- Angular CLI (
npm install -g @angular/cli)
git clone https://github.com/rbasehewa/angular-dotnet-timeoffset.git
cd angular-dotnet-timeoffsetcd TimezoneApi
dotnet restore
dotnet runThe API will start at https://localhost:5001 (or http://localhost:5000)
cd timezone-ui
npm install
ng serveThe application will open at http://localhost:4200
The API always stores and returns timestamps in UTC:
public class TimeEntry
{
public int Id { get; set; }
public DateTime CreatedAt { get; set; } // Always stored as UTC
public string Description { get; set; }
}
[HttpPost]
public IActionResult CreateEntry([FromBody] TimeEntry entry)
{
entry.CreatedAt = DateTime.UtcNow; // Force UTC
// Save to database...
return Ok(entry);
}The Angular app receives UTC and converts to local time:
export class TimeDisplayComponent {
entries: TimeEntry[] = [];
ngOnInit() {
this.http.get<TimeEntry[]>('/api/entries')
.subscribe(entries => {
this.entries = entries.map(entry => ({
...entry,
// JavaScript Date automatically converts UTC to local
createdAt: new Date(entry.createdAt)
}));
});
}
}Template displays local time:
<div *ngFor="let entry of entries">
<p>Created: {{ entry.createdAt | date:'medium' }}</p>
<!-- Shows: "Jan 26, 2026, 2:30:45 PM" in user's timezone -->
</div>- Database and API never store local times
- Eliminates ambiguity during DST transitions
- Enables accurate time calculations
- API returns dates like
"2026-01-26T14:30:45.123Z" - The
Zindicates UTC (Zulu time) - Universal standard for date interchange
- Browser automatically handles timezone conversion
- Respects user's system timezone settings
- No server-side timezone logic needed
- Angular's built-in
datepipe handles formatting - Automatically uses browser's locale
- Customizable formats (short, medium, long, full)
You can test timezone handling by:
-
Changing System Timezone:
- Windows: Settings β Time & Language β Date & Time
- macOS: System Preferences β Date & Time
- Linux:
timedatectl set-timezone [timezone]
-
Browser DevTools:
- Open Chrome DevTools
- Press
Ctrl+Shift+P(Cmd+Shift+P on Mac) - Type "timezone" and select "Show Sensors"
- Choose a different timezone
-
Expected Behavior:
- Same UTC timestamp from API
- Different local display time
- Correct relative time calculations
angular-dotnet-timeoffset/
βββ TimezoneApi/ # .NET 8 Web API
β βββ Controllers/
β β βββ TimeEntriesController.cs
β βββ Models/
β β βββ TimeEntry.cs
β βββ Program.cs
β βββ appsettings.json
β
βββ timezone-ui/ # Angular 20 Frontend
βββ src/
β βββ app/
β β βββ components/
β β βββ services/
β β βββ models/
β βββ environments/
βββ angular.json
{
"Logging": {
"LogLevel": {
"Default": "Information"
}
},
"AllowedHosts": "*",
"Cors": {
"AllowedOrigins": ["http://localhost:4200"]
}
}export const environment = {
production: false,
apiUrl: 'https://localhost:5001/api'
};Solution: Ensure you're converting string dates to JavaScript Date objects:
createdAt: new Date(entry.createdAt)Solution: Check that API is returning dates with Z suffix (UTC indicator)
Solution: Ensure CORS is configured in the .NET API:
builder.Services.AddCors(options => {
options.AddDefaultPolicy(builder => {
builder.WithOrigins("http://localhost:4200")
.AllowAnyMethod()
.AllowAnyHeader();
});
});- β Always store timestamps as UTC in the database
- β Use ISO 8601 format for date serialization
- β Convert to local time only on display
- β Use timezone-aware date libraries when needed
- β Consider user's timezone preference for multi-tenant apps
Contributions are welcome! This is a reference implementation, so issues and pull requests that improve clarity or demonstrate additional patterns are appreciated.
This project is open source and available under the MIT License.
Ryan Maddumahewa
- GitHub: @rbasehewa
- LinkedIn: ryanmaddumahewa
- Website: ryanmaddumahewa.dev
If this project helped you understand timezone handling in full-stack applications, please give it a star! β
Built with β€οΈ using .NET 8 and Angular 20