Skip to content

Commit

Permalink
add splunk style adjuster support
Browse files Browse the repository at this point in the history
  • Loading branch information
cwensel committed Oct 27, 2023
1 parent 2da7704 commit ae462c7
Show file tree
Hide file tree
Showing 26 changed files with 1,411 additions and 46 deletions.
77 changes: 51 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Mini-Parsers - An API for parsing discrete types

[![Maven Central](https://img.shields.io/maven-central/v/io.heretical/mini-parsers-core)](https://search.maven.org/search?q=g:io.heretical+mini-parsers)
[![javadoc](https://javadoc.io/badge2/io.heretical/mini-parsers-core/javadoc.svg?label=javadoc+mini-parsers-core)](https://javadoc.io/doc/io.heretical/mini-parsers-core)
[![javadoc](https://javadoc.io/badge2/io.heretical/mini-parsers-temporal/javadoc.svg?label=javadoc+mini-parsers-temporal)](https://javadoc.io/doc/io.heretical/mini-parsers-temporal)

## Overview

Mini-Parsers is a Java API for parsing short discrete text strings into native types where a single type may have
Expand All @@ -11,29 +15,22 @@ validation, or normalizing data in a column during data cleansing and ETL.
For example, the same instant in time (`java.time.Instant`) may have multiple formats. The two strings `1423526400000`
and `2015-02-10T02:04:30+00:00` are equivalent if the first is interpreted as the milliseconds since the epoch.

Only absolute and duration text representation disambiguation is currently supported. But with plans for handling more
complex relative temporal representations like `10 days ago` or `last tuesday`. Also support for common units of measure
are being considered.
Absolute, duration, and relative adjuster text representation disambiguation is currently supported.

But with plans for handling more complex relative temporal representations like `10 days ago` or `last tuesday`. Also
support for common units of measure are being considered.

Final Releases are available on Maven Central:

```gradle
implementation 'io.heretical:mini-parsers-core:1.1.0'
implementation 'io.heretical:mini-parsers-temporal:1.1.0'
implementation 'io.heretical:mini-parsers-temporal:2.0.0'
```

```xml
<dependency>
<groupId>io.heretical</groupId>
<artifactId>mini-parsers-core</artifactId>
<version>1.1.0</version>
<type>pom</type>
</dependency>

<dependency>
<groupId>io.heretical</groupId>
<artifactId>mini-parsers-temporal</artifactId>
<version>1.1.0</version>
<version>2.0.0</version>
<type>pom</type>
</dependency>
```
Expand All @@ -46,7 +43,7 @@ This library requires Java 8 and the parsing functionality is dependent on [Parb

## Usage

For the most comprehensive examples on usage, see the unit tests for each sub-project.
For the most comprehensive examples on usage, see the unit tests for each subproject.

### Temporal Parsers Syntax

Expand Down Expand Up @@ -87,22 +84,22 @@ Where `number` is any natural integer (with commas).

And `unit` is either the Unit name or Abbreviation, case-insensitive:

| Unit | Abbreviation | Example
| -----| ------------ | -------
| Milliseconds | ms | 300ms
| Seconds | s, sec | 30s 30sec
| Minutes | m, min | 20m 20min
| Hours | h, hrs | 3h 3hrs
| Days | d, days | 5d 5 days
| Weeks | w, wks | 2w 2wks
| Months | mos | 3mos
| Years | y, yrs | 2y 2rs
| Unit | Abbreviation | Example |
|--------------|--------------|-----------|
| Milliseconds | ms | 300ms |
| Seconds | s, sec | 30s 30sec |
| Minutes | m, min | 20m 20min |
| Hours | h, hrs | 3h 3hrs |
| Days | d, days | 5d 5 days |
| Weeks | w, wks | 2w 2wks |
| Months | mos | 3mos |
| Years | y, yrs | 2y 2rs |

For example, `10000ms` and `10,000 milliseconds` are equivalent.

#### Date/Times

Currently only absolute date/time strings are supported.
Absolute and relative date/time strings are supported.

##### Absolute

Expand Down Expand Up @@ -135,4 +132,32 @@ This parser builds a grammar for all specified formats. When applied and a match
`java.time.format.DateTimeFormatter` for that date/time format is used to parse the value. The parsed result is then
coaxed into a `Instant` after applying any context values like `Locale` or `ZoneId`.

Note the grammar is used to search for the actual formatter to use instead of attempting every `DateFormatterInstance`.
Note the grammar is used to search for the actual formatter to use instead of attempting every `DateFormatterInstance`.

##### Relative

The class `RelativeDateTimeAdjusterParser` will adjust the current time based on a simple adjustment syntax, and return an
`Instant`.

The syntax is adopted from [Splunk](https://docs.splunk.com/Documentation/Splunk/latest/Search/Specifytimemodifiersinyoursearch).

```java
// optionally set the Clock, ZoneId, and Locale
Context context = new Context();

RelativeDateTimeAdjusterParser parser = new RelativeDateTimeAdjusterParser( context );

java.time.Instant hourAgo = parser.parseOrFail( "-60m" ).getResult();

java.time.Instant hourAgoOnTheHour = parser.parseOrFail( "-1h@h" ).getResult();

java.time.Instant weekAgoToday = parser.parseOrFail( "-7d@d" ).getResult();

java.time.Instant beginningOfCurrentWeek = parser.parseOrFail( "@w0" ).getResult(); // depends on locale

java.time.Instant tomorrow = parser.parseOrFail( "+24h@s" ).getResult();
```




Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,42 @@

package heretical.parser.temporal;

import java.time.Instant;

import heretical.parser.temporal.grammar.DateTimeGrammar;
import org.parboiled.Rule;

/**
*
* The AbsoluteDateTimeParser class will parse common date time formats, and return an {@link Instant}.
* <p>
* Where common formats supported look like:
* <pre>
* 02/10/15 02:04
* February/10/15 02:04
* 02/10/15 02:04AM
* February 10th 2015, 02
* November 09th 2018, 20:00:00
* 02/10/15 02
* 20150210
* 20150210T020430Z
* 2015-02-10T02:04:30+00:00
* 2015-02-10 02:04:30+00:00
* </pre>
*/
public class AbsoluteDateTimeParser extends DateTimeParser
{
/**
* Creates a new AbsoluteDateTimeParser instance.
*/
public AbsoluteDateTimeParser()
{
}

/**
* Creates a new AbsoluteDateTimeParser instance.
*
* @param context of type Context
*/
public AbsoluteDateTimeParser( Context context )
{
super( context );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,15 @@
public abstract class BaseTemporalExpressionParser<R, E extends Expression, G extends BaseParser<E>>
{
private static final Logger LOG = LoggerFactory.getLogger( BaseTemporalExpressionParser.class );
protected Context context;

private final Context context;
private Rule grammar;

public BaseTemporalExpressionParser()
{
this( new Context() );
}

public BaseTemporalExpressionParser( Context context )
{
this.context = context;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
*/
public class Context
{
/**
* The default locale used by the parser. Defaults to the system default.
* <p/>
* {@code Locale.getDefault()
*/
public static final Locale DEFAULT_LOCALE = Locale.getDefault();

Clock clock = Clock.systemUTC();
Expand All @@ -32,23 +37,47 @@ public Context()
{
}

/**
* Uses the given zoneId and the default locale.
*
* @param zoneId the zoneId to use
* @param locale the locale to use
*/
public Context( ZoneId zoneId, Locale locale )
{
setClock( Clock.system( zoneId ) );
setLocale( locale );
}

/**
* Uses the given clock and the default locale.
* <p/>
* Most commonly used for testing.
*
* @param clock the clock to use
* @param locale the locale to use
*/
public Context( Clock clock, Locale locale )
{
setClock( clock );
setLocale( locale );
}

/**
* Uses the given clock and the default locale.
* <p/>
* Most commonly used for testing.
*
* @param clock the clock to use
*/
public Context( Clock clock )
{
this.clock = clock;
}

/**
* @return the clock used by the parser
*/
public Clock getClock()
{
return clock;
Expand All @@ -68,11 +97,17 @@ private void setLocale( Locale locale )
this.locale = locale;
}

/**
* @return the locale used by the parser
*/
public Locale getLocale()
{
return locale;
}

/**
* @return the week field for the locale
*/
public TemporalField getWeekField()
{
return WeekFields.of( getLocale() ).dayOfWeek();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,48 @@
package heretical.parser.temporal;

/**
*
* The DateTimeFormatParseException class is thrown when a date time format string cannot be parsed.
*/
public class DateTimeFormatParseException extends RuntimeException
{
/**
* Creates a new DateTimeFormatParseException instance.
*/
public DateTimeFormatParseException()
{
}

/**
* @param message of type String
*/
public DateTimeFormatParseException( String message )
{
super( message );
}

/**
* @param message of type String
* @param cause of type Throwable
*/
public DateTimeFormatParseException( String message, Throwable cause )
{
super( message, cause );
}

/**
* @param cause of type Throwable
*/
public DateTimeFormatParseException( Throwable cause )
{
super( cause );
}

/**
* @param message of type String
* @param cause of type Throwable
* @param enableSuppression of type boolean
* @param writableStackTrace of type boolean
*/
public DateTimeFormatParseException( String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace )
{
super( message, cause, enableSuppression, writableStackTrace );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@
*/
public abstract class DateTimeParser extends BaseTemporalExpressionParser<Instant, DateTimeExp, DateTimeGrammar>
{
public DateTimeParser()
{
}

public DateTimeParser( Context context )
{
super( context );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,21 @@
import org.parboiled.Rule;

/**
* The DurationParser class parses strings that represent durations.
* <p>
* This grammar can distinguish between either ISO-8601 duration strings, like `PT20.345S`, or simplified
* natural language duration strings, like `10 days` or `15min`, and resolve them into `java.time.Duration` instances.
* <p>
* See {@link ISODurationParser} or {@link NaturalDurationParser} for more specific parsers.
* <p>
* This class is not thread-safe.
*/
public class DurationParser extends BaseTemporalExpressionParser<Duration, DurationExp, DurationGrammar>
{
public DurationParser()
{
}

public DurationParser( Context context )
{
super( context );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,31 @@
import org.parboiled.Rule;

/**
* The ISODurationParser class parses strings that represent durations in ISO-8601 formats.
* <p/>
* Where an ISO duration format would look like the following:
*
* <pre>
* PT20.345S
* +PT20.345S
* -PT20.345S // negated duration
* </pre>
* This class is not thread-safe.
*/
public class ISODurationParser extends DurationParser
{
/**
* Creates a new ISODurationParser instance.
*/
public ISODurationParser()
{
}

/**
* Creates a new ISODurationParser instance.
*
* @param context of type Context
*/
public ISODurationParser( Context context )
{
super( context );
Expand Down
Loading

0 comments on commit ae462c7

Please sign in to comment.