Skip to content

Latest commit

ย 

History

52 Commits

Folders and files

NameName
Last commit message
Last commit date
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐Ÿ• DateTime Widget for Windows

A lightweight, always-on-top Windows desktop widget that shows time and date in Gregorian and Persian (Jalali) calendars โ€” synced with an online time API and beautifully themed with Material Design.

.NET WPF MaterialDesign TimeZoneConverter Tests License


โœจ Features

  • ๐Ÿ—“๏ธ Dual calendar support โ€” Gregorian and Persian (Jalali) dates, with Persian digit rendering
  • ๐ŸŒ Multi-timezone โ€” pick any Windows system timezone; the widget recalculates automatically
  • ๐Ÿ”„ Windows โ†” IANA mapping โ€” powered by TimeZoneConverter, so Windows IDs like Iran Standard Time map to Asia/Tehran when talking to the time API
  • โ˜๏ธ Online time sync โ€” pulls accurate time from timeapi.io with retry + circuit-breaker resilience (Polly)
  • ๐Ÿ•’ Configurable format โ€” 12/24-hour, optional seconds, optional date row
  • ๐ŸŽจ Customizable appearance โ€” Material Design color picker, opacity slider, three widget sizes
  • ๐Ÿ–ฑ๏ธ Draggable, always-on-top window โ€” right-click for settings, hover for close
  • ๐Ÿ’พ Persistent settings โ€” window position, color, size, language, timezone saved automatically
  • ๐ŸŒ Bilingual UI โ€” English and Persian (RTL-aware) rendering
  • ๐Ÿงช Comprehensive unit test suite โ€” Services, ViewModels, Converters covered with xUnit + Moq + FluentAssertions

๐Ÿ“ธ Preview

DateTime Widget preview DateTime Widget preview DateTime Widget preview

๐Ÿ—๏ธ Architecture

The application follows a clean MVVM structure with dependency injection, background hosted services, and a service abstraction layer.

WindowsTimeWidget/
โ”œโ”€โ”€ Abstractions/           Interfaces (ITimeService, ISettingsService, ITimeSyncService)
โ”œโ”€โ”€ Models/                 Domain models (WidgetSettings, WidgetLanguage, WidgetSize, TimeApiResponse)
โ”œโ”€โ”€ Services/
โ”‚   โ”œโ”€โ”€ DateFormatter.cs            Gregorian + Persian formatting, digit conversion
โ”‚   โ”œโ”€โ”€ SettingsService.cs          JSON persistence under %APPDATA%
โ”‚   โ”œโ”€โ”€ TimeService.cs              API sync + system-time fallback + drift projection
โ”‚   โ”‚                               (converts Windows timezone IDs โ†’ IANA via TimeZoneConverter)
โ”‚   โ””โ”€โ”€ BackgroundServices/
โ”‚       โ”œโ”€โ”€ TimeSyncService.cs      IHostedService, periodic + triggered re-sync
โ”‚       โ”œโ”€โ”€ TimeSyncOptions.cs      Configuration binding
โ”‚       โ””โ”€โ”€ ServiceCollectionExtensions.cs
โ”œโ”€โ”€ ViewModels/
โ”‚   โ”œโ”€โ”€ ViewModelBase.cs            INotifyPropertyChanged base
โ”‚   โ”œโ”€โ”€ RelayCommand.cs             ICommand implementation
โ”‚   โ”œโ”€โ”€ MainViewModel.cs            Drives the widget window
โ”‚   โ””โ”€โ”€ SettingsViewModel.cs        Drives the settings window
โ”œโ”€โ”€ Views/
โ”‚   โ”œโ”€โ”€ Converters/
โ”‚   โ”‚   โ”œโ”€โ”€ BoolToFlowDirectionConverter.cs
โ”‚   โ”‚   โ”œโ”€โ”€ ColorToHexConverter.cs
โ”‚   โ”‚   โ”œโ”€โ”€ EnumToBoolConverter.cs
โ”‚   โ”‚   โ””โ”€โ”€ HexToBrushConverter.cs
โ”‚   โ”œโ”€โ”€ UserControls/
โ”‚   โ”‚   โ””โ”€โ”€ DateTimeDisplay.xaml    The visual content of the widget
โ”‚   โ””โ”€โ”€ Windows/
โ”‚       โ”œโ”€โ”€ MainWindow.xaml         Frameless, draggable, always-on-top widget
โ”‚       โ””โ”€โ”€ SettingsWindow.xaml     Material Design settings dialog
โ”œโ”€โ”€ App.xaml / App.xaml.cs          Host builder, DI, HTTP client, Polly policies
โ””โ”€โ”€ appsettings.json                TimeSync configuration

Design principles

  • Dependency injection everywhere โ€” Microsoft.Extensions.Hosting drives the app lifecycle
  • Interfaces over concretes โ€” every service has an I* abstraction, making the ViewModels unit-testable
  • Resilience by default โ€” HTTP calls go through Polly retry + circuit-breaker policies
  • Non-blocking UI โ€” time sync runs on a BackgroundService; the UI just reads projected time
  • Culture-invariant formatting โ€” DateFormatter uses explicit en-US and PersianCalendar, independent of the host culture
  • Portable timezone IDs โ€” Windows IDs are converted to IANA at the HTTP boundary, keeping the UI Windows-native while remaining compatible with the API

๐Ÿš€ Getting Started

