Datetime and time picker for Angular Material — with an adapter-pluggable core.
Standalone components · Signal-driven · Material 3 only · Adapter-pluggable
npm · GitHub · Live Demo · API Reference
- ✅
<sd-datetime-picker>— calendar + time spinner inside a CDK Overlay - ✅
[sdDatetimePicker]input directive — fullControlValueAccessorsupport ([formControl],formControlName,[(ngModel)]) - ✅ Default actions render automatically (Now / Cancel / Apply) — projection-based override available
- ✅ Adapter-pluggable — use the included native adapter or implement your own
SdDateAdapter<D> - ✅ Standalone-only components — no NgModule
- ✅ Signal-driven (Angular 19+
input(),model(),output(),signal(),computed()) - ✅ Material 3 theming with system-token support and dark-mode compatibility
- ✅ Tree-shakable — only what you import is bundled
- ✅ Tested — unit and integration coverage includes custom non-native date objects
npm install @sdcorejs/angular-material-datetime @angular/material @angular/cdkThen register the date adapter in your app.config.ts:
import { provideSdNativeDateAdapter } from '@sdcorejs/angular-material-datetime';
export const appConfig: ApplicationConfig = {
providers: [
provideSdNativeDateAdapter(),
],
};import {
SdDatetimePicker, SdDatetimePickerInput, SdDatetimePickerToggle,
} from '@sdcorejs/angular-material-datetime';
@Component({
imports: [
ReactiveFormsModule, MatInputModule, MatFormFieldModule, MatButtonModule,
SdDatetimePicker, SdDatetimePickerInput, SdDatetimePickerToggle,
],
template: `
<mat-form-field>
<mat-label>Pick a datetime</mat-label>
<input matInput [sdDatetimePicker]="picker" [formControl]="ctrl">
<button matSuffix [sdDatetimePickerToggle]="picker">Open</button>
<sd-datetime-picker #picker></sd-datetime-picker>
</mat-form-field>
`,
})
export class MyComponent {
ctrl = new FormControl<Date | null>(null);
}The default actions (Now / Cancel / Apply) render automatically. Nothing else needed.
Project your own <sd-datetime-picker-actions> block to fully replace the defaults — for example, Vietnamese labels:
<sd-datetime-picker #picker>
<sd-datetime-picker-actions>
<button mat-button sdDatetimePickerNow>Bây giờ</button>
<button mat-button sdDatetimePickerCancel>Hủy</button>
<button mat-flat-button sdDatetimePickerApply>Xác nhận</button>
</sd-datetime-picker-actions>
</sd-datetime-picker>Add SdDatetimePickerActions, SdDatetimePickerNow, SdDatetimePickerCancel, SdDatetimePickerApply to your component imports only when projecting custom actions.
<sd-datetime-picker #picker [showSeconds]="true" [stepMinute]="5"></sd-datetime-picker>stepMinute uses strict validation: valid minutes must be divisible by the configured step. Invalid step configuration (<= 0, fractional, non-finite, or > 60) is normalized to 1.
<sd-datetime-picker
#picker
[minDate]="today"
[maxDate]="endOfMonth">
</sd-datetime-picker>minDate and maxDate are inclusive full datetime boundaries. The input directive exposes sdDatetimeParse, sdDatetimeMin, sdDatetimeMax, and sdDatetimeMinuteStep validation errors.
import { SD_DATETIME_DEFAULT_OPTIONS } from '@sdcorejs/angular-material-datetime';
providers: [{
provide: SD_DATETIME_DEFAULT_OPTIONS,
useValue: { showSeconds: true, stepMinute: 15, touchUi: true },
}]Explicit picker inputs override injected defaults. touchUi uses a centered, viewport-constrained overlay.
import { Injectable } from '@angular/core';
import { SdDatetimeIntl } from '@sdcorejs/angular-material-datetime';
@Injectable()
export class VietnameseDatetimeIntl extends SdDatetimeIntl {
override dialogLabel = 'Chọn ngày và giờ';
override nowLabel = 'Hiện tại';
override cancelLabel = 'Hủy';
override applyLabel = 'Áp dụng';
override hourLabel = 'Giờ';
override minuteLabel = 'Phút';
override secondLabel = 'Giây';
}
providers: [{ provide: SdDatetimeIntl, useClass: VietnameseDatetimeIntl }]After changing labels at runtime, call intl.changes.next() so open pickers refresh.
ctrl = new FormControl<Date | null>(null, Validators.required);<mat-form-field>
<input matInput [sdDatetimePicker]="picker" [formControl]="ctrl">
<button matSuffix [sdDatetimePickerToggle]="picker">Open</button>
<sd-datetime-picker #picker></sd-datetime-picker>
@if (ctrl.invalid && ctrl.touched) {
<mat-error>Required</mat-error>
}
</mat-form-field>See the live demo for 9 working examples plus copy-pastable source.
| Package | Purpose | Status |
|---|---|---|
@sdcorejs/angular-material-datetime |
Core components + directives + native date adapter | v1 ✅ |
@sdcorejs/angular-material-datetime-moment |
Moment.js date adapter | v1.x planned |
@sdcorejs/angular-material-datetime-date-fns |
date-fns date adapter | v1.x planned |
The Moment and date-fns packages are placeholders with empty public APIs. They are excluded from the release workflow until runtime implementations exist; use the native adapter today.
The library requires an Angular Material 3 theme. Define your application theme with mat.theme(...) so the picker can read system variables such as --mat-sys-surface-container, --mat-sys-primary, and --mat-sys-outline-variant. These variables keep the picker aligned with your palette and dark mode.
@use '@angular/material' as mat;
html {
@include mat.theme((
color: mat.$violet-palette,
typography: Roboto,
density: 0,
));
}If your application uses an earlier Angular Material theming API, migrate the application theme to mat.theme(...) before adopting this package version. The datetime picker expects Material 3 system variables and does not provide a compatibility styling layer for older theme definitions.
npm ci
npm run build:allOutputs dist/datetime/, dist/moment-adapter/, dist/date-fns-adapter/ (FESM2022 + .d.ts).
npm test
npm run test:coveragePowered by Jest + jest-preset-angular. Coverage thresholds: 80% global, 95% native adapter directory.
This repo uses Changesets + a GitHub Actions workflow. Day-to-day flow:
- Make changes on a feature branch.
- Add a changeset describing what changed:
npx changeset
- Commit the generated
.changeset/*.mdalongside your code change. - Open a PR into
main.
When the PR merges, the Release workflow:
- If unreleased changesets exist → opens a "Version Packages" PR that bumps versions + updates CHANGELOG.
- When that PR is merged → builds with
ng-packagr, packs once, verifies that exact Angular Package Format tarball, and publishes those same bytes.
The source workspace under projects/datetime and the built dist/datetime directory are never direct publish targets. A manually approved release must retain the verified absolute tarball path and SHA-256 produced by the release preflight:
export SD_DATETIME_RELEASE_TARBALL=/absolute/path/to/sdcorejs-angular-material-datetime-1.0.4.tgz
export SD_DATETIME_RELEASE_SHA256=<verified-sha256>
npm run releaseRequirements:
NPM_TOKENrepository secret (already configured)- Member of the
@sdcorejsnpm org with publish access
| Angular | @sdcorejs/angular-material-datetime |
|---|---|
| 19.x | 1.x |
| 20.x | 1.x |
| 21.x | 1.x |
| 22.x | 1.x |
Angular, Angular Material, CDK, and Angular Forms 19–22 with RxJS 7 are verified by clean strict-template consumer builds from one packed release artifact.
@sdcorejs/angular-material-datetime is designed around:
- Material-idiomatic API (extends, not replaces, the Material datepicker family)
- Standalone-only — zero NgModule, zero migration friction
- Signal-driven internals — predictable change detection, no Zone.js coupling
- Adapter-pluggable — your date library, your choice
- Sensible defaults — works out of the box, customizable when you need it
- AI-friendly contracts — semantic naming, explicit types, copy-pastable examples
@sdcorejs/utils— TypeScript utilities@sdcorejs/angular-material-datetime— this package
MIT — Copyright (c) 2026 Trần Thuận Nghĩa and contributors
