@ztimson/utils 0.29.0 → 0.29.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1233,9 +1233,46 @@ function dayOfYear(date) {
1233
1233
  function formatDate(format = "YYYY-MM-DD H:mm", date = /* @__PURE__ */ new Date(), tz = "local") {
1234
1234
  if (typeof date === "number" || typeof date === "string") date = new Date(date);
1235
1235
  if (isNaN(date.getTime())) throw new Error("Invalid date input");
1236
- const numericTz = typeof tz === "number";
1237
- const localTz = tz === "local" || !numericTz && tz.toLowerCase?.() === "local";
1238
- const tzName = localTz ? Intl.DateTimeFormat().resolvedOptions().timeZone : numericTz ? "UTC" : tz;
1236
+ const TIMEZONE_MAP = [
1237
+ { name: "IDLW", iana: "Etc/GMT+12", offset: -720 },
1238
+ { name: "SST", iana: "Pacific/Pago_Pago", offset: -660 },
1239
+ { name: "HST", iana: "Pacific/Honolulu", offset: -600 },
1240
+ { name: "AKST", iana: "America/Anchorage", offset: -540 },
1241
+ { name: "PST", iana: "America/Los_Angeles", offset: -480 },
1242
+ { name: "MST", iana: "America/Denver", offset: -420 },
1243
+ { name: "CST", iana: "America/Chicago", offset: -360 },
1244
+ { name: "EST", iana: "America/New_York", offset: -300 },
1245
+ { name: "AST", iana: "America/Halifax", offset: -240 },
1246
+ { name: "BRT", iana: "America/Sao_Paulo", offset: -180 },
1247
+ { name: "MAT", iana: "Atlantic/South_Georgia", offset: -120 },
1248
+ { name: "AZOT", iana: "Atlantic/Azores", offset: -60 },
1249
+ { name: "UTC", iana: "UTC", offset: 0 },
1250
+ { name: "CET", iana: "Europe/Paris", offset: 60 },
1251
+ { name: "EET", iana: "Europe/Athens", offset: 120 },
1252
+ { name: "MSK", iana: "Europe/Moscow", offset: 180 },
1253
+ { name: "GST", iana: "Asia/Dubai", offset: 240 },
1254
+ { name: "PKT", iana: "Asia/Karachi", offset: 300 },
1255
+ { name: "IST", iana: "Asia/Kolkata", offset: 330 },
1256
+ { name: "BST", iana: "Asia/Dhaka", offset: 360 },
1257
+ { name: "ICT", iana: "Asia/Bangkok", offset: 420 },
1258
+ { name: "CST", iana: "Asia/Shanghai", offset: 480 },
1259
+ { name: "JST", iana: "Asia/Tokyo", offset: 540 },
1260
+ { name: "AEST", iana: "Australia/Sydney", offset: 600 },
1261
+ { name: "SBT", iana: "Pacific/Guadalcanal", offset: 660 },
1262
+ { name: "TOT", iana: "Pacific/Tongatapu", offset: 780 },
1263
+ { name: "LINT", iana: "Pacific/Kiritimati", offset: 840 }
1264
+ ];
1265
+ let numericTz = typeof tz === "number";
1266
+ const localTz = tz === "local" || !numericTz && tz.toString().toLowerCase?.() === "local";
1267
+ let tzName = localTz ? Intl.DateTimeFormat().resolvedOptions().timeZone : numericTz ? "UTC" : tz;
1268
+ let offsetMinutes = 0;
1269
+ if (numericTz) {
1270
+ offsetMinutes = Math.abs(tz) < 24 ? tz * 60 : tz;
1271
+ const closest = TIMEZONE_MAP.reduce(
1272
+ (prev, curr) => Math.abs(curr.offset - offsetMinutes) < Math.abs(prev.offset - offsetMinutes) ? curr : prev
1273
+ );
1274
+ tzName = closest.iana;
1275
+ }
1239
1276
  if (!numericTz && tzName !== "UTC") {
1240
1277
  try {
1241
1278
  new Intl.DateTimeFormat("en-US", { timeZone: tzName }).format();
@@ -1285,8 +1322,7 @@ function formatDate(format = "YYYY-MM-DD H:mm", date = /* @__PURE__ */ new Date(
1285
1322
  }
1286
1323
  };
1287
1324
  } else {
1288
- const offset = numericTz ? tz : 0;
1289
- zonedDate = new Date(date.getTime() + offset * 60 * 60 * 1e3);
1325
+ zonedDate = new Date(date.getTime() + offsetMinutes * 60 * 1e3);
1290
1326
  get = (fn2) => zonedDate[`getUTC${fn2}`]();
1291
1327
  }
1292
1328
  function numSuffix2(n) {
@@ -1296,10 +1332,9 @@ function formatDate(format = "YYYY-MM-DD H:mm", date = /* @__PURE__ */ new Date(
1296
1332
  }
1297
1333
  function getTZOffset() {
1298
1334
  if (numericTz) {
1299
- const total = tz * 60;
1300
- const hours = Math.floor(Math.abs(total) / 60);
1301
- const mins = Math.abs(total) % 60;
1302
- return `${tz >= 0 ? "+" : "-"}${String(hours).padStart(2, "0")}:${String(mins).padStart(2, "0")}`;
1335
+ const hours = Math.floor(Math.abs(offsetMinutes) / 60);
1336
+ const mins = Math.abs(offsetMinutes) % 60;
1337
+ return `${offsetMinutes >= 0 ? "+" : "-"}${String(hours).padStart(2, "0")}:${String(mins).padStart(2, "0")}`;
1303
1338
  }
1304
1339
  try {
1305
1340
  const offset = new Intl.DateTimeFormat("en-US", { timeZone: tzName, timeZoneName: "longOffset", hour: "2-digit", minute: "2-digit" }).formatToParts(date).find((p) => p.type === "timeZoneName")?.value.match(/([+-]\d{2}:\d{2})/)?.[1];
@@ -1309,7 +1344,6 @@ function formatDate(format = "YYYY-MM-DD H:mm", date = /* @__PURE__ */ new Date(
1309
1344
  return "+00:00";
1310
1345
  }
1311
1346
  function getTZAbbr() {
1312
- if (numericTz && tz === 0) return "UTC";
1313
1347
  try {
1314
1348
  return new Intl.DateTimeFormat("en-US", { timeZone: tzName, timeZoneName: "short" }).formatToParts(date).find((p) => p.type === "timeZoneName")?.value || "";
1315
1349
  } catch {
@@ -3017,22 +3051,23 @@ function fromXml(xml) {
3017
3051
  if (xml[pos] !== "<") return parseText();
3018
3052
  pos++;
3019
3053
  if (xml[pos] === "?") {
3020
- parseDeclaration();
3021
- return parseNode();
3054
+ const declaration = parseDeclaration();
3055
+ return { ["?" + declaration]: "", ...parseNode() };
3022
3056
  }
3023
3057
  if (xml[pos] === "!") {
3024
3058
  parseComment();
3025
3059
  return parseNode();
3026
3060
  }
3027
3061
  const tagName = parseTagName();
3028
- const attributes = parseAttributes();
3062
+ parseAttributes();
3029
3063
  skipWhitespace();
3030
3064
  if (xml[pos] === "/" && xml[pos + 1] === ">") {
3031
3065
  pos += 2;
3032
- return { tag: tagName, attributes, children: [] };
3066
+ return { [tagName]: "" };
3033
3067
  }
3034
3068
  pos++;
3035
3069
  const children = [];
3070
+ let textContent = "";
3036
3071
  while (pos < xml.length) {
3037
3072
  skipWhitespace();
3038
3073
  if (xml[pos] === "<" && xml[pos + 1] === "/") {
@@ -3043,9 +3078,33 @@ function fromXml(xml) {
3043
3078
  break;
3044
3079
  }
3045
3080
  const child = parseNode();
3046
- if (child) children.push(child);
3081
+ if (typeof child === "string") {
3082
+ textContent += child;
3083
+ } else if (child) {
3084
+ children.push(child);
3085
+ }
3086
+ }
3087
+ if (children.length === 0 && textContent) {
3088
+ const value = isNumeric(textContent) ? Number(textContent) : textContent;
3089
+ return { [tagName]: value };
3047
3090
  }
3048
- return { tag: tagName, attributes, children };
3091
+ if (children.length === 0) {
3092
+ return { [tagName]: "" };
3093
+ }
3094
+ const result = {};
3095
+ for (const child of children) {
3096
+ for (const [key, value] of Object.entries(child)) {
3097
+ if (result[key]) {
3098
+ if (!Array.isArray(result[key])) {
3099
+ result[key] = [result[key]];
3100
+ }
3101
+ result[key].push(value);
3102
+ } else {
3103
+ result[key] = value;
3104
+ }
3105
+ }
3106
+ }
3107
+ return { [tagName]: result };
3049
3108
  }
3050
3109
  function parseTagName() {
3051
3110
  let name = "";
@@ -3078,8 +3137,14 @@ function fromXml(xml) {
3078
3137
  return text ? escapeXml(text, true) : null;
3079
3138
  }
3080
3139
  function parseDeclaration() {
3140
+ pos++;
3141
+ let name = "";
3142
+ while (pos < xml.length && xml[pos] !== " " && xml[pos] !== "?") {
3143
+ name += xml[pos++];
3144
+ }
3081
3145
  while (xml[pos] !== ">") pos++;
3082
3146
  pos++;
3147
+ return name;
3083
3148
  }
3084
3149
  function parseComment() {
3085
3150
  while (!(xml[pos] === "-" && xml[pos + 1] === "-" && xml[pos + 2] === ">")) pos++;
@@ -3088,6 +3153,9 @@ function fromXml(xml) {
3088
3153
  function skipWhitespace() {
3089
3154
  while (pos < xml.length && /\s/.test(xml[pos])) pos++;
3090
3155
  }
3156
+ function isNumeric(str) {
3157
+ return !isNaN(Number(str)) && !isNaN(parseFloat(str)) && str.trim() !== "";
3158
+ }
3091
3159
  return parseNode();
3092
3160
  }
3093
3161
  function toXml(obj, indent = "") {