38 lines
		
	
	
		
			1.0 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			38 lines
		
	
	
		
			1.0 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| // app/utils/datetime.ts
 | |
| export function formatCrmTimestamp(
 | |
|   input: string | null | undefined,
 | |
|   opts?: { locale?: string; timeZone?: string; includeSeconds?: boolean }
 | |
| ): string {
 | |
|   if (!input) return 'N/A';
 | |
| 
 | |
|   // Accept: 2025-06-30 10:04:58.387651 (also with 'T', with/without fraction)
 | |
|   const m = String(input).trim().match(
 | |
|     /^(\d{4})-(\d{2})-(\d{2})[ T](\d{2}):(\d{2}):(\d{2})(?:\.(\d{1,6}))?$/
 | |
|   );
 | |
|   if (!m) return input; // fallback: show as-is
 | |
| 
 | |
|   const [, y, mo, d, hh, mm, ss, frac = ''] = m;
 | |
|   const ms = Number((frac + '000').slice(0, 3)); // micro→millis
 | |
| 
 | |
|   const dt = new Date(
 | |
|     Number(y),
 | |
|     Number(mo) - 1,
 | |
|     Number(d),
 | |
|     Number(hh),
 | |
|     Number(mm),
 | |
|     Number(ss),
 | |
|     ms
 | |
|   );
 | |
| 
 | |
|   const locale = opts?.locale ?? 'en-MY';
 | |
|   const timeZone = opts?.timeZone ?? 'Asia/Kuala_Lumpur';
 | |
|   const timeStyle = opts?.includeSeconds ? 'medium' : 'short';
 | |
| 
 | |
|   return new Intl.DateTimeFormat(locale, {
 | |
|     dateStyle: 'medium',
 | |
|     timeStyle,         // 'short'=no seconds, 'medium'=with seconds
 | |
|     timeZone,
 | |
|     hour12: true,
 | |
|   }).format(dt);
 | |
| }
 |