@libs-ui/pipes-format-date
v0.2.357-11
Published
> Bộ đôi pipe standalone để định dạng thời gian và khởi tạo đối tượng Dayjs trong Angular template.
Readme
@libs-ui/pipes-format-date
Bộ đôi pipe standalone để định dạng thời gian và khởi tạo đối tượng Dayjs trong Angular template.
Giới thiệu
@libs-ui/pipes-format-date cung cấp hai pipe standalone:
LibsUiPipesFormatDatePipe: Chuyển đổi các kiểu đầu vào thời gian (string, number/Unix timestamp, Dayjs object) thành chuỗi hiển thị theo 1 trong các định dạng alias cố định (xem bảng ở mục "Hành vi quan trọng"), hỗ trợ đa ngôn ngữ. Không phải cú pháp format Dayjs tự do.LibsUiPipesGetDayjsPipe: Nhận một config object và trả về đối tượngdayjs.Dayjs, cho phép gọi các method của Dayjs trực tiếp trong template (.year(),.format(), v.v.).
Cả hai pipe đều là Pure Pipe — Angular chỉ thực thi lại transform khi tham số đầu vào (reference) thay đổi, giúp tránh tính toán lại không cần thiết.
Tính năng
- Chấp nhận đầu vào đa dạng: ISO string, Unix timestamp (giây),
dayjs.Dayjs,null,undefined - Hỗ trợ đa ngôn ngữ (vi, en) thông qua tham số
lang - Hỗ trợ
formatInputđể parse chuỗi đầu vào có định dạng không chuẩn (vd:DD/MM/YYYY HH:mm) - Tích hợp múi giờ
Asia/Ho_Chi_Minhtheo mặc định thông quagetDayjstừ@libs-ui/utils - Pure Pipe — hiệu năng cao, chỉ tính toán lại khi input thay đổi
- Standalone — import trực tiếp vào component, không cần NgModule
⚠️ Hành vi quan trọng — formatOutput KHÔNG phải chuỗi format Dayjs tự do
Đây là điểm khác biệt lớn nhất so với dùng Dayjs trực tiếp. formatDate (hàm bên trong pipe) không truyền thẳng formatOutput vào dayjs().format(...). Thay vào đó, nó so khớp formatOutput với một danh sách alias cố định (qua getTypeByFormat) để quy về 1 trong 8 kiểu định dạng nội bộ. Nếu formatOutput không khớp bất kỳ alias nào trong bảng dưới, pipe sẽ ÂM THẦM dùng định dạng mặc định dmy hm (MMM D, YYYY HH:mm / D MMM, YYYY HH:mm) — KHÔNG throw lỗi, KHÔNG cảnh báo.
Bảng alias hợp lệ cho formatOutput (accepted values):
| Alias truyền vào formatOutput | Kiểu nội bộ | Output tiếng Anh (lang mặc định/'en') | Output tiếng Việt (lang: 'vi') |
|---|---|---|---|
| 'dm', 'MM-DD', 'dd/MM', 'dd/mm' | dm | MMM D → Dec 25 | D MMM → 25 Thg 12 |
| 'dmy', 'DD/MM/YYYY', 'YYYY-MM-DD', 'dd/MM/yyyy', 'dd-MM-yyyy', 'dd/mm/yyyy' | dmy | MMM D, YYYY → Dec 25, 2024 | D MMM, YYYY → 25 Thg 12, 2024 |
| 'dmy hm' (mặc định khi không truyền hoặc không khớp alias nào) | dmy hm | MMM D, YYYY HH:mm → Dec 25, 2024 00:00 | D MMM, YYYY HH:mm → 25 Thg 12, 2024 00:00 |
| 'my', 'M/YYYY', 'YYYY-MM', 'MM/yyyy' | my | MMM YYYY → Dec 2024 | MMM, YYYY → Thg 12, 2024 |
| 'dmy hms', 'YYYY/MM/DD hh:mm:ss', 'dd/mm/yyyy hh:mm:ss' | dmy hms | MMM D, YYYY HH:mm:ss → Dec 25, 2024 00:00:00 | D MMM, YYYY HH:mm:ss → 25 Thg 12, 2024 00:00:00 |
| 'hh:mm', 'HH:mm' | hh:mm | HH:mm → 00:00 | HH:mm → 00:00 |
| 'hh:mm A', 'HH:mm A' | hh:mm A | HH:mm A → 00:00 AM | HH:mm A → 00:00 AM |
| 'dd/mm/yyyy hh:mm:ss:ss', 'yyyy/mm/dd hh:mm:ss:ss', 'yyyy-mm-dd hh:mm:ss:ss', 'dd-mm-yyyy hh:mm:ss:ss' (không phân biệt hoa thường) | dmy hmsS | MMM D, YYYY HH:mm:ss:SSS | D MMM, YYYY HH:mm:ss:SSS |
| Bất kỳ chuỗi nào khác (vd: 'DD/MM/YYYY HH:mm:ss', 'dddd, DD MMMM YYYY', 'YYYY/MM/DD HH:mm') | Không khớp → fallback dmy hm | MMM D, YYYY HH:mm | D MMM, YYYY HH:mm |
⚠️ Hệ quả quan trọng nhất: Giá trị mặc định của tham số formatOutput là 'YYYY/MM/DD HH:mm' — chuỗi này không nằm trong bảng alias, nên khi KHÔNG truyền formatOutput, pipe KHÔNG trả về dạng 2024/12/25 00:00 mà trả về dạng mặc định dmy hm, ví dụ: Dec 25, 2024 00:00 (en) hoặc 25 Thg 12, 2024 00:00 (vi). Muốn format tùy ý theo cú pháp Dayjs gốc (không bị giới hạn bởi bảng alias), dùng LibsUiPipesGetDayjsPipe rồi gọi .format(...) trực tiếp (xem mục 4).
Khi nào sử dụng
- Cần hiển thị ngày/giờ theo 1 trong các dạng chuẩn có sẵn (ngày/tháng/năm, giờ:phút, giờ:phút:giây...) — xem bảng alias ở trên
- Cần định dạng thời gian có ngôn ngữ (thứ/tháng viết tắt bằng tiếng Việt hoặc tiếng Anh) theo các alias
dm/dmy/dmy hm/my/dmy hms/dmy hmsS - Cần parse chuỗi thời gian có định dạng lạ (vd:
10/03/2025 15:12) làm đầu vào (formatInput) - Cần format hoàn toàn tự do theo cú pháp Dayjs (vd:
dddd, DD MMMM YYYY) → dùngLibsUiPipesGetDayjsPipe+.format()thay vìLibsUiPipesFormatDatePipe - Xử lý timestamp Unix (giây) nhận về từ API backend
Cài đặt
npm install @libs-ui/pipes-format-dateImport
import { LibsUiPipesFormatDatePipe, LibsUiPipesGetDayjsPipe } from '@libs-ui/pipes-format-date';
@Component({
standalone: true,
imports: [LibsUiPipesFormatDatePipe, LibsUiPipesGetDayjsPipe],
// ...
})
export class MyComponent {}Ví dụ sử dụng
1. Định dạng mặc định (không truyền formatOutput)
import { Component } from '@angular/core';
import { LibsUiPipesFormatDatePipe } from '@libs-ui/pipes-format-date';
@Component({
standalone: true,
imports: [LibsUiPipesFormatDatePipe],
template: `
<p>{{ now | LibsUiPipesFormatDatePipe }}</p>
<!-- Output: Jul 2, 2026 18:09 (định dạng mặc định "dmy hm" — KHÔNG phải "2026/07/02 18:09") -->
`,
})
export class OrderCreatedAtComponent {
now = Date.now() / 1000; // Unix timestamp tính bằng giây
}2. Dùng alias hợp lệ và đa ngôn ngữ
import { Component } from '@angular/core';
import { LibsUiPipesFormatDatePipe } from '@libs-ui/pipes-format-date';
@Component({
standalone: true,
imports: [LibsUiPipesFormatDatePipe],
template: `
<!-- Alias 'DD/MM/YYYY' → quy về kiểu nội bộ "dmy" -->
<p>{{ dateStr | LibsUiPipesFormatDatePipe:'DD/MM/YYYY' }}</p>
<!-- Output: Dec 25, 2024 (KHÔNG phải 25/12/2024 — vì "dmy" luôn render "MMM D, YYYY") -->
<!-- Alias 'dmy hms' → có giờ phút giây -->
<p>{{ dateStr | LibsUiPipesFormatDatePipe:'dmy hms' }}</p>
<!-- Output: Dec 25, 2024 00:00:00 -->
<!-- Alias 'dmy' + lang 'vi' → tháng viết tắt tiếng Việt -->
<p>{{ timestamp | LibsUiPipesFormatDatePipe:'dmy':'vi' }}</p>
<!-- Output: 20 Thg 5, 2024 -->
<!-- Alias 'dmy hm' + lang 'en' -->
<p>{{ timestamp | LibsUiPipesFormatDatePipe:'dmy hm':'en' }}</p>
<!-- Output: May 20, 2024 15:30 -->
`,
})
export class EventDateComponent {
dateStr = '2024-12-25';
timestamp = 1716222600; // 20/05/2024 15:30:00 GMT+7
}⚠️ Muốn format tự do đúng nghĩa đen theo cú pháp Dayjs (vd: dddd, DD MMMM YYYY để ra "Thứ Hai, 20 Tháng Năm 2024") — pipe LibsUiPipesFormatDatePipe không hỗ trợ, vì mọi format truyền vào đều bị quy về 1 trong 8 alias ở bảng trên. Dùng LibsUiPipesGetDayjsPipe (mục 4) rồi gọi .format('dddd, DD MMMM YYYY') trực tiếp trên đối tượng Dayjs trả về.
3. Custom input format (parse chuỗi định dạng lạ)
import { Component } from '@angular/core';
import { LibsUiPipesFormatDatePipe } from '@libs-ui/pipes-format-date';
@Component({
standalone: true,
imports: [LibsUiPipesFormatDatePipe],
template: `
<!--
Chuỗi đầu vào có định dạng DD/MM/YYYY HH:mm → cần truyền formatInput
để dayjs parse đúng trước khi định dạng lại đầu ra.
formatOutput 'dmy hm' là alias hợp lệ (giữ nguyên giờ:phút).
-->
<p>
{{ rawDate | LibsUiPipesFormatDatePipe:'dmy hm':undefined:'DD/MM/YYYY HH:mm' }}
</p>
<!-- Output: Mar 10, 2025 15:12 -->
`,
})
export class ImportedDateComponent {
rawDate = '10/03/2025 15:12';
}4. Lấy đối tượng Dayjs trong template (LibsUiPipesGetDayjsPipe)
import { Component } from '@angular/core';
import { LibsUiPipesGetDayjsPipe } from '@libs-ui/pipes-format-date';
@Component({
standalone: true,
imports: [LibsUiPipesGetDayjsPipe],
template: `
@let dayjsObj = (dayjsConfig | LibsUiPipesGetDayjsPipe);
@if (dayjsObj) {
<p>Year: {{ dayjsObj.year() }}</p>
<p>Month: {{ dayjsObj.month() + 1 }}</p>
<p>Formatted: {{ dayjsObj.format('DD-MM-YYYY') }}</p>
}
`,
})
export class DateInfoComponent {
dayjsConfig = { date: '2024-05-20' };
}5. Dùng formatDate trực tiếp trong TypeScript (không inject pipe)
Pipe chỉ là thin wrapper của hàm formatDate từ @libs-ui/utils. Khi cần dùng trong logic TypeScript, import thẳng hàm đó — không cần inject pipe. Hành vi alias mô tả ở trên áp dụng y hệt cho hàm này.
import { formatDate } from '@libs-ui/utils';
// Không truyền formatOutput → alias mặc định 'dmy hm'
const result1 = formatDate(Date.now() / 1000);
// Output: 'Jul 2, 2026 18:09' (KHÔNG phải '2026/07/02 18:09')
// Alias 'dmy' hợp lệ
const result2 = formatDate('2024-12-25', 'dmy');
// Output: 'Dec 25, 2024'
// Alias 'dmy' + ngôn ngữ tiếng Việt
const result3 = formatDate(1716222600, 'dmy', 'vi');
// Output: '20 Thg 5, 2024'
// Với formatInput để parse chuỗi định dạng lạ (formatOutput là alias hợp lệ 'dmy hm')
const result4 = formatDate('10/03/2025 15:12', 'dmy hm', undefined, 'DD/MM/YYYY HH:mm');
// Output: 'Mar 10, 2025 15:12'
// formatOutput KHÔNG khớp alias nào → fallback 'dmy hm', KHÔNG throw lỗi
const result5 = formatDate('2024-12-25', 'DD/MM/YYYY HH:mm:ss A');
// Output: 'Dec 25, 2024 00:00' (không phải định dạng đã truyền vào)Transform — LibsUiPipesFormatDatePipe
Pipe name: LibsUiPipesFormatDatePipe
Cú pháp template:
value | LibsUiPipesFormatDatePipe : formatOutput : lang : formatInput| Tham số | Type | Bắt buộc | Default | Mô tả | Ví dụ |
|---|---|---|---|---|---|
| time (value) | string \| number \| dayjs.Dayjs \| null \| undefined | Không | undefined | Giá trị thời gian cần định dạng | 1716222600, '2024-12-25', dayjsObj |
| formatOutput | string | Không | 'YYYY/MM/DD HH:mm' | PHẢI là 1 trong các alias ở bảng "Hành vi quan trọng" phía trên — không phải cú pháp Dayjs tự do. Giá trị mặc định 'YYYY/MM/DD HH:mm' không khớp alias nào nên thực tế luôn fallback về dmy hm | 'dmy', 'DD/MM/YYYY', 'hh:mm A' |
| lang | string | Không | undefined | Locale hiển thị cho thứ/tháng (vi dùng tháng viết tắt kiểu Thg 5) | 'vi', 'en' |
| formatInput | string | Không | undefined | Định dạng của chuỗi đầu vào khi Dayjs không tự nhận diện được (đây là cú pháp Dayjs tự do, không bị giới hạn alias) | 'DD/MM/YYYY HH:mm', 'MM-DD-YYYY' |
Ví dụ template:
{{ now | LibsUiPipesFormatDatePipe }}
{{ dateStr | LibsUiPipesFormatDatePipe:'dmy' }}
{{ timestamp | LibsUiPipesFormatDatePipe:'dmy':'vi' }}
{{ rawDate | LibsUiPipesFormatDatePipe:'dmy hm':undefined:'DD/MM/YYYY HH:mm' }}Ví dụ standalone (không dùng template):
import { LibsUiPipesFormatDatePipe } from '@libs-ui/pipes-format-date';
const pipe = new LibsUiPipesFormatDatePipe();
pipe.transform('2024-12-25');
// Output: 'Dec 25, 2024 00:00' (alias mặc định 'dmy hm')
pipe.transform('2024-12-25', 'dmy');
// Output: 'Dec 25, 2024'
pipe.transform(1716222600, 'dmy', 'vi');
// Output: '20 Thg 5, 2024'
pipe.transform('10/03/2025 15:12', 'dmy hm', undefined, 'DD/MM/YYYY HH:mm');
// Output: 'Mar 10, 2025 15:12'
pipe.transform(null);
// Output: '' (chuỗi rỗng — không throw lỗi)
pipe.transform(undefined);
// Output: '' (chuỗi rỗng — không throw lỗi)Transform — LibsUiPipesGetDayjsPipe
Pipe name: LibsUiPipesGetDayjsPipe
Cú pháp template:
config | LibsUiPipesGetDayjsPipe| Tham số | Type | Bắt buộc | Mô tả | Ví dụ |
|---|---|---|---|---|
| config | { date?: dayjs.ConfigType; utc?: boolean; formatOfDate?: string } | Không | undefined | Config để tạo đối tượng Dayjs. Nếu không truyền, trả về Dayjs hiện tại | { date: '2024-05-20' }, { date: 1716222600, utc: true } |
| config.date | dayjs.ConfigType | Không | undefined | Giá trị thời gian: string, number (Unix giây), Dayjs object | '2024-05-20', 1716222600 |
| config.utc | boolean | Không | undefined | true để parse và trả về theo UTC | true |
| config.formatOfDate | string | Không | undefined | Định dạng của config.date khi là chuỗi không chuẩn | 'DD/MM/YYYY' |
Giá trị trả về: dayjs.Dayjs | undefined — trả về undefined khi config.date không hợp lệ hoặc rỗng.
Ví dụ template:
@let d = ({ date: '2024-05-20' } | LibsUiPipesGetDayjsPipe);
@if (d) {
<span>{{ d.format('DD/MM/YYYY') }}</span>
<span>{{ d.year() }} / {{ d.month() + 1 }}</span>
}Ví dụ standalone:
import { LibsUiPipesGetDayjsPipe } from '@libs-ui/pipes-format-date';
const pipe = new LibsUiPipesGetDayjsPipe();
const d = pipe.transform({ date: '2024-05-20' });
// d là đối tượng dayjs.Dayjs
d?.format('DD/MM/YYYY'); // '20/05/2024'
d?.year(); // 2024
d?.month(); // 4 (zero-based)
pipe.transform(undefined); // Trả về dayjs() hiện tại
pipe.transform({ date: undefined }); // Trả về undefinedLưu ý quan trọng
⚠️ formatOutput chỉ nhận alias cố định, KHÔNG phải cú pháp Dayjs tự do: Xem đầy đủ bảng alias ở mục "Hành vi quan trọng" phía trên. Truyền chuỗi format Dayjs tùy ý không nằm trong bảng sẽ bị âm thầm bỏ qua và fallback về dmy hm.
⚠️ Unix timestamp tính bằng giây, không phải milliseconds: Backend thường trả về Unix timestamp theo giây (1716222600). Nếu bạn có timestamp theo milliseconds (JavaScript Date.now()), cần chia 1000 trước khi truyền vào pipe: Date.now() / 1000.
⚠️ Múi giờ mặc định là Asia/Ho_Chi_Minh: Pipe dùng getDayjs từ @libs-ui/utils — đã được cấu hình mặc định múi giờ Asia/Ho_Chi_Minh. Nếu cần múi giờ UTC, dùng LibsUiPipesGetDayjsPipe với { utc: true }.
⚠️ Locale lang cần cấu hình Dayjs: Tham số lang chỉ hoạt động với các locale đã được import và đăng ký trong Dayjs. Mặc định @libs-ui/utils đã đăng ký sẵn vi và en.
⚠️ formatInput khi chuỗi đầu vào không chuẩn: Nếu truyền chuỗi như 10/03/2025 15:12 mà không có formatInput, Dayjs có thể parse sai hoặc trả về Invalid Date. Luôn cung cấp formatInput khi chuỗi đầu vào không ở dạng ISO 8601.
⚠️ Không dùng function trong template: Không gọi formatDate(...) trực tiếp trong template interpolation. Hãy dùng pipe hoặc khai báo computed() trong TypeScript.
<!-- SAI — gọi function trong template, tính toán lại mỗi change detection cycle -->
<p>{{ formatDate(order.createdAt) }}</p>
<!-- ĐÚNG — dùng pipe. 'dmy' là alias hợp lệ (xem bảng alias phía trên) -->
<p>{{ order.createdAt | LibsUiPipesFormatDatePipe:'dmy' }}</p>Unit Tests
npx nx test pipes-format-date
npx nx test pipes-format-date --coverage