Prerequisites

  • Windows 10 / 11
  • .NET 10 SDK (with the Windows Desktop workload)
  • Internet connection for time sync (the widget falls back to system time if offline)

Build & Run

git clone https://github.com/omiddadvar/Windows-Time-Widget.git
cd Windows-Time-Widget
dotnet restore
dotnet run --project WindowsTimeWidget

Run the tests

dotnet test

Or filter by module:

dotnet test --filter "Module~Services"
dotnet test --filter "Module~ViewModels"
dotnet test --filter "Module~Views.Converters"

โš™๏ธ Configuration

Time sync behavior is controlled via appsettings.json:

{
  "TimeSync": {
    "SyncInterval": "00:05:00",
    "StartupDelay": "00:00:03"
  }
}
Key Default Description
TimeSync:SyncInterval 00:05:00 How often to re-sync with the API
TimeSync:StartupDelay 00:00:03 Delay before the first sync after app start

User settings

Persisted at:

%APPDATA%\Mohaasaan\DateTimeWidget\settings.json

Contains timezone, color, size, opacity, language, format flags, and window position.

Timezone resolution

Windows exposes timezones through TimeZoneInfo.GetSystemTimeZones(), which returns Windows IDs (e.g. Iran Standard Time, N. Central Asia Standard Time). The time API at timeapi.io only accepts IANA IDs (e.g. Asia/Tehran, Asia/Novosibirsk).

TimeService bridges the two using TimeZoneConverter:

  • On the way out to the API: TZConvert.TryWindowsToIana(id, out var iana).
  • On the way back to TimeZoneInfo: the IANA ID is passed through unchanged.
  • If the input is already an IANA ID, TryWindowsToIana returns false and the ID is used as-is.

This means the settings dropdown can show Windows-native timezone names (what users expect on Windows), while the HTTP layer stays compatible with the API's IANA-only contract.


๐ŸŽฎ Usage

Action Result
Left-click + drag Move the widget
Right-click Open the settings window
Hover top-right corner Reveal the close button
Change timezone in settings Widget re-syncs immediately
Change language to Persian Dates render RTL with Persian digits

๐Ÿงช Testing

The project ships with a comprehensive xUnit test suite organized by module to mirror the application structure.

Stack

  • xUnit โ€” test framework
  • Moq + Moq.Contrib.HttpClient โ€” mock ISettingsService, ITimeService, and HttpMessageHandler
  • FluentAssertions โ€” readable assertion syntax

Coverage areas

Module Focus
Models.TimeApiResponse Deserialization of the real snake_case API payload, DateTimeOffset computed property, offset handling, partial payloads
Services.DateFormatter Gregorian + Persian formatting, digit conversion, culture invariance
Services.SettingsService Persistence, cloning, invalid input, corrupt-file recovery, event raising
Services.TimeService API sync success/failure, Windows โ†’ IANA URL translation, drift projection, multi-zone reads, semaphore guard, malformed payload fallback
Services.BackgroundServices Hosted service cadence, option binding, DI registration, exception โ†’ system-time fallback
ViewModels.ViewModelBase SetProperty, OnPropertyChanged semantics
ViewModels.RelayCommand Execute/CanExecute both overloads, null guards
ViewModels.MainViewModel Settings binding, derived properties, timer lifecycle, FormattedTime derived from CurrentTime
ViewModels.SettingsViewModel Load/save/reset, hex normalization, event propagation, timezone selection
Views.Converters All four converters โ€” including round-trip and fallback paths
Views.UserControls.DateTimeDisplay Construction smoke test, tree structure, binding propagation, visibility, flow direction

Test conventions

  • Every test is annotated with // Arrange, // Act, // Assert comments
  • WPF-dependent tests run on a dedicated STA thread via a StaRunner helper
  • Settings-based tests back up and restore %APPDATA% to avoid polluting the host
  • Assembly-level DisableTestParallelization prevents cross-test interference on shared resources

๐Ÿ› ๏ธ Tech Stack

Concern Choice
Runtime .NET 10 (net10.0-windows)
UI WPF + MaterialDesignInXamlToolkit
DI / Hosting Microsoft.Extensions.Hosting
HTTP HttpClient + System.Net.Http.Json
Resilience Polly (retry with exponential backoff, circuit breaker)
Timezone mapping TimeZoneConverter 7.2.0
Serialization System.Text.Json
Testing xUnit, Moq, Moq.Contrib.HttpClient, FluentAssertions

๐Ÿค Contributing

Contributions are welcome!

  1. Fork the repo
  2. Create a feature branch: git checkout -b feature/amazing-thing
  3. Follow the existing style:
    • MVVM โ€” no logic in code-behind
    • Interfaces for every service
    • Unit tests for every ViewModel / Service / Converter change
    • // Arrange, // Act, // Assert comments in tests
  4. Run dotnet test and make sure everything is green
  5. Open a Pull Request

Areas that would benefit from help

  • UI automation tests (FlaUI) for the actual window behavior
  • Localization โ€” currently English + Persian; more languages welcome
  • Additional time providers โ€” NTP, worldtimeapi.org as fallback sources
  • Tray icon + startup shortcut support
  • Dark / light theme sync with the OS

๐Ÿ“„ License

This project is licensed under the MIT License โ€” see the LICENSE file for details.


๐Ÿ™ Acknowledgements


Made with โค๏ธ for the Windows desktop.

If this project helps you, consider giving it a โญ

About

A lightweight, always-on-top Windows widget showing time & date in Gregorian and Persian calendars.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages