@@ -43,7 +43,8 @@ const getValue = (element, valueType) => {
4343 let suffix = element . getAttribute ( "value-suffix" ) || "" ;
4444
4545 // Determine elementType using type first, fallback to tagName (both lowercase and uppercase respectively)
46- const elementType = element . type ? element . type . toLowerCase ( ) : null ;
46+ // const elementType = element.type ? element.type.toLowerCase() : null;
47+ const elementType = element . getAttribute ( "type" ) || element . type
4748 const tagName = element . tagName . toUpperCase ( ) ;
4849
4950 // Switch statement to handle different element types and tagNames
@@ -196,139 +197,174 @@ const handleCheckbox = (element, prefix = "", suffix = "") => {
196197 * @param {string } valueType - Specifies the type of transformation to apply to the date/time value.
197198 * @returns {any } - The transformed or processed date/time value.
198199 */
200+ /**
201+ * Handles and transforms a date/time value with a 3-step pipeline:
202+ * 1. SNAP: Adjusts date to start/end of periods (Month, Week, Year).
203+ * 2. MATH: Adds/Subtracts Years, Months, Weeks, Days, Hours, Minutes, Seconds.
204+ * 3. FORMAT: Returns the final string/number representation.
205+ */
199206const handleDateTime = ( element , value , valueType ) => {
200- // Convert special string '$now' to current date
207+ const inputType = ( element . getAttribute ( "type" ) || element . type || "" ) . toLowerCase ( ) ;
208+ let date ;
201209 if ( value === "$now" ) {
202- value = new Date ( ) ;
210+ date = new Date ( ) ;
211+ } else if ( value instanceof Date ) {
212+ // If it's already a date object, clone it to avoid mutating references
213+ date = new Date ( value . getTime ( ) ) ;
203214 } else if ( value ) {
204- // Initialize a new Date from the string or object
205- value = new Date ( value ) ;
215+ if (
216+ typeof value === "string" &&
217+ / ^ \d { 4 } - \d { 2 } - \d { 2 } (?: [ T ] \d { 2 } : \d { 2 } (?: : \d { 2 } (?: \. \d { 3 } ) ? ) ? (?: Z | [ + - ] \d { 2 } : ? \d { 2 } ) ? ) ? $ / . test (
218+ value
219+ )
220+ ) {
221+ // Normalize datetime-local only when seconds are absent; preserve provided seconds.
222+ if ( inputType === "datetime-local" && / ^ [ 0 - 9 T : - ] { 16 } $ / . test ( value ) ) {
223+ value = `${ value } :00` ;
224+ }
225+ date = new Date ( value ) ;
226+ } else {
227+ date = new Date ( value ) ;
228+ }
206229 } else {
207- value = new Date ( ) ;
230+ console . warn ( "Provided date is invalid:" , value ) ;
231+ return "" ;
208232 }
209233
210- // Check if value is a valid date
211- if ( value instanceof Date && ! isNaN ( value . getTime ( ) ) ) {
212- // Default behavior if no specific valueType provided
213- if ( ! valueType ) {
214- value = value . toISOString ( ) ;
215- }
216-
217- // Format for 'time' type elements
218- if ( element . type === "time" && ! valueType ) {
219- value = value . substring ( 11 , 19 ) + "Z" ;
234+ if ( date instanceof Date && ! isNaN ( date . getTime ( ) ) ) {
235+ // These operations "floor" or "ceil" the date to a specific boundary.
236+ switch ( valueType ) {
237+ case "startOfDay" :
238+ date . setHours ( 0 , 0 , 0 , 0 ) ;
239+ break ;
240+ case "startOfWeek" :
241+ const startWkOff = parseInt (
242+ element . getAttribute ( "week-start-day" ) || 0 ,
243+ 10
244+ ) ;
245+ date . setDate ( date . getDate ( ) - date . getDay ( ) + startWkOff ) ;
246+ date . setHours ( 0 , 0 , 0 , 0 ) ;
247+ break ;
248+ case "endOfWeek" :
249+ const endWkOff = parseInt (
250+ element . getAttribute ( "week-start-day" ) || 0 ,
251+ 10
252+ ) ;
253+ date . setDate ( date . getDate ( ) - date . getDay ( ) + 6 + endWkOff ) ;
254+ date . setHours ( 23 , 59 , 59 , 999 ) ;
255+ break ;
256+ case "startOfMonth" :
257+ date . setDate ( 1 ) ;
258+ date . setHours ( 0 , 0 , 0 , 0 ) ;
259+ break ;
260+ case "endOfMonth" :
261+ date . setMonth ( date . getMonth ( ) + 1 , 0 ) ;
262+ date . setHours ( 23 , 59 , 59 , 999 ) ;
263+ break ;
264+ case "startOfYear" :
265+ date . setMonth ( 0 , 1 ) ;
266+ date . setHours ( 0 , 0 , 0 , 0 ) ;
267+ break ;
268+ case "endOfYear" :
269+ date . setMonth ( 11 , 31 ) ;
270+ date . setHours ( 23 , 59 , 59 , 999 ) ;
271+ break ;
220272 }
221273
222- if ( valueType ) {
223- switch ( valueType ) {
224- case "getDayName" :
225- const days = [
226- "Sunday" ,
227- "Monday" ,
228- "Tuesday" ,
229- "Wednesday" ,
230- "Thursday" ,
231- "Friday" ,
232- "Saturday"
233- ] ;
234- value = days [ value . getDay ( ) ] ;
235- break ;
236- case "getMonthName" :
237- const months = [
238- "January" ,
239- "February" ,
240- "March" ,
241- "April" ,
242- "May" ,
243- "June" ,
244- "July" ,
245- "August" ,
246- "September" ,
247- "October" ,
248- "November" ,
249- "December"
250- ] ;
251- value = months [ value . getMonth ( ) ] ;
252- break ;
253- case "getYear" :
254- case "getFullYear" :
255- // Returns the full 4-digit year (e.g., 2025)
256- value = value . getFullYear ( ) ;
257- break ;
258- case "toUnixTimestamp" :
259- value = Math . floor ( value . getTime ( ) / 1000 ) ;
260- break ;
261- case "toLocaleString" :
262- let locale = element . getAttribute ( "locale" ) || "en-US" ;
263- value = value . toLocaleString ( locale ) ;
264- break ;
265- case "addDays" :
266- // Add days to the current date
267- const addDays = parseInt ( element . getAttribute ( "add-days" ) || 0 , 10 ) ;
268- value . setDate ( value . getDate ( ) + addDays ) ;
269- value = value . toISOString ( ) ;
270- break ;
271- case "subtractDays" :
272- // Subtract days from the current date
273- const subtractDays = parseInt ( element . getAttribute ( "subtract-days" ) || 0 , 10 ) ;
274- value . setDate ( value . getDate ( ) - subtractDays ) ;
275- value = value . toISOString ( ) ;
276- break ;
277- case "startOfDay" :
278- // Get the start of the current day (12:00 midnight)
279- const startOfDay = new Date ( value ) ;
280- startOfDay . setHours ( 0 , 0 , 0 , 0 ) ; // Set time to midnight
281- value = startOfDay . toISOString ( ) ;
282- break ;
283- case "startOfWeek" :
284- // Get the start of the current week (Sunday by default)
285- const startOfWeekOffset = parseInt ( element . getAttribute ( "week-start-day" ) || 0 , 10 ) ; // Default to Sunday (0)
286- const startOfWeek = new Date ( value ) ;
287- startOfWeek . setDate ( value . getDate ( ) - value . getDay ( ) + startOfWeekOffset ) ;
288- startOfWeek . setHours ( 0 , 0 , 0 , 0 ) ; // Set to midnight
289- value = startOfWeek . toISOString ( ) ;
290- break ;
291- case "endOfWeek" :
292- // Get the end of the current week (Saturday by default)
293- const endOfWeekOffset = parseInt ( element . getAttribute ( "week-start-day" ) || 0 , 10 ) ; // Default to Sunday (0)
294- const endOfWeek = new Date ( value ) ;
295- endOfWeek . setDate ( value . getDate ( ) - value . getDay ( ) + 6 + endOfWeekOffset ) ;
296- endOfWeek . setHours ( 23 , 59 , 59 , 999 ) ; // Set to the end of the day
297- value = endOfWeek . toISOString ( ) ;
298- break ;
299- case "startOfMonth" :
300- // Get the start of the month
301- value = new Date ( value . getFullYear ( ) , value . getMonth ( ) , 1 ) . toISOString ( ) ;
302- break ;
303- case "endOfMonth" :
304- // Get the end of the month
305- value = new Date ( value . getFullYear ( ) , value . getMonth ( ) + 1 , 0 ) . toISOString ( ) ;
306- break ;
307- case "startOfYear" :
308- // Get the start of the year
309- value = new Date ( value . getFullYear ( ) , 0 , 1 ) . toISOString ( ) ;
310- break ;
311- case "endOfYear" :
312- // Get the end of the year
313- value = new Date ( value . getFullYear ( ) , 11 , 31 ) . toISOString ( ) ;
314- break ;
315-
274+ // --- PHASE 3: MATH (Modify the Date)
275+ // Helper to get integer value from attribute safely
276+ const getVal = ( attr ) =>
277+ parseInt ( element . getAttribute ( attr ) || "0" , 10 ) ;
278+
279+ // 1. Years
280+ const addYears = getVal ( "add-years" ) - getVal ( "subtract-years" ) ;
281+ if ( addYears ) date . setFullYear ( date . getFullYear ( ) + addYears ) ;
282+
283+ // 2. Months
284+ const addMonths = getVal ( "add-months" ) - getVal ( "subtract-months" ) ;
285+ if ( addMonths ) date . setMonth ( date . getMonth ( ) + addMonths ) ;
286+
287+ // 3. Weeks
288+ const addWeeks = getVal ( "add-weeks" ) - getVal ( "subtract-weeks" ) ;
289+ if ( addWeeks ) date . setDate ( date . getDate ( ) + addWeeks * 7 ) ;
290+
291+ // 4. Days
292+ const addDays = getVal ( "add-days" ) - getVal ( "subtract-days" ) ;
293+ if ( addDays ) date . setDate ( date . getDate ( ) + addDays ) ;
294+
295+ // 5. Hours
296+ const addHours = getVal ( "add-hours" ) - getVal ( "subtract-hours" ) ;
297+ if ( addHours ) date . setHours ( date . getHours ( ) + addHours ) ;
298+
299+ // 6. Minutes
300+ const addMinutes = getVal ( "add-minutes" ) - getVal ( "subtract-minutes" ) ;
301+ if ( addMinutes ) date . setMinutes ( date . getMinutes ( ) + addMinutes ) ;
302+
303+ // 7. Seconds
304+ const addSeconds = getVal ( "add-seconds" ) - getVal ( "subtract-seconds" ) ;
305+ if ( addSeconds ) date . setSeconds ( date . getSeconds ( ) + addSeconds ) ;
306+
307+
308+ // --- PHASE 4: FORMATTING (Output the Result) ---
309+ switch ( valueType ) {
310+ case "getDayName" :
311+ const days = [
312+ "Sunday" ,
313+ "Monday" ,
314+ "Tuesday" ,
315+ "Wednesday" ,
316+ "Thursday" ,
317+ "Friday" ,
318+ "Saturday" ,
319+ ] ;
320+ return days [ date . getDay ( ) ] ;
321+
322+ case "getMonthName" :
323+ const months = [
324+ "January" ,
325+ "February" ,
326+ "March" ,
327+ "April" ,
328+ "May" ,
329+ "June" ,
330+ "July" ,
331+ "August" ,
332+ "September" ,
333+ "October" ,
334+ "November" ,
335+ "December" ,
336+ ] ;
337+ return months [ date . getMonth ( ) ] ;
338+
339+ case "getYear" :
340+ case "getFullYear" :
341+ return date . getFullYear ( ) ;
342+
343+ case "toUnixTimestamp" :
344+ return Math . floor ( date . getTime ( ) / 1000 ) ;
345+
346+ case "toLocaleString" :
347+ const locale = element . getAttribute ( "locale" ) || "en-US" ;
348+ return date . toLocaleString ( locale ) ;
349+
350+ default :
351+ // Handle generic methods if specified
352+ if ( valueType && typeof date [ valueType ] === "function" ) {
353+ return date [ valueType ] ( ) ;
354+ }
316355
317- default :
318- if ( typeof value [ valueType ] === "function " ) {
319- value = value [ valueType ] ( ) ;
320- } else {
321- console . warn (
322- `The method ${ valueType } is not a function of Date object.`
323- ) ;
324- }
325- break ;
326- }
356+ const pad = ( n ) => String ( n ) . padStart ( 2 , "0" ) ;
357+ if ( inputType === "datetime-local " ) {
358+ // Return a datetime-local compatible string with seconds to avoid invalid values
359+ return ` ${ date . getFullYear ( ) } - ${ pad (
360+ date . getMonth ( ) + 1
361+ ) } - ${ pad ( date . getDate ( ) ) } T ${ pad ( date . getHours ( ) ) } : ${ pad (
362+ date . getMinutes ( )
363+ ) } : ${ pad ( date . getSeconds ( ) ) } ` ;
364+ }
365+ return date . toISOString ( ) ;
327366 }
328- } else {
329- console . warn ( "Provided date is invalid or could not be parsed:" , value ) ;
330367 }
331- return value ;
332368} ;
333369
334370/**
0 commit comments