Skip to content

Commit

Permalink
all but 4 tests passing
Browse files Browse the repository at this point in the history
  • Loading branch information
kensnyder committed Sep 16, 2024
1 parent 491b751 commit 83e5c46
Show file tree
Hide file tree
Showing 20 changed files with 287 additions and 237 deletions.
79 changes: 40 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ There are three ways to use any-date-parser:

Example:

```js
```ts
require('any-date-parser');
Date.fromString('2020-10-15');
// same as new Date(2020, 9, 15, 0, 0, 0, 0)
Expand All @@ -71,7 +71,7 @@ Date.fromString('2020-10-15');

Example:

```js
```ts
const parser = require('any-date-parser');
parser.fromString('2020-10-15');
// same as new Date(2020, 9, 15, 0, 0, 0, 0)
Expand All @@ -81,16 +81,17 @@ parser.fromString('2020-10-15');
returns an object with one or more integer values for the following keys: year,
month, day, hour, minute, second, millisecond, offset. Example:

```js
```ts
const parser = require('any-date-parser');
parser.attempt('15 Oct 2020 at 6pm');
// returns:
/* returns:
{
year: 2020,
month: 10,
day: 15,
hour: 18,
}
*/
```

4.) There are npm packages that integrate any-date-parser directly into popular
Expand Down Expand Up @@ -154,15 +155,15 @@ Second, parsers must have `units` or `handler`.

### Example 1: matcher + units

```js
```ts
const parser,
{ Format } = require('any-date-parser');
{ Format } = require('any-date-parser');

parser.addFormat(
new Format({
matcher: /^(\d+) days? into month (\d+) in year (\d{4})$/,
units: ['day', 'month', 'year'],
})
new Format({
matcher: /^(\d+) days? into month (\d+) in year (\d{4})$/,
units: ['day', 'month', 'year'],
})
);
```

Expand All @@ -173,59 +174,59 @@ or Bengali. To support those you can use the `template` option given in

### Example 2: matcher + handler

```js
```ts
const parser,
{ Format } = require('any-date-parser');
{ Format } = require('any-date-parser');

parser.addFormat(
new Format({
matcher: /^Q([1-4]) (\d{4})$/,
handler: function ([, quarter, year]) {
const monthByQuarter = { 1: 1, 2: 4, 3: 7, 4: 10 };
const month = monthByQuarter[quarter];
return { year, month };
},
})
new Format({
matcher: /^Q([1-4]) (\d{4})$/,
handler: function ([, quarter, year]) {
const monthByQuarter = { 1: 1, 2: 4, 3: 7, 4: 10 };
const month = monthByQuarter[quarter];
return { year, month };
},
})
);
```

### Example 3: template + units

```js
```ts
const parser,
{ Format } = require('any-date-parser');
{ Format } = require('any-date-parser');

parser.addFormat(
new Format({
template: 'The (_DAY_)(?:_ORDINAL_) day of (_MONTH_), (_YEAR_)',
units: ['day', 'month', 'year'],
})
new Format({
template: 'The (_DAY_)(?:_ORDINAL_) day of (_MONTH_), (_YEAR_)',
units: ['day', 'month', 'year'],
})
);
```

### Example 4: template + handler

```js
```ts
const parser,
{ Format } = require('any-date-parser');
{ Format } = require('any-date-parser');

