@@ -1933,6 +1933,230 @@ describe('phantom.dates', () => {
19331933 } ) ;
19341934 } ) ;
19351935
1936+ describe ( 'format.detect' , ( ) => {
1937+ var originalOfPattern ;
1938+ var originalLocalDateParse ;
1939+ var originalLocalDateTimeParse ;
1940+
1941+ beforeEach ( ( ) => {
1942+ // Store originals
1943+ originalOfPattern = mockJavaTime . format . DateTimeFormatter . ofPattern ;
1944+ originalLocalDateParse = mockJavaTime . LocalDate . parse ;
1945+ originalLocalDateTimeParse = mockJavaTime . LocalDateTime . parse ;
1946+
1947+ // Create a smart mock that simulates format matching
1948+ mockJavaTime . format . DateTimeFormatter . ofPattern = jest . fn ( ( pattern ) => {
1949+ var formatter = {
1950+ parse : jest . fn ( ( str ) => {
1951+ // Simulate format matching based on pattern and string
1952+ var s = String ( str ) ;
1953+
1954+ // Check if pattern matches the string structure
1955+ if ( pattern === 'yyyy-MM-dd' && / ^ \d { 4 } - \d { 2 } - \d { 2 } $ / . test ( s ) ) {
1956+ return mockLocalDate ;
1957+ }
1958+ if ( pattern === "yyyy-MM-dd'T'HH:mm:ss" && / ^ \d { 4 } - \d { 2 } - \d { 2 } T \d { 2 } : \d { 2 } : \d { 2 } $ / . test ( s ) ) {
1959+ return mockLocalDateTime ;
1960+ }
1961+ if ( pattern === "yyyy-MM-dd'T'HH:mm:ss.SSS" && / ^ \d { 4 } - \d { 2 } - \d { 2 } T \d { 2 } : \d { 2 } : \d { 2 } \. \d { 3 } $ / . test ( s ) ) {
1962+ return mockLocalDateTime ;
1963+ }
1964+ if ( pattern === 'yyyy-MM-dd HH:mm:ss' && / ^ \d { 4 } - \d { 2 } - \d { 2 } \d { 2 } : \d { 2 } : \d { 2 } $ / . test ( s ) ) {
1965+ return mockLocalDateTime ;
1966+ }
1967+ // US format: MM/dd/yyyy - month must be 01-12
1968+ if ( pattern === 'MM/dd/yyyy' && / ^ ( \d { 2 } ) \/ ( \d { 2 } ) \/ ( \d { 4 } ) $ / . test ( s ) ) {
1969+ var parts = s . match ( / ^ ( \d { 2 } ) \/ ( \d { 2 } ) \/ ( \d { 4 } ) $ / ) ;
1970+ var month = parseInt ( parts [ 1 ] , 10 ) ;
1971+ if ( month >= 1 && month <= 12 ) {
1972+ return mockLocalDate ;
1973+ }
1974+ }
1975+ if ( pattern === 'MM/dd/yyyy HH:mm:ss' && / ^ ( \d { 2 } ) \/ ( \d { 2 } ) \/ ( \d { 4 } ) \d { 2 } : \d { 2 } : \d { 2 } $ / . test ( s ) ) {
1976+ var parts = s . match ( / ^ ( \d { 2 } ) \/ ( \d { 2 } ) \/ ( \d { 4 } ) / ) ;
1977+ var month = parseInt ( parts [ 1 ] , 10 ) ;
1978+ if ( month >= 1 && month <= 12 ) {
1979+ return mockLocalDateTime ;
1980+ }
1981+ }
1982+ // EU format: dd/MM/yyyy - day must be 01-31, month 01-12
1983+ if ( pattern === 'dd/MM/yyyy' && / ^ ( \d { 2 } ) \/ ( \d { 2 } ) \/ ( \d { 4 } ) $ / . test ( s ) ) {
1984+ var parts = s . match ( / ^ ( \d { 2 } ) \/ ( \d { 2 } ) \/ ( \d { 4 } ) $ / ) ;
1985+ var day = parseInt ( parts [ 1 ] , 10 ) ;
1986+ var month = parseInt ( parts [ 2 ] , 10 ) ;
1987+ // If day > 12, it's likely EU format (day first)
1988+ // If month > 12, it's definitely EU format
1989+ if ( day > 12 || month > 12 ) {
1990+ return mockLocalDate ;
1991+ }
1992+ // Ambiguous case - try both, but prefer EU if day > 12
1993+ if ( day > 12 ) {
1994+ return mockLocalDate ;
1995+ }
1996+ }
1997+ if ( pattern === 'dd/MM/yyyy HH:mm:ss' && / ^ ( \d { 2 } ) \/ ( \d { 2 } ) \/ ( \d { 4 } ) \d { 2 } : \d { 2 } : \d { 2 } $ / . test ( s ) ) {
1998+ var parts = s . match ( / ^ ( \d { 2 } ) \/ ( \d { 2 } ) \/ ( \d { 4 } ) / ) ;
1999+ var day = parseInt ( parts [ 1 ] , 10 ) ;
2000+ var month = parseInt ( parts [ 2 ] , 10 ) ;
2001+ // If day > 12, it's likely EU format (day first)
2002+ if ( day > 12 || month > 12 ) {
2003+ return mockLocalDateTime ;
2004+ }
2005+ }
2006+ if ( pattern === 'MM-dd-yyyy' && / ^ \d { 2 } - \d { 2 } - \d { 4 } $ / . test ( s ) ) {
2007+ return mockLocalDate ;
2008+ }
2009+ if ( pattern === 'dd.MM.yyyy' && / ^ \d { 2 } \. \d { 2 } \. \d { 4 } $ / . test ( s ) ) {
2010+ return mockLocalDate ;
2011+ }
2012+
2013+ // If no match, throw error
2014+ throw new Error ( 'Parse failed' ) ;
2015+ } )
2016+ } ;
2017+ return formatter ;
2018+ } ) ;
2019+
2020+ // Mock LocalDate.parse to work with formatter
2021+ mockJavaTime . LocalDate . parse = jest . fn ( ( str , formatter ) => {
2022+ if ( formatter ) {
2023+ return formatter . parse ( str ) ;
2024+ }
2025+ // Default ISO format
2026+ if ( / ^ \d { 4 } - \d { 2 } - \d { 2 } $ / . test ( String ( str ) ) ) {
2027+ return mockLocalDate ;
2028+ }
2029+ throw new Error ( 'Parse failed' ) ;
2030+ } ) ;
2031+
2032+ // Mock LocalDateTime.parse to work with formatter
2033+ mockJavaTime . LocalDateTime . parse = jest . fn ( ( str , formatter ) => {
2034+ if ( formatter ) {
2035+ return formatter . parse ( str ) ;
2036+ }
2037+ // Default ISO format
2038+ if ( / ^ \d { 4 } - \d { 2 } - \d { 2 } T \d { 2 } : \d { 2 } : \d { 2 } / . test ( String ( str ) ) ) {
2039+ return mockLocalDateTime ;
2040+ }
2041+ throw new Error ( 'Parse failed' ) ;
2042+ } ) ;
2043+ } ) ;
2044+
2045+ test ( 'should detect ISO date format' , ( ) => {
2046+ var result = phantom . dates . format . detect ( '2024-12-16' ) ;
2047+ expect ( result ) . toBeDefined ( ) ;
2048+ expect ( result . valid ) . toBe ( true ) ;
2049+ expect ( result . type ) . toBe ( 'date' ) ;
2050+ expect ( result . format ) . toBe ( 'yyyy-MM-dd' ) ;
2051+ expect ( result . name ) . toBe ( 'ISO_DATE' ) ;
2052+ } ) ;
2053+
2054+ test ( 'should detect ISO datetime format' , ( ) => {
2055+ var result = phantom . dates . format . detect ( "2024-12-16T14:30:00" ) ;
2056+ expect ( result ) . toBeDefined ( ) ;
2057+ expect ( result . valid ) . toBe ( true ) ;
2058+ expect ( result . type ) . toBe ( 'datetime' ) ;
2059+ expect ( result . format ) . toBe ( "yyyy-MM-dd'T'HH:mm:ss" ) ;
2060+ expect ( result . name ) . toBe ( 'ISO_DATETIME' ) ;
2061+ } ) ;
2062+
2063+ test ( 'should detect ISO datetime with milliseconds' , ( ) => {
2064+ var result = phantom . dates . format . detect ( "2024-12-16T14:30:00.123" ) ;
2065+ expect ( result ) . toBeDefined ( ) ;
2066+ expect ( result . valid ) . toBe ( true ) ;
2067+ expect ( result . type ) . toBe ( 'datetime' ) ;
2068+ expect ( result . format ) . toBe ( "yyyy-MM-dd'T'HH:mm:ss.SSS" ) ;
2069+ expect ( result . name ) . toBe ( 'ISO_DATETIME_MS' ) ;
2070+ } ) ;
2071+
2072+ test ( 'should detect US date format' , ( ) => {
2073+ var result = phantom . dates . format . detect ( '12/16/2024' ) ;
2074+ expect ( result ) . toBeDefined ( ) ;
2075+ expect ( result . valid ) . toBe ( true ) ;
2076+ expect ( result . type ) . toBe ( 'date' ) ;
2077+ expect ( result . format ) . toBe ( 'MM/dd/yyyy' ) ;
2078+ expect ( result . name ) . toBe ( 'US_DATE' ) ;
2079+ } ) ;
2080+
2081+ test ( 'should detect US datetime format' , ( ) => {
2082+ var result = phantom . dates . format . detect ( '12/16/2024 14:30:00' ) ;
2083+ expect ( result ) . toBeDefined ( ) ;
2084+ expect ( result . valid ) . toBe ( true ) ;
2085+ expect ( result . type ) . toBe ( 'datetime' ) ;
2086+ expect ( result . format ) . toBe ( 'MM/dd/yyyy HH:mm:ss' ) ;
2087+ expect ( result . name ) . toBe ( 'US_DATETIME' ) ;
2088+ } ) ;
2089+
2090+ test ( 'should detect EU date format' , ( ) => {
2091+ var result = phantom . dates . format . detect ( '16/12/2024' ) ;
2092+ expect ( result ) . toBeDefined ( ) ;
2093+ expect ( result . valid ) . toBe ( true ) ;
2094+ expect ( result . type ) . toBe ( 'date' ) ;
2095+ expect ( result . format ) . toBe ( 'dd/MM/yyyy' ) ;
2096+ expect ( result . name ) . toBe ( 'EU_DATE' ) ;
2097+ } ) ;
2098+
2099+ test ( 'should detect EU datetime format' , ( ) => {
2100+ var result = phantom . dates . format . detect ( '16/12/2024 14:30:00' ) ;
2101+ expect ( result ) . toBeDefined ( ) ;
2102+ expect ( result . valid ) . toBe ( true ) ;
2103+ expect ( result . type ) . toBe ( 'datetime' ) ;
2104+ expect ( result . format ) . toBe ( 'dd/MM/yyyy HH:mm:ss' ) ;
2105+ expect ( result . name ) . toBe ( 'EU_DATETIME' ) ;
2106+ } ) ;
2107+
2108+ test ( 'should return invalid for unrecognized format' , ( ) => {
2109+ var result = phantom . dates . format . detect ( 'invalid-date-format' ) ;
2110+ expect ( result ) . toBeDefined ( ) ;
2111+ expect ( result . valid ) . toBe ( false ) ;
2112+ expect ( result . format ) . toBeNull ( ) ;
2113+ expect ( result . name ) . toBeNull ( ) ;
2114+ expect ( result . type ) . toBeNull ( ) ;
2115+ } ) ;
2116+
2117+ test ( 'should fail on null/undefined input' , ( ) => {
2118+ expect ( ( ) => phantom . dates . format . detect ( null ) ) . toThrow ( ) ;
2119+ expect ( ( ) => phantom . dates . format . detect ( undefined ) ) . toThrow ( ) ;
2120+ } ) ;
2121+
2122+ test ( 'should fail on empty string' , ( ) => {
2123+ expect ( ( ) => phantom . dates . format . detect ( '' ) ) . toThrow ( ) ;
2124+ } ) ;
2125+
2126+ test ( 'should fail without Java.time' , ( ) => {
2127+ delete global . java . time ;
2128+ expect ( ( ) => phantom . dates . format . detect ( '2024-12-16' ) ) . toThrow ( ) ;
2129+ global . java . time = mockJavaTime ;
2130+ } ) ;
2131+
2132+ test ( 'should detect ISO datetime with space separator' , ( ) => {
2133+ var result = phantom . dates . format . detect ( '2024-12-16 14:30:00' ) ;
2134+ expect ( result ) . toBeDefined ( ) ;
2135+ expect ( result . valid ) . toBe ( true ) ;
2136+ expect ( result . type ) . toBe ( 'datetime' ) ;
2137+ expect ( result . format ) . toBe ( 'yyyy-MM-dd HH:mm:ss' ) ;
2138+ expect ( result . name ) . toBe ( 'ISO_DATETIME_SPACE' ) ;
2139+ } ) ;
2140+
2141+ test ( 'should detect date with dash separators (US)' , ( ) => {
2142+ var result = phantom . dates . format . detect ( '12-16-2024' ) ;
2143+ expect ( result ) . toBeDefined ( ) ;
2144+ expect ( result . valid ) . toBe ( true ) ;
2145+ expect ( result . type ) . toBe ( 'date' ) ;
2146+ expect ( result . format ) . toBe ( 'MM-dd-yyyy' ) ;
2147+ expect ( result . name ) . toBe ( 'US_DATE_DASH' ) ;
2148+ } ) ;
2149+
2150+ test ( 'should detect date with dot separators (EU)' , ( ) => {
2151+ var result = phantom . dates . format . detect ( '16.12.2024' ) ;
2152+ expect ( result ) . toBeDefined ( ) ;
2153+ expect ( result . valid ) . toBe ( true ) ;
2154+ expect ( result . type ) . toBe ( 'date' ) ;
2155+ expect ( result . format ) . toBe ( 'dd.MM.yyyy' ) ;
2156+ expect ( result . name ) . toBe ( 'EU_DATE_DOT' ) ;
2157+ } ) ;
2158+ } ) ;
2159+
19362160 describe ( 'UNIT constants' , ( ) => {
19372161 test ( 'should have unit constants' , ( ) => {
19382162 expect ( phantom . dates . UNIT . DAYS ) . toBe ( 'DAYS' ) ;
0 commit comments