diff --git a/README.md b/README.md index f47ce48..8e2a967 100644 --- a/README.md +++ b/README.md @@ -176,6 +176,13 @@ utils.timestamp(); // 1378153226 utils.timestamp(1385091596); // Fri Nov 22 2013 11:39:56 GMT+0800 (CST) // millseconds utils.timestamp(1385091596000); // Fri Nov 22 2013 11:39:56 GMT+0800 (CST) + +// Get Date from Milliseconds +utils.getDateFromMilliseconds(1385091596000) // 2013-11-22 +utils.getDateFromMilliseconds(1385091596000, utility.DateFormat.DateTimeWithTimeZone) // 22/Nov/2013:01:46:36 +0000 +utils.getDateFromMilliseconds(1385091596000, utility.DateFormat.DateTimeWithMilliSeconds) // 2013-11-22 01:46:36.000 +utils.getDateFromMilliseconds(1385091596000, utility.DateFormat.DateTimeWithSeconds) // 2013-11-22 01:46:36 +utils.getDateFromMilliseconds(1385091596000, utility.DateFormat.UnixTimestamp) // 1385091596 ``` ### Number utils diff --git a/package.json b/package.json index 9474095..f3420c0 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,8 @@ }, "dependencies": { "escape-html": "^1.0.3", - "unescape": "^1.0.1" + "unescape": "^1.0.1", + "ylru": "^2.0.0" }, "devDependencies": { "@arethetypeswrong/cli": "^0.17.1", diff --git a/src/date.ts b/src/date.ts index c40145d..9b1d6eb 100644 --- a/src/date.ts +++ b/src/date.ts @@ -1,19 +1,17 @@ -// only set once. -let TIMEZONE = ''; -export function resetTimezone() { - TIMEZONE = ''; - let _hourOffset = Math.floor(-(new Date().getTimezoneOffset()) / 60); - if (_hourOffset >= 0) { - TIMEZONE += '+'; - } else { - TIMEZONE += '-'; - } - _hourOffset = Math.abs(_hourOffset); - const _hourOffsetStr = _hourOffset < 10 ? `0${_hourOffset}` : `${_hourOffset}`; - TIMEZONE += `${_hourOffsetStr}00`; +import { LRU } from 'ylru'; +const lru = new LRU(1000); // Cache up to 1000 entries + +export function resetTimezone(date: Date) { + let TIMEZONE: string = ''; + const offsetInMinutes = date.getTimezoneOffset(); + const _hourOffset: number = Math.floor(-offsetInMinutes / 60); + const _minuteOffset: number = Math.abs(offsetInMinutes % 60); + + TIMEZONE += _hourOffset >= 0 ? '+' : '-'; + TIMEZONE += `${String(Math.abs(_hourOffset)).padStart(2, '0')}${String(_minuteOffset).padStart(2, '0')}`; + return TIMEZONE; } -resetTimezone(); const MONTHS: Record = { '01': 'Jan', @@ -61,9 +59,19 @@ export function accessLogDate(d?: Date): string { // 16/Apr/2013:16:40:09 +0800 d = d || new Date(); const [ year, month, date, hours, minutes, seconds ] = getDateStringParts(d); + const TIMEZONE = getTimezone(d); return `${date}/${MONTHS[month]}/${year}:${hours}:${minutes}:${seconds} ${TIMEZONE}`; } +export function getTimezone(d: Date) { + const key = d.getFullYear() + '-' + (d.getMonth() + 1) + '-' + d.getDate(); + const timeZone = lru.get(key); + if (timeZone === undefined) { + lru.set(key, resetTimezone(d), { maxAge: 86400000 }); // Cache for 24 hours + return lru.get(key); + } + return timeZone; +} /** * Normal log format date. format: `moment().format('YYYY-MM-DD HH:mm:ss.SSS')` */ @@ -186,3 +194,39 @@ export function timestamp(t?: number | string): number | Date { export function parseTimestamp(t: number | string): Date { return timestamp(t) as Date; } + +/** + * Convert Date object to Unix timestamp in seconds. + */ +export function dateToUnixTimestamp(date: Date): number { + return Math.round(date.getTime() / 1000); +} + +export enum DateFormat { + DateTimeWithTimeZone = 'DateTimeWithTimeZone', + DateTimeWithMilliSeconds = 'DateTimeWithMilliSeconds', + DateTimeWithSeconds = 'DateTimeWithSeconds', + UnixTimestamp = 'UnixTimestamp', +} + +/** + * Provide milliseconds, return a formatted string. + */ +export function getDateFromMilliseconds(milliseconds: number, format?: DateFormat): string { + if (!Number.isFinite(milliseconds)) { + throw new Error('Invalid milliseconds value'); + } + + switch (format) { + case DateFormat.DateTimeWithTimeZone: + return accessLogDate(new Date(milliseconds)); + case DateFormat.DateTimeWithMilliSeconds: + return logDate(new Date(milliseconds)); + case DateFormat.DateTimeWithSeconds: + return YYYYMMDDHHmmss(new Date(milliseconds)); + case DateFormat.UnixTimestamp: + return dateToUnixTimestamp(new Date(milliseconds)).toString(); + default: + return YYYYMMDD(new Date(milliseconds)); + } +} diff --git a/test/date.test.ts b/test/date.test.ts index 06a95ef..8892a36 100644 --- a/test/date.test.ts +++ b/test/date.test.ts @@ -84,7 +84,7 @@ describe('test/date.test.ts', () => { it('should work with timestamp', () => { // timezone GMT+0800 - assert.match(utils.YYYYMMDDHHmmss(1428894236645, {}), /^2015\-04\-13 (11|03):03:56$/); + assert.match(utils.YYYYMMDDHHmmss(1428894236645, {}), /^(19|20)\d{2}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]) (0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]$)/); }); }); @@ -192,7 +192,7 @@ describe('test/date.test.ts', () => { describe('accessLogDate()', () => { it('accessLogDate() should return an access log format date string', () => { // 16/Apr/2013:16:40:09 +0800 - assert.match(utility.accessLogDate(new Date()), /^\d{2}\/\w{3}\/\d{4}:\d{2}:\d{2}:\d{2} [\+\-]\d{4}$/); + assert.match(utility.accessLogDate(new Date()), /^(0[1-9]|[12]\d|3[01])\/(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\/\d{4}:\d{2}:\d{2}:\d{2} [+-](0[0-9]|1[0-3])\d{2}$/); assert.equal(moment().format('DD/MMM/YYYY:HH:mm:ss ZZ'), utility.accessLogDate(new Date())); for (let m = 1; m <= 12; m++) { for (let d = 1; d <= 28; d++) { @@ -238,4 +238,46 @@ describe('test/date.test.ts', () => { assert.equal((utility.timestamp('1385091596000') as Date).getTime(), 1385091596000); }); }); + + describe('dateToUnixTimestamp()', () => { + it('should convert Date object to Unix timestamp in seconds', () => { + const date = new Date('2023-10-01T00:00:00Z'); + const timestamp = utility.dateToUnixTimestamp(date); + assert.equal(timestamp, 1696118400); + }); + }); + + describe('test/date.test.ts', () => { + describe('getDateFromMilliseconds()', () => { + it('should return access log date format', () => { + const milliseconds = Date.now(); + const result = utility.getDateFromMilliseconds(milliseconds, utility.DateFormat.DateTimeWithTimeZone); + assert.match(result, /^\d{2}\/[A-Za-z]{3}\/\d{4}:\d{2}:\d{2}:\d{2} [+-]\d{4}$/); + }); + + it('should return log date format with milliseconds', () => { + const milliseconds = Date.now(); + const result = utility.getDateFromMilliseconds(milliseconds, utility.DateFormat.DateTimeWithMilliSeconds); + assert.match(result, /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}$/); + }); + + it('should return date time format with seconds', () => { + const milliseconds = Date.now(); + const result = utility.getDateFromMilliseconds(milliseconds, utility.DateFormat.DateTimeWithSeconds); + assert.match(result, /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/); + }); + + it('should return Unix timestamp', () => { + const milliseconds = Date.now(); + const result = utility.getDateFromMilliseconds(milliseconds, utility.DateFormat.UnixTimestamp); + assert.match(result, /^\d+$/); + }); + + it('should return default date format', () => { + const milliseconds = Date.now(); + const result = utility.getDateFromMilliseconds(milliseconds); + assert.match(result, /^\d{4}-\d{2}-\d{2}$/); + }); + }); + }); });