🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

predict-data-types

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

predict-data-types - npm Package Compare versions

Comparing version
1.3.1
to
1.5.0
+212
-64
index.js

@@ -1,7 +0,1 @@

const dayjs = require('dayjs');
const customParseFormat = require('dayjs/plugin/customParseFormat');
// Enable dayjs plugins
dayjs.extend(customParseFormat);
// Cached compiled regex patterns for performance

@@ -13,4 +7,2 @@ const PATTERNS = {

EMAIL: /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}$/i,
DATE_COMPONENT: /(\d{4})-(\d{1,2})-(\d{1,2})/,
DATE_CHARS: /^[\d\-/\s:.TZ+-]+$/,
LEADING_ZERO: /^0\d/,

@@ -24,71 +16,227 @@ IPV4: /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/,

// Date format patterns supported for parsing (from re-date-parser + extensions)
const DATE_FORMATS = [
// ISO 8601 and variants with timezone
'YYYY-MM-DDTHH:mm:ss.SSSZ',
// With timezone offset
'YYYY/MM/DD HH:mm:ss.SSS z',
'YYYY-MM-DD HH:mm:ss.SSS z',
'MM/DD/YYYY HH:mm:ss.SSS z',
'DD/MM/YYYY HH:mm:ss.SSS z',
'YYYY/MM/DD HH:mm:ss z',
'YYYY-MM-DD HH:mm:ss z',
'MM/DD/YYYY HH:mm:ss z',
'DD/MM/YYYY HH:mm:ss z',
// With milliseconds
'YYYY/MM/DD HH:mm:ss.SSS',
'YYYY-MM-DD HH:mm:ss.SSS',
'MM/DD/YYYY HH:mm:ss.SSS',
'DD/MM/YYYY HH:mm:ss.SSS',
// With month names and milliseconds
'dd MMM yyyy HH:mm:ss.SSS z',
'MMM dd yyyy HH:mm:ss.SSS z',
'dd MMM yyyy HH:mm:ss.SSS',
'MMM dd yyyy HH:mm:ss.SSS',
// Standard date-time formats
'YYYY/MM/DD HH:mm:ss',
'YYYY-MM-DD HH:mm:ss',
'MM/DD/YYYY HH:mm:ss',
'DD/MM/YYYY HH:mm:ss',
// With month names
'dd MMM yyyy HH:mm:ss',
'MMM dd yyyy HH:mm:ss',
'dd-MMM-yyyy HH:mm:ss',
'MMM-dd-yyyy HH:mm:ss',
// Date with time (hours and minutes only)
'YYYY/MM/DD HH:mm',
'YYYY-MM-DD HH:mm',
'MM/DD/YYYY HH:mm',
'DD/MM/YYYY HH:mm',
'dd MMM yyyy HH:mm',
'MMM dd yyyy HH:mm',
// Date only formats
'YYYY/MM/DD',
'YYYY-MM-DD',
'MM/DD/YYYY',
'DD/MM/YYYY',
'MM-DD-YYYY',
'DD-MM-YYYY',
// With month names (date only)
'dd MMM yyyy',
'MMM dd yyyy',
'dd-MMM-yyyy',
'MMM-dd-yyyy'
];
/**
* Checks if a given value represents a valid date in various formats
* @param {string} value - The value to check
* @returns {boolean} True if the value is a valid date, false otherwise
* Parse month name to month index
* @param {string} monthString - Month name (e.g., 'Jan', 'January')
* @returns {number|null} Month index (0-11) or null if invalid
*/
function isDate(value) {
const formats = [
'YYYY-MM-DDTHH:mm:ss.SSSZ',
'YYYY-MM-DDTHH:mm:ss.SSSZ',
'YYYY-MM-DDTHH:mm:ssZ',
'YYYY-MM-DDTHH:mm:ss',
'YYYY-MM-DDTHH:mmZ',
'YYYY-MM-DDTHH:mm',
'YYYY-MM-DD HH:mm:ss.SSS',
'YYYY-MM-DD HH:mm:ss',
'YYYY-MM-DD HH:mm',
'YYYY-MM-DD',
'DD/MM/YYYY',
'DD/MM/YYYY HH:mm:ss',
'DD/MM/YYYY HH:mm',
'MM/DD/YYYY',
'MM/DD/YYYY HH:mm:ss',
'MM/DD/YYYY HH:mm',
'DD-MMM-YYYY',
'DD-MMM-YYYY HH:mm:ss',
'DD-MMM-YYYY HH:mm',
'MMM-DD-YYYY',
'MMM-DD-YYYY HH:mm:ss',
'MMM-DD-YYYY HH:mm'
function parseMonth(monthString) {
const monthNames = [
'jan', 'feb', 'mar', 'apr', 'may', 'jun',
'jul', 'aug', 'sep', 'oct', 'nov', 'dec'
];
const normalized = monthString.toLowerCase().substring(0, 3);
const monthIndex = monthNames.indexOf(normalized);
return monthIndex === -1 ? null : monthIndex;
}
// First try strict parsing with specific formats
for (let i = 0; i < formats.length; i++) {
const date = dayjs(value, formats[i], true);
if (date.isValid() && date.format(formats[i]) === value) {
return true;
/**
* Parse timezone offset string to minutes
* @param {string} tzString - Timezone string (e.g., '+05:30', '-08:00', 'Z')
* @returns {number|null} Offset in minutes or null if invalid
*/
function parseTimezone(tzString) {
if (tzString === 'Z' || tzString === 'z') return 0;
const match = tzString.match(/^([+-])(\d{2}):?(\d{2})?$/);
if (!match) return null;
const sign = match[1] === '+' ? 1 : -1;
const hours = parseInt(match[2], 10);
const minutes = parseInt(match[3] || '00', 10);
return sign * ((hours * 60) + minutes);
}
/**
* Parse a date string with a specific format
* @param {string} input - Date string to parse
* @param {string} format - Format pattern
* @returns {Date|null} Parsed Date object or null if invalid
*/
function parseWithFormat(input, format) {
const parts = input.trim().split(/[\s\/\-\:\.TZ]+/).filter(p => p);
const formatParts = format.trim().split(/[\s\/\-\:\.TZ]+/).filter(p => p);
if (parts.length < 3) return null; // At minimum need year, month, day
const dateValues = {};
let timezoneOffset = null;
let partIndex = 0;
for (let i = 0; i < formatParts.length && partIndex < parts.length; i++) {
const formatPart = formatParts[i];
const part = parts[partIndex];
if (formatPart === 'YYYY' || formatPart === 'yyyy') {
const year = parseInt(part, 10);
if (isNaN(year) || part.length !== 4) return null;
dateValues.year = year;
partIndex++;
} else if (formatPart === 'MM') {
const month = parseInt(part, 10);
if (isNaN(month) || month < 1 || month > 12) return null;
dateValues.month = month - 1;
partIndex++;
} else if (formatPart === 'MMM') {
const month = parseMonth(part);
if (month === null) return null;
dateValues.month = month;
partIndex++;
} else if (formatPart === 'DD' || formatPart === 'dd') {
const day = parseInt(part, 10);
if (isNaN(day) || day < 1 || day > 31) return null;
dateValues.day = day;
partIndex++;
} else if (formatPart === 'HH') {
const hour = parseInt(part, 10);
if (isNaN(hour) || hour < 0 || hour > 23) return null;
dateValues.hour = hour;
partIndex++;
} else if (formatPart === 'mm') {
const minute = parseInt(part, 10);
if (isNaN(minute) || minute < 0 || minute > 59) return null;
dateValues.minute = minute;
partIndex++;
} else if (formatPart === 'ss') {
const second = parseInt(part, 10);
if (isNaN(second) || second < 0 || second > 59) return null;
dateValues.second = second;
partIndex++;
} else if (formatPart === 'SSS') {
const ms = parseInt(part, 10);
if (isNaN(ms)) return null;
dateValues.millisecond = ms;
partIndex++;
} else if (formatPart === 'z') {
// Timezone offset
timezoneOffset = parseTimezone(part);
if (timezoneOffset === null) return null;
partIndex++;
}
}
// For ISO format and basic dates, be more conservative
// Only accept if it looks like a valid date and doesn't contain invalid characters
if (!PATTERNS.DATE_CHARS.test(value)) {
return false;
if (dateValues.year === undefined || dateValues.month === undefined || dateValues.day === undefined) {
return null;
}
const defaultParsed = dayjs(value);
if (defaultParsed.isValid() && value.length >= 8) { // At least YYYY-MM-DD length
// Check if the parsed date's string representation matches the input
// This prevents dayjs from "fixing" invalid dates like 2023-13-32
const reformatted = defaultParsed.format('YYYY-MM-DD');
if (value.startsWith(reformatted) || value === reformatted) {
const year = defaultParsed.year();
const month = defaultParsed.month() + 1;
const day = defaultParsed.date();
// Create date object (UTC if timezone specified, local otherwise)
const date = timezoneOffset !== null
? new Date(Date.UTC(
dateValues.year,
dateValues.month,
dateValues.day,
dateValues.hour || 0,
dateValues.minute || 0,
dateValues.second || 0,
dateValues.millisecond || 0
))
: new Date(
dateValues.year,
dateValues.month,
dateValues.day,
dateValues.hour || 0,
dateValues.minute || 0,
dateValues.second || 0,
dateValues.millisecond || 0
);
// Extract original components for validation
const dateMatch = value.match(PATTERNS.DATE_COMPONENT);
if (dateMatch) {
const [, origYear, origMonth, origDay] = dateMatch;
if (parseInt(origYear) === year &&
parseInt(origMonth) === month &&
parseInt(origDay) === day) {
return true;
}
}
// Apply timezone offset if present
if (timezoneOffset !== null) {
const localTime = date.getTime();
const localOffset = date.getTimezoneOffset() * 60000;
const targetOffset = timezoneOffset * 60000;
const targetTime = localTime - localOffset + targetOffset;
date.setTime(targetTime);
}
// Check if date rolled over (invalid date like Feb 30 becomes Mar 2)
if (date.getFullYear() !== dateValues.year ||
date.getMonth() !== dateValues.month ||
date.getDate() !== dateValues.day) {
return null;
}
return date;
}
/**
* Try to parse a date string with all supported formats
* @param {string} input - Date string to parse
* @returns {Date|null} Parsed Date object or null if invalid
*/
function tryParseDate(input) {
for (const format of DATE_FORMATS) {
const date = parseWithFormat(input, format);
if (date !== null) {
return date;
}
}
return null;
}
return false;
/**
* Checks if a given value represents a valid date in various formats
* @param {string} value - The value to check
* @returns {boolean} True if the value is a valid date, false otherwise
*/
function isDate(value) {
const trimmedValue = value.trim();
// Basic length check
if (trimmedValue.length < 8) return false;
// Try to parse with supported formats
const parsedDate = tryParseDate(trimmedValue);
return parsedDate !== null;
}

@@ -95,0 +243,0 @@

{
"name": "predict-data-types",
"version": "1.3.1",
"description": "A simple npm package that predicts data types for comma-separated values, including JSON objects, and validates URLs, phone numbers, email addresses, IP addresses, colors, percentages, and currency within string values.",
"version": "1.5.0",
"description": "A lightweight, zero-dependency npm package that predicts data types for comma-separated values, including JSON objects, and validates URLs, phone numbers, email addresses, IP addresses, colors, percentages, and currency within string values.",
"main": "index.js",

@@ -47,12 +47,10 @@ "author": "Melih Birim",

"license": "MIT",
"dependencies": {
"dayjs": "^1.11.18"
},
"dependencies": {},
"devDependencies": {
"@types/node": "^24.3.1",
"chai": "^4.3.7",
"eslint": "^9.35.0",
"mocha": "^10.2.0",
"typescript": "^5.9.2"
"@types/node": "^24.10.4",
"chai": "^4.5.0",
"eslint": "^9.39.2",
"mocha": "^10.8.2",
"typescript": "^5.9.3"
}
}
}

@@ -6,3 +6,3 @@ # Predict Data Types

A lightweight npm package that automatically predicts data types for comma-separated values. Supports 14 data types including primitives, URLs, emails, UUIDs, dates, IP addresses, colors, percentages, and currency.
A lightweight, zero-dependency npm package that automatically predicts data types for comma-separated values. Supports 14 data types including primitives, URLs, emails, UUIDs, dates, IP addresses, colors, percentages, and currency.

@@ -14,3 +14,3 @@ ## Features

- TypeScript definitions included
- Minimal dependencies (only dayjs)
- Zero dependencies - completely standalone
- Comprehensive test coverage

@@ -17,0 +17,0 @@ - Optimized regex patterns