Skip to content

Commit a49ddae

Browse files
feat: enhance DateParser to support STRING mode for date parsing
1 parent e05791d commit a49ddae

File tree

2 files changed

+67
-19
lines changed

2 files changed

+67
-19
lines changed

src/foam/parse/DateParser.java

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
*/
3535
public class DateParser {
3636

37-
public enum DateParseMode { DATE, DATETIME, DATETIME_UTC }
37+
public enum DateParseMode { DATE, STRING, DATETIME, DATETIME_UTC }
3838

3939
private Grammar grammar_;
4040

@@ -98,7 +98,7 @@ public Date parseString(String str, String opt_name) {
9898
str = str.trim();
9999
StringPStream sps = new StringPStream(str);
100100
ParserContext x = new ParserContextImpl();
101-
x.set("dateParseMode", DateParseMode.DATE);
101+
x.set("dateParseMode", DateParseMode.STRING); // STRING mode: date-only → noon GMT, with time → local time
102102

103103
PStream parseResult = grammar_.parse(sps, x, opt_name);
104104
if ( parseResult == null || parseResult.value() == null ) {
@@ -315,11 +315,39 @@ private Date buildDate(DateParseMode mode, int year, int month, int day,
315315
Calendar cal;
316316
switch (mode) {
317317
case DATE:
318+
// Always noon GMT - for parseDateString
318319
cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
319320
cal.clear();
320321
cal.set(year, month, day, 12, 0, 0);
321322
return cal.getTime();
323+
case STRING:
324+
// For parseString: date-only → noon GMT, with time → local time
325+
if ( hour < 0 && minute < 0 && second < 0 ) {
326+
// No time components - return noon GMT
327+
cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
328+
cal.clear();
329+
cal.set(year, month, day, 12, 0, 0);
330+
return cal.getTime();
331+
}
332+
// Has time - return local time
333+
if ( tz != null ) {
334+
int offset = parseTimezone(tz);
335+
cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
336+
cal.clear();
337+
cal.set(year, month, day, hour >= 0 ? hour : 0,
338+
minute >= 0 ? minute : 0, second >= 0 ? second : 0);
339+
if ( ms >= 0 ) cal.set(Calendar.MILLISECOND, ms);
340+
cal.add(Calendar.MINUTE, -offset);
341+
return cal.getTime();
342+
}
343+
cal = Calendar.getInstance();
344+
cal.clear();
345+
cal.set(year, month, day, hour >= 0 ? hour : 0,
346+
minute >= 0 ? minute : 0, second >= 0 ? second : 0);
347+
if ( ms >= 0 ) cal.set(Calendar.MILLISECOND, ms);
348+
return cal.getTime();
322349
case DATETIME:
350+
// Always local time with default hour 12 - for parseDateTime
323351
if ( tz != null ) {
324352
int offset = parseTimezone(tz);
325353
cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
@@ -337,6 +365,7 @@ private Date buildDate(DateParseMode mode, int year, int month, int day,
337365
if ( ms >= 0 ) cal.set(Calendar.MILLISECOND, ms);
338366
return cal.getTime();
339367
case DATETIME_UTC:
368+
// Always UTC - for parseDateTimeUTC
340369
cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
341370
cal.clear();
342371
cal.set(year, month, day, hour >= 0 ? hour : 0,

src/foam/parse/DateParser.js

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,27 @@ foam.CLASS({
3636
var offset, utcTime;
3737
switch ( mode ) {
3838
case 'DATE':
39+
// Always noon GMT - for parseDateString
3940
return new Date(Date.UTC(year, month, day, 12, 0, 0, 0));
41+
case 'STRING':
42+
// For parseString: date-only → noon GMT, with time → local time
43+
if ( hour < 0 && minute < 0 && second < 0 ) {
44+
// No time components - return noon GMT
45+
return new Date(Date.UTC(year, month, day, 12, 0, 0, 0));
46+
}
47+
// Has time - return local time
48+
if ( tz ) {
49+
offset = this.parseTimezone(tz);
50+
utcTime = Date.UTC(year, month, day,
51+
hour >= 0 ? hour : 0, minute >= 0 ? minute : 0,
52+
second >= 0 ? second : 0, ms >= 0 ? ms : 0);
53+
return new Date(utcTime - offset * 60000);
54+
}
55+
return new Date(year, month, day,
56+
hour >= 0 ? hour : 0, minute >= 0 ? minute : 0,
57+
second >= 0 ? second : 0, ms >= 0 ? ms : 0);
4058
case 'DATETIME':
59+
// Always local time with default hour 12 - for parseDateTime
4160
if ( tz ) {
4261
offset = this.parseTimezone(tz);
4362
utcTime = Date.UTC(year, month, day,
@@ -49,6 +68,7 @@ foam.CLASS({
4968
hour >= 0 ? hour : 12, minute >= 0 ? minute : 0,
5069
second >= 0 ? second : 0, ms >= 0 ? ms : 0);
5170
case 'DATETIME_UTC':
71+
// Always UTC - for parseDateTimeUTC
5272
utcTime = Date.UTC(year, month, day,
5373
hour >= 0 ? hour : 0, minute >= 0 ? minute : 0,
5474
second >= 0 ? second : 0, ms >= 0 ? ms : 0);
@@ -578,19 +598,19 @@ foam.CLASS({
578598

579599
function parseString(str, opt_name) {
580600
if ( ! str || str.trim() === '' ) {
581-
return this.validateDate(this.INVALID_DATE, str);
601+
throw new Error('Unsupported Date format: empty or null string');
582602
}
583603
str = str.trim();
584604

585-
// For parseString, detect mode based on result - use DATETIME as default
586-
this.dateParseMode = 'DATETIME';
605+
// STRING mode: date-only → noon GMT, with time → local time
606+
this.dateParseMode = 'STRING';
587607

588608
this.ps.setString(str);
589609
var start = this.getSymbol(opt_name || 'START');
590610
var parseResult = this.ps.apply(start, this);
591611

592-
if ( ! parseResult ) {
593-
return this.validateDate(this.INVALID_DATE, str);
612+
if ( ! parseResult || ! parseResult.value ) {
613+
throw new Error('Unsupported Date format: ' + str);
594614
}
595615

596616
if ( parseResult.pos < str.length ) {
@@ -602,7 +622,7 @@ foam.CLASS({
602622

603623
function parseDateString(str, opt_name) {
604624
if ( ! str || str.trim() === '' ) {
605-
return this.validateDate(this.INVALID_DATE, str);
625+
throw new Error('Unsupported Date format: empty or null string');
606626
}
607627
str = str.trim();
608628
this.dateParseMode = 'DATE';
@@ -611,16 +631,16 @@ foam.CLASS({
611631
var start = this.getSymbol(opt_name || 'START');
612632
var parseResult = this.ps.apply(start, this);
613633

614-
if ( ! parseResult ) {
615-
return this.validateDate(this.INVALID_DATE, str);
634+
if ( ! parseResult || ! parseResult.value ) {
635+
throw new Error('Unsupported Date format: ' + str);
616636
}
617637

618638
return parseResult.value;
619639
},
620640

621641
function parseDateTime(str, opt_name) {
622642
if ( ! str || str.trim() === '' ) {
623-
return this.validateDate(this.INVALID_DATE, str);
643+
throw new Error('Unsupported DateTime format: empty or null string');
624644
}
625645
str = str.trim();
626646
this.dateParseMode = 'DATETIME';
@@ -629,21 +649,21 @@ foam.CLASS({
629649
var start = this.getSymbol(opt_name || 'START');
630650
var parseResult = this.ps.apply(start, this);
631651

632-
if ( ! parseResult ) {
633-
return this.validateDate(this.INVALID_DATE, str);
652+
if ( ! parseResult || ! parseResult.value ) {
653+
throw new Error('Unsupported DateTime format: ' + str);
634654
}
635655

636656
if ( parseResult.pos < str.length ) {
637657
console.warn('DateParser: Partial parse in parseDateTime. Input:', str);
638-
return this.validateDate(this.INVALID_DATE, str);
658+
throw new Error('Unsupported DateTime format: ' + str);
639659
}
640660

641661
return parseResult.value;
642662
},
643663

644664
function parseDateTimeUTC(str, opt_name) {
645665
if ( ! str || str.trim() === '' ) {
646-
return this.validateDateUTC(this.INVALID_DATE, str);
666+
throw new Error('Unsupported DateTime format: empty or null string');
647667
}
648668
str = str.trim();
649669
this.dateParseMode = 'DATETIME_UTC';
@@ -652,13 +672,12 @@ foam.CLASS({
652672
var start = this.getSymbol(opt_name || 'START');
653673
var parseResult = this.ps.apply(start, this);
654674

655-
if ( ! parseResult ) {
656-
return this.validateDateUTC(this.INVALID_DATE, str);
675+
if ( ! parseResult || ! parseResult.value ) {
676+
throw new Error('Unsupported DateTime format: ' + str);
657677
}
658678

659679
if ( parseResult.pos < str.length ) {
660-
console.warn('DateParser: Partial parse in parseDateTimeUTC. Input:', str);
661-
return this.validateDateUTC(this.INVALID_DATE, str);
680+
throw new Error('Unsupported DateTime format: ' + str);
662681
}
663682

664683
return parseResult.value;

0 commit comments

Comments
 (0)