parser.addFormat(
new Format({
template: '^Q([1-4]) (_YEAR_)$',
handler: function ([, quarter, year]) {
const monthByQuarter = { 1: 1, 2: 4, 3: 7, 4: 10 };
const month = monthByQuarter[quarter];
return { year, month };
},
})
new Format({
template: '^Q([1-4]) (_YEAR_)$',
handler: function ([, quarter, year]) {
const monthByQuarter = { 1: 1, 2: 4, 3: 7, 4: 10 };
const month = monthByQuarter[quarter];
return { year, month };
},
})
);
```

### Removing parsing rules

To remove support for a certain format, use `removeFormat()`

```js
```ts
const parser = require('any-date-parser');
const dayMonth = require('any-date-parser/src/formats/dayMonth/dayMonth.js');

Expand All @@ -237,7 +238,7 @@ parser.removeFormat(dayMonth);
To create a new parser with a limited list of formats or your own custom
formats, use `new Parser`

```js
```ts
const { Parser } = require('any-date-parser');
const time24Hours = require('any-date-parser/src/formats/time24Hours/time24Hours.js');
const yearMonthDay = require('any-date-parser/src/formats/yearMonthDay/yearMonthDay.js');
Expand All @@ -249,7 +250,7 @@ parser.addFormats([time24Hours, yearMonthDay, ago]);

You can convert your custom parser to a function. For example:

```js
```ts
const { Parser } = require('any-date-parser');
const parser = new Parser();
// ....
Expand Down
20 changes: 6 additions & 14 deletions index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
// import our main modules
import Format from './src/Format/Format';
import LocaleHelper from './src/LocaleHelper/LocaleHelper.js';
import Parser from './src/Parser/Parser';
// import our formats
import defaultLocale from './src/data/defaultLocale';
import ago from './src/formats/ago/ago';
import atSeconds from './src/formats/atSeconds/atSeconds';
import chinese from './src/formats/chinese/chinese';
Expand Down Expand Up @@ -57,17 +53,13 @@ parser
fuzzy,
]);

// make it easy to consume our other main modules and functions
parser.Parser = Parser;
parser.Format = Format;
parser.LocaleHelper = LocaleHelper;
parser.defaultLocale = defaultLocale;

// create functions on Date
parser.fromString = Date.fromString = parser.exportAsFunction();
parser.fromAny = Date.fromAny = parser.exportAsFunctionAny();

// create functions on Date and window
// @ts-expect-error Yes, we are extending the global Date object
Date.fromString = parser.fromString;
// @ts-expect-error Yes, we are extending the global Date object
Date.fromAny = parser.fromAny;
if (typeof window !== 'undefined') {
// @ts-expect-error Yes, we are extending the global window object
window.anyDateParser = parser;
}

Expand Down
Empty file modified scripts/test.sh
100644 → 100755
Empty file.
77 changes: 54 additions & 23 deletions src/Format/Format.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,45 @@
import LocaleHelper from '../LocaleHelper/LocaleHelper';
import Parser from '../Parser/Parser';
import defaultLocale from '../data/defaultLocale';
import removeFillerWords from '../removeFillerWords/removeFillerWords';

export type HandlerResult = {
year?: number;
month?: number;
day?: number;
hour?: number;
minute?: number;
second?: number;
millisecond?: number;
offset?: number;
invalid?: string;
};

export type Bounds = {
lower: string;
upper: string;
inclusive: boolean;
strict: boolean;
};

export type LocaleOrBounds =
| string
| {
locale?: string;
bounds?: Bounds;
};

/**
* Represents a parsable date format
*/
export default class Format {
template: string;
units: string[];
matcher: RegExp;
handler: (matches: string[], locale: string) => HandlerResult;
locales: string[];
regexByLocale: Record<string, RegExp>;
parser: Parser | undefined;
/**
* Given a definition, create a parsable format
* @param {Object} definition The format definition
Expand Down Expand Up @@ -87,21 +121,21 @@ export default class Format {

/**
* Run this format's RegExp against the given string
* @param {String} string The date string
* @param {String} locale The language locale such as en-US, pt-BR, zh, es, etc.
* @returns {Array|null} Array of matches or null on non-match
* @param dateStr The date string
* @param locale The language locale such as en-US, pt-BR, zh, es, etc.
* @returns Array of matches or null on non-match
*/
getMatches(string, locale = defaultLocale) {
return string.match(this.getRegExp(locale));
getMatches(dateStr: string, locale = defaultLocale): string[] | null {
return dateStr.match(this.getRegExp(locale));
}

/**
* Given matches against this RegExp, convert to object
* @param {String[]} matches An array of matched parts
* @param {String} locale The language locale such as en-US, pt-BR, zh, es, etc.
* @returns {Object} Object which may contain year, month, day, hour, minute, second, millisecond, offset, invalid
* @param matches An array of matched parts
* @param locale The language locale such as en-US, pt-BR, zh, es, etc.
* @returns Object which may contain year, month, day, hour, minute, second, millisecond, offset, invalid
*/
toDateTime(matches, locale = defaultLocale) {
toDateTime(matches: string[], locale = defaultLocale) {
const locHelper = LocaleHelper.factory(locale);
if (this.units) {
return locHelper.getObject(this.units, matches);
Expand All @@ -115,11 +149,14 @@ export default class Format {

/**
* Attempt to parse a string in this format
* @param {String} string The date string
* @param {String} locale The language locale such as en-US, pt-BR, zh, es, etc.
* @param strDate The date string
* @param locale The language locale such as en-US, pt-BR, zh, es, etc.
* @returns {Object|null} Null if format can't handle this string, Object for result or error
*/
attempt(string, locale = defaultLocale) {
attempt(
strDate: string,
locale: LocaleOrBounds = defaultLocale
): HandlerResult {
let effectiveLocale = locale;
const bounds = {
lower: '0001-01-01T00:00:00',
Expand All @@ -131,28 +168,22 @@ export default class Format {
effectiveLocale = locale.locale || defaultLocale;
Object.assign(bounds, locale.bounds || {});
}
string = removeFillerWords(String(string), locale).trim();
const matches = this.getMatches(string, locale);
strDate = removeFillerWords(String(strDate), locale).trim();
const matches = this.getMatches(strDate, effectiveLocale);
if (matches) {
const dt = this.toDateTime(matches, locale);
const dtDate = this.dt;
if (
dtDate instanceof Date &&
!this.isInRange(dtDate, bounds) &&
bounds.strict
) {
const dt = this.toDateTime(matches, effectiveLocale);
if (dt instanceof Date && !this.isInRange(dt, bounds) && bounds.strict) {
const inclusive = bounds.inclusive ? 'inclusive' : 'not inclusive';
return {
invalid: `Date not in range ${bounds.lower} to ${bounds.upper} ${inclusive}`,
bounds,
};
}
return dt || null;
}
return null;
}

isInRange(date, bounds) {
isInRange(date: Date, bounds: Bounds) {
const dateStr = date.toJSON();
if (bounds.inclusive) {
return dateStr >= bounds.lower && dateStr <= bounds.upper;
Expand Down
12 changes: 6 additions & 6 deletions src/Parser/Parser.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, expect, it } from 'vitest';
import { describe, expect, it, vi } from 'vitest';
import Parser from './Parser';

describe('Parser', () => {
Expand All @@ -25,16 +25,16 @@ describe('Parser', () => {
expect(result).toBe(false);
});
it('should attempt() a single format', () => {
const format = { attempt: jest.fn(() => 'foo') };
const format = { attempt: vi.fn(() => 'foo') };
const parser = new Parser();
parser.addFormat(format);
const result = parser.attempt('date', 'locale');
expect(result).toBe('foo');
expect(format.attempt).toHaveBeenCalledWith('date', 'locale');
});
it('should attempt() 2 formats', () => {
const format1 = { attempt: jest.fn(() => null) };
const format2 = { attempt: jest.fn(() => 'foo') };
const format1 = { attempt: vi.fn(() => null) };
const format2 = { attempt: vi.fn(() => 'foo') };
const parser = new Parser();
parser.addFormat(format1);
parser.addFormat(format2);
Expand All @@ -44,10 +44,10 @@ describe('Parser', () => {
expect(format2.attempt).toHaveBeenCalledWith('date', 'locale');
});
it('should return invalid when all attempt()s fail', () => {
const format = { attempt: jest.fn(() => null) };
const format = { attempt: vi.fn(() => null) };
const parser = new Parser();
parser.addFormat(format);
const result = parser.attempt('baddate', 'locale');
expect(result).toEqual({ invalid: 'Unable to parse baddate' });
expect(result.invalid).toMatch(/Unable to parse/);
});
});
Loading

0 comments on commit 83e5c46

Please sign in to comment.