@@ -340,18 +340,15 @@ describe('parseTimeString', () => {
340340 }
341341 } ) ;
342342
343- it ( 'should throw on missing colon with format hint' , ( ) => {
344- try {
345- parseTimeString ( '1430' ) ;
346- expect . fail ( 'Should have thrown' ) ;
347- } catch ( e ) {
348- expect ( e ) . toBeInstanceOf ( ParseError ) ;
349- const message = ( e as ParseError ) . message ;
350- expect ( message ) . toBe (
351- 'Invalid time format: "1430". Expected format: "HH:MM" (e.g., "09:00", "14:30") or "H:MM AM/PM" (e.g., "9:07 AM") at token index 1' ,
352- ) ;
353- console . log ( 'Missing colon error:' , message ) ;
354- }
343+ it ( 'should parse ISO 8601 basic format: "1430"' , ( ) => {
344+ const result = parseTimeString ( '1430' ) ;
345+ expect ( result ) . toEqual ( {
346+ kind : 'Time' ,
347+ hour : 14 ,
348+ minute : 30 ,
349+ second : undefined ,
350+ fraction : undefined ,
351+ } ) ;
355352 } ) ;
356353
357354 it ( 'should throw on invalid hour in 24-hour format with helpful message' , ( ) => {
@@ -696,4 +693,283 @@ describe('parseTimeString', () => {
696693 } ) ;
697694 } ) ;
698695 } ) ;
696+
697+ describe ( 'bare single-digit hours' , ( ) => {
698+ it ( 'should parse single digit "0" as 00:00' , ( ) => {
699+ const result = parseTimeString ( '0' ) ;
700+ expect ( result ) . toEqual ( {
701+ kind : 'Time' ,
702+ hour : 0 ,
703+ minute : 0 ,
704+ second : undefined ,
705+ fraction : undefined ,
706+ } ) ;
707+ } ) ;
708+
709+ it ( 'should parse single digit "7" as 07:00' , ( ) => {
710+ const result = parseTimeString ( '7' ) ;
711+ expect ( result ) . toEqual ( {
712+ kind : 'Time' ,
713+ hour : 7 ,
714+ minute : 0 ,
715+ second : undefined ,
716+ fraction : undefined ,
717+ } ) ;
718+ } ) ;
719+
720+ it ( 'should parse single digit "9" as 09:00' , ( ) => {
721+ const result = parseTimeString ( '9' ) ;
722+ expect ( result ) . toEqual ( {
723+ kind : 'Time' ,
724+ hour : 9 ,
725+ minute : 0 ,
726+ second : undefined ,
727+ fraction : undefined ,
728+ } ) ;
729+ } ) ;
730+
731+ it ( 'should parse double digit "23" as 23:00' , ( ) => {
732+ const result = parseTimeString ( '23' ) ;
733+ expect ( result ) . toEqual ( {
734+ kind : 'Time' ,
735+ hour : 23 ,
736+ minute : 0 ,
737+ second : undefined ,
738+ fraction : undefined ,
739+ } ) ;
740+ } ) ;
741+
742+ it ( 'should parse "7 AM" as 7:00 AM' , ( ) => {
743+ const result = parseTimeString ( '7 AM' ) ;
744+ expect ( result ) . toEqual ( {
745+ kind : 'Time' ,
746+ hour : 7 ,
747+ minute : 0 ,
748+ second : undefined ,
749+ fraction : undefined ,
750+ } ) ;
751+ } ) ;
752+
753+ it ( 'should parse "12 PM" as 12:00 PM (noon)' , ( ) => {
754+ const result = parseTimeString ( '12 PM' ) ;
755+ expect ( result ) . toEqual ( {
756+ kind : 'Time' ,
757+ hour : 12 ,
758+ minute : 0 ,
759+ second : undefined ,
760+ fraction : undefined ,
761+ } ) ;
762+ } ) ;
763+
764+ it ( 'should parse "12 AM" as 12:00 AM (midnight)' , ( ) => {
765+ const result = parseTimeString ( '12 AM' ) ;
766+ expect ( result ) . toEqual ( {
767+ kind : 'Time' ,
768+ hour : 0 ,
769+ minute : 0 ,
770+ second : undefined ,
771+ fraction : undefined ,
772+ } ) ;
773+ } ) ;
774+
775+ it ( 'should parse "3 pm" (lowercase) as 15:00' , ( ) => {
776+ const result = parseTimeString ( '3 pm' ) ;
777+ expect ( result ) . toEqual ( {
778+ kind : 'Time' ,
779+ hour : 15 ,
780+ minute : 0 ,
781+ second : undefined ,
782+ fraction : undefined ,
783+ } ) ;
784+ } ) ;
785+
786+ it ( 'should throw on "0 AM" (invalid 12-hour format)' , ( ) => {
787+ try {
788+ parseTimeString ( '0 AM' ) ;
789+ expect . fail ( 'Should have thrown' ) ;
790+ } catch ( e ) {
791+ expect ( e ) . toBeInstanceOf ( ParseError ) ;
792+ }
793+ } ) ;
794+
795+ it ( 'should throw on "24" (invalid 24-hour format)' , ( ) => {
796+ try {
797+ parseTimeString ( '24' ) ;
798+ expect . fail ( 'Should have thrown' ) ;
799+ } catch ( e ) {
800+ expect ( e ) . toBeInstanceOf ( ParseError ) ;
801+ const message = ( e as ParseError ) . message ;
802+ expect ( message ) . toContain ( 'Invalid hour for 24-hour format: 24' ) ;
803+ }
804+ } ) ;
805+
806+ it ( 'should throw on "13 AM" (invalid 12-hour format)' , ( ) => {
807+ try {
808+ parseTimeString ( '13 AM' ) ;
809+ expect . fail ( 'Should have thrown' ) ;
810+ } catch ( e ) {
811+ expect ( e ) . toBeInstanceOf ( ParseError ) ;
812+ const message = ( e as ParseError ) . message ;
813+ expect ( message ) . toContain ( 'Invalid hour for 12-hour format: 13' ) ;
814+ }
815+ } ) ;
816+ } ) ;
817+
818+ describe ( 'ISO 8601 basic format (compact, no colons)' , ( ) => {
819+ it ( 'should parse "1430" as 14:30' , ( ) => {
820+ const result = parseTimeString ( '1430' ) ;
821+ expect ( result ) . toEqual ( {
822+ kind : 'Time' ,
823+ hour : 14 ,
824+ minute : 30 ,
825+ second : undefined ,
826+ fraction : undefined ,
827+ } ) ;
828+ } ) ;
829+
830+ it ( 'should parse "0900" as 09:00' , ( ) => {
831+ const result = parseTimeString ( '0900' ) ;
832+ expect ( result ) . toEqual ( {
833+ kind : 'Time' ,
834+ hour : 9 ,
835+ minute : 0 ,
836+ second : undefined ,
837+ fraction : undefined ,
838+ } ) ;
839+ } ) ;
840+
841+ it ( 'should parse "0000" as 00:00 (midnight)' , ( ) => {
842+ const result = parseTimeString ( '0000' ) ;
843+ expect ( result ) . toEqual ( {
844+ kind : 'Time' ,
845+ hour : 0 ,
846+ minute : 0 ,
847+ second : undefined ,
848+ fraction : undefined ,
849+ } ) ;
850+ } ) ;
851+
852+ it ( 'should parse "2359" as 23:59' , ( ) => {
853+ const result = parseTimeString ( '2359' ) ;
854+ expect ( result ) . toEqual ( {
855+ kind : 'Time' ,
856+ hour : 23 ,
857+ minute : 59 ,
858+ second : undefined ,
859+ fraction : undefined ,
860+ } ) ;
861+ } ) ;
862+
863+ it ( 'should parse "143045" as 14:30:45 (with seconds)' , ( ) => {
864+ const result = parseTimeString ( '143045' ) ;
865+ expect ( result ) . toEqual ( {
866+ kind : 'Time' ,
867+ hour : 14 ,
868+ minute : 30 ,
869+ second : 45 ,
870+ fraction : undefined ,
871+ } ) ;
872+ } ) ;
873+
874+ it ( 'should parse "090000" as 09:00:00' , ( ) => {
875+ const result = parseTimeString ( '090000' ) ;
876+ expect ( result ) . toEqual ( {
877+ kind : 'Time' ,
878+ hour : 9 ,
879+ minute : 0 ,
880+ second : 0 ,
881+ fraction : undefined ,
882+ } ) ;
883+ } ) ;
884+
885+ it ( 'should parse "235959" as 23:59:59' , ( ) => {
886+ const result = parseTimeString ( '235959' ) ;
887+ expect ( result ) . toEqual ( {
888+ kind : 'Time' ,
889+ hour : 23 ,
890+ minute : 59 ,
891+ second : 59 ,
892+ fraction : undefined ,
893+ } ) ;
894+ } ) ;
895+
896+ it ( 'should parse "143045.123" with fractional seconds' , ( ) => {
897+ const result = parseTimeString ( '143045.123' ) ;
898+ expect ( result ) . toEqual ( {
899+ kind : 'Time' ,
900+ hour : 14 ,
901+ minute : 30 ,
902+ second : 45 ,
903+ fraction : '123' ,
904+ } ) ;
905+ } ) ;
906+
907+ it ( 'should parse "143045,123" with comma separator' , ( ) => {
908+ const result = parseTimeString ( '143045,123' ) ;
909+ expect ( result ) . toEqual ( {
910+ kind : 'Time' ,
911+ hour : 14 ,
912+ minute : 30 ,
913+ second : 45 ,
914+ fraction : '123' ,
915+ } ) ;
916+ } ) ;
917+
918+ it ( 'should throw on invalid basic format hour "2430"' , ( ) => {
919+ try {
920+ parseTimeString ( '2430' ) ;
921+ expect . fail ( 'Should have thrown' ) ;
922+ } catch ( e ) {
923+ expect ( e ) . toBeInstanceOf ( ParseError ) ;
924+ const message = ( e as ParseError ) . message ;
925+ expect ( message ) . toContain ( 'Invalid hour for 24-hour format: 24' ) ;
926+ }
927+ } ) ;
928+
929+ it ( 'should throw on invalid basic format minute "1460"' , ( ) => {
930+ try {
931+ parseTimeString ( '1460' ) ;
932+ expect . fail ( 'Should have thrown' ) ;
933+ } catch ( e ) {
934+ expect ( e ) . toBeInstanceOf ( ParseError ) ;
935+ const message = ( e as ParseError ) . message ;
936+ expect ( message ) . toContain ( 'Invalid minute: 60' ) ;
937+ }
938+ } ) ;
939+
940+ it ( 'should throw on invalid basic format second "143060"' , ( ) => {
941+ try {
942+ parseTimeString ( '143060' ) ;
943+ expect . fail ( 'Should have thrown' ) ;
944+ } catch ( e ) {
945+ expect ( e ) . toBeInstanceOf ( ParseError ) ;
946+ const message = ( e as ParseError ) . message ;
947+ expect ( message ) . toContain ( 'Invalid second: 60' ) ;
948+ }
949+ } ) ;
950+
951+ it ( 'should not confuse 3-digit number "123" with basic format' , ( ) => {
952+ // "123" should be treated as bare hour 123, which will fail validation
953+ try {
954+ parseTimeString ( '123' ) ;
955+ expect . fail ( 'Should have thrown' ) ;
956+ } catch ( e ) {
957+ expect ( e ) . toBeInstanceOf ( ParseError ) ;
958+ const message = ( e as ParseError ) . message ;
959+ expect ( message ) . toContain ( 'Invalid hour for 24-hour format: 123' ) ;
960+ }
961+ } ) ;
962+
963+ it ( 'should not confuse 5-digit number "12345" with basic format' , ( ) => {
964+ // "12345" should be treated as bare hour 12345, which will fail validation
965+ try {
966+ parseTimeString ( '12345' ) ;
967+ expect . fail ( 'Should have thrown' ) ;
968+ } catch ( e ) {
969+ expect ( e ) . toBeInstanceOf ( ParseError ) ;
970+ const message = ( e as ParseError ) . message ;
971+ expect ( message ) . toContain ( 'Invalid hour for 24-hour format: 12345' ) ;
972+ }
973+ } ) ;
974+ } ) ;
699975} ) ;
0 commit comments