diff --git a/js/date.altimpl.ts b/js/date.altimpl.ts new file mode 100644 index 0000000..5f90723 --- /dev/null +++ b/js/date.altimpl.ts @@ -0,0 +1,142 @@ +// alternative implementation +/** + * 计算时间间隔中星期的天数 + * @param before + * @param after + * @returns [总天数, 周一天数, 周二天数, 周三天数, 周四天数, 周五天数, 周六天数, 周日天数] + */ +export function getWeekday( + before: Date, + after: Date, +): [number, number, number, number, number, number, number, number] { + if (before > after) { + throw new Error('before cannot > after'); + } + + let weekdayall = 0; + let weekday1 = 0; + let weekday2 = 0; + let weekday3 = 0; + let weekday4 = 0; + let weekday5 = 0; + let weekday6 = 0; + let weekday7 = 0; + // 获取 before 的第一时间 + const beforeDay = new Date( + `${before.getFullYear()}-${before.getMonth() + 1}-${before.getDate()}`, + ); + // 获取 after 的最后时间 + const afterDay = new Date( + `${after.getFullYear()}-${after.getMonth() + 1}-${after.getDate()}`, + ); + // 24 * 60 * 60 * 1,000 = 86,400,000 毫秒 + const MILLISECONDS_ONE_DAY = 86400000; + + // 两个时间中间包含多少天 + let days = (afterDay.getTime() - beforeDay.getTime()) / + MILLISECONDS_ONE_DAY + 1; + weekdayall = days; + + // 如果不是从周一开始 + switch (before.getDay()) { + case 2: + weekday2++; + weekday3++; + weekday4++; + weekday5++; + weekday6++; + weekday7++; + days -= 6; + break; + case 3: + weekday3++; + weekday4++; + weekday5++; + weekday6++; + weekday7++; + days -= 5; + break; + case 4: + weekday4++; + weekday5++; + weekday6++; + weekday7++; + days -= 4; + break; + case 5: + weekday5++; + weekday6++; + weekday7++; + days -= 3; + break; + case 6: + weekday6++; + weekday7++; + days -= 2; + break; + case 0: + weekday7++; + days -= 1; + break; + default: + break; + } + + // 如果不是从周日结束 + switch (after.getDay()) { + case 1: + weekday1++; + days -= 1; + break; + case 2: + weekday1++; + weekday2++; + days -= 2; + break; + case 3: + weekday1++; + weekday2++; + weekday3++; + days -= 3; + break; + case 4: + weekday1++; + weekday2++; + weekday3++; + weekday4++; + days -= 4; + break; + case 5: + weekday1++; + weekday2++; + weekday3++; + weekday4++; + weekday5++; + days -= 5; + break; + case 6: + weekday1++; + weekday2++; + weekday3++; + weekday4++; + weekday5++; + weekday6++; + days -= 6; + break; + default: + break; + } + + const weeks = Math.floor(days / 7); + + return [ + weekdayall, + weekday1 += weeks, + weekday2 += weeks, + weekday3 += weeks, + weekday4 += weeks, + weekday5 += weeks, + weekday6 += weeks, + weekday7 += weeks, + ]; +} diff --git a/js/date.ts b/js/date.ts index 352de4c..df82ba4 100644 --- a/js/date.ts +++ b/js/date.ts @@ -445,3 +445,52 @@ export function parseDate( return { y, m, d, h, min, s }; } + +/** + * 计算时间间隔中星期的天数 + * @param before + * @param after + * @returns [总天数, 周一天数, 周二天数, 周三天数, 周四天数, 周五天数, 周六天数, 周日天数] + */ +export function getWeekday( + before: Date, + after: Date, +): [number, number, number, number, number, number, number, number] { + // 获取 before 的第一时间 + const beforeDay = new Date( + `${before.getFullYear()}-${before.getMonth() + 1}-${before.getDate()}`, + ); + // 获取 after 的最后时间 + const afterDay = new Date( + `${after.getFullYear()}-${after.getMonth() + 1}-${after.getDate()}`, + ); + // 24 * 60 * 60 * 1,000 = 86,400,000 毫秒 + const MILLISECONDS_ONE_DAY = 86400000; + + /** 两个时间中间包含多少天 */ + const days = Math.round( + (afterDay.getTime() - beforeDay.getTime()) / MILLISECONDS_ONE_DAY, + ) + 1; + + const beforeWeekday = before.getDay(); + const afterWeekday = after.getDay(); + const weeks = ( + days - (8 - beforeWeekday) % 7 - afterWeekday + ) / 7; + + return [ + days, + (afterWeekday !== 0 ? 1 : 0) + weeks, + (beforeWeekday === 2 ? 1 : 0) + (afterWeekday >= 2 ? 1 : 0) + weeks, + (beforeWeekday === 2 || beforeWeekday === 3 ? 1 : 0) + + (afterWeekday >= 3 ? 1 : 0) + + weeks, + (beforeWeekday >= 2 && beforeWeekday <= 4 ? 1 : 0) + + (afterWeekday >= 4 ? 1 : 0) + weeks, + (beforeWeekday >= 2 && beforeWeekday <= 5 ? 1 : 0) + + (afterWeekday >= 5 ? 1 : 0) + weeks, + (beforeWeekday >= 2 && beforeWeekday <= 6 ? 1 : 0) + + (afterWeekday === 6 ? 1 : 0) + weeks, + (beforeWeekday !== 1 ? 1 : 0) + weeks, + ]; +} diff --git a/js/hash.ts b/js/hash.ts index ff3afb2..04e2dd3 100644 --- a/js/hash.ts +++ b/js/hash.ts @@ -1,6 +1,21 @@ +/** + * 校验或签名数据, 目的在于保护数据不被篡改及确认数据来源可靠. + * + * 主要有三种方式: + * + * 1. 非加密. + * 2. HMAC 签名. + * 3. RSA 签名. + */ import { encode } from "../lib/hex"; import { decode } from "../lib/base64"; +/** + * 获取数据散列值 + * @param algorithm 可选 SHA-1 等算法, 不支持 MD5 + * @param d 需要计算散列值的数据 + * @returns 数据的散列值 + */ export async function hashString( algorithm: "SHA-1" | "SHA-256" | "SHA-512", d: string | Uint8Array @@ -66,7 +81,7 @@ export async function hmac( * @param d * @returns */ -export async function rasSign( +export async function rsaSign( key: { hash: 'SHA-256' | 'SHA-512'; /** secret key */ diff --git a/js/lz-string.ts b/js/lz-string.ts index fb468b9..16f4ced 100644 --- a/js/lz-string.ts +++ b/js/lz-string.ts @@ -14,7 +14,9 @@ const f = String.fromCharCode; const keyStrUriSafe = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-!'; -const baseReverseDic: Record = {}; +export const keyStrBase64Safe = + 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; + const baseReverseDic: Record = {}; /** * 乱序字符串 diff --git a/js/str.ts b/js/str.ts index c301a37..83556f3 100644 --- a/js/str.ts +++ b/js/str.ts @@ -90,3 +90,33 @@ export function fromUnicodeStr(str: string): string { } return encoded; } + +/** + * 为文本添加 UTF-8 BOM (for Windows...) + * - [Magic Number](https://en.wikipedia.org/wiki/Magic_number_(programming)) + * - [Byte Order Mark](https://en.wikipedia.org/wiki/Byte_order_mark) + * @param v 需要增加 BOM 的文本 + * @returns + */ +export function textWithBOM(v: Uint8Array | string): Uint8Array { + let data: Uint8Array; + if (typeof v === 'string') { + const encoder = new TextEncoder(); + data = encoder.encode(v); + } else { + // 避免重复增加 BOM + if (v[0] === 239 && v[1] === 187 && v[2] === 191) { + return v; + } + // 基于性能考虑, 不会更严格检查 Uint8Array 有效性 + data = v; + } + + /** EF BB BF */ + const mn = [239, 187, 191]; + const u8a = new Uint8Array(data.length + 3); // mn.length is 3 + u8a.set(mn); + u8a.set(data, 3); // mn.length is 3 + + return u8a; +} diff --git a/ts/error.ts b/ts/error.ts new file mode 100644 index 0000000..f6e4c31 --- /dev/null +++ b/ts/error.ts @@ -0,0 +1,10 @@ +/** + * 断言传入值是一个 Error + * 常用在错误处理 catch 中. + * @param v + */ +export function assertError(v: unknown): asserts v is Error { + if (!(v instanceof Error)) { + throw new TypeError(`should be Error but ${v}`); + } +}