├── .github
└── tokenmill-logo.svg
├── .gitignore
├── .gitlab-ci.yml
├── Dockerfile.jdk10.test
├── Dockerfile.jdk11.test
├── Dockerfile.jdk7.test
├── Dockerfile.jdk8.test
├── Dockerfile.jdk9.test
├── LICENSE
├── Makefile
├── README.md
├── deps.edn
├── project.clj
├── src
└── timewords
│ ├── core.clj
│ ├── fuzzy
│ ├── en
│ │ ├── absolute.clj
│ │ ├── en.clj
│ │ ├── relative.clj
│ │ └── utils.clj
│ ├── fuzzy.clj
│ └── lt
│ │ ├── absolute.clj
│ │ ├── lt.clj
│ │ ├── relative.clj
│ │ └── utils.clj
│ └── standard
│ ├── formats.clj
│ ├── standard.clj
│ └── utils.clj
└── test
└── timewords
├── cleaner_test.clj
├── core_test.clj
├── de_test.clj
├── en_relative_quantified_test.clj
├── en_relative_test.clj
├── en_test.clj
├── es_test.clj
├── fr_test.clj
├── java_test.clj
├── lt_relative_test.clj
├── lt_test.clj
├── ru_test.clj
└── special_cases_test.clj
/.github/tokenmill-logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | pom.xml
2 | pom.xml.asc
3 | *jar
4 | /lib/
5 | /classes/
6 | /target/
7 | /checkouts/
8 | .lein-deps-sum
9 | .lein-repl-history
10 | .lein-plugins/
11 | .lein-failures
12 | .nrepl-port
13 | .idea/
14 | metadata-detection.iml
15 | timewords.iml
16 | .cpcache
17 |
--------------------------------------------------------------------------------
/.gitlab-ci.yml:
--------------------------------------------------------------------------------
1 | stages:
2 | - test
3 | - deploy
4 |
5 | variables:
6 | DOCKER_DRIVER: overlay2
7 | DOCKER_HOST: tcp://docker:2375/
8 |
9 | run-unit-tests:
10 | stage: test
11 | image: clojure:alpine
12 | when: always
13 | script:
14 | - lein do clean, test
15 |
16 | run-jdk7-tests:
17 | stage: test
18 | image: docker:stable
19 | when: manual
20 | services:
21 | - docker:dind
22 | script:
23 | - docker build -f Dockerfile.jdk7.test -t timewords:jdk7 .
24 | - docker run timewords:jdk7
25 |
26 | run-jdk8-tests:
27 | stage: test
28 | image: docker:stable
29 | when: always
30 | services:
31 | - docker:dind
32 | script:
33 | - docker build -f Dockerfile.jdk8.test -t timewords:jdk8 .
34 | - docker run timewords:jdk8
35 |
36 | run-jdk10-tests:
37 | stage: test
38 | image: docker:stable
39 | when: always
40 | services:
41 | - docker:dind
42 | script:
43 | - docker build -f Dockerfile.jdk10.test -t timewords:jdk10 .
44 | - docker run timewords:jdk10
45 |
46 | run-jdk11-tests:
47 | stage: test
48 | image: docker:stable
49 | when: always
50 | services:
51 | - docker:dind
52 | script:
53 | - docker build -f Dockerfile.jdk11.test -t timewords:jdk11 .
54 | - docker run timewords:jdk11
55 |
--------------------------------------------------------------------------------
/Dockerfile.jdk10.test:
--------------------------------------------------------------------------------
1 | FROM openjdk:10
2 |
3 | ENV LEIN_VERSION=2.8.1
4 | ENV LEIN_INSTALL=/usr/local/bin/
5 |
6 | WORKDIR /tmp
7 |
8 | # Download the whole repo as an archive
9 | RUN mkdir -p $LEIN_INSTALL \
10 | && wget -q https://raw.githubusercontent.com/technomancy/leiningen/$LEIN_VERSION/bin/lein-pkg \
11 | && echo "Comparing lein-pkg checksum ..." \
12 | && echo "019faa5f91a463bf9742c3634ee32fb3db8c47f0 *lein-pkg" | sha1sum -c - \
13 | && mv lein-pkg $LEIN_INSTALL/lein \
14 | && chmod 0755 $LEIN_INSTALL/lein \
15 | && wget -q https://github.com/technomancy/leiningen/releases/download/$LEIN_VERSION/leiningen-$LEIN_VERSION-standalone.zip \
16 | && wget -q https://github.com/technomancy/leiningen/releases/download/$LEIN_VERSION/leiningen-$LEIN_VERSION-standalone.zip.asc \
17 | && gpg --keyserver ha.pool.sks-keyservers.net --recv-key 2B72BF956E23DE5E830D50F6002AF007D1A7CC18 \
18 | && echo "Verifying Jar file signature ..." \
19 | && gpg --verify leiningen-$LEIN_VERSION-standalone.zip.asc \
20 | && rm leiningen-$LEIN_VERSION-standalone.zip.asc \
21 | && mkdir -p /usr/share/java \
22 | && mv leiningen-$LEIN_VERSION-standalone.zip /usr/share/java/leiningen-$LEIN_VERSION-standalone.jar
23 |
24 | ENV PATH=$PATH:$LEIN_INSTALL
25 | ENV LEIN_ROOT 1
26 |
27 | # Install clojure 1.9.0 so users don't have to download it every time
28 | RUN echo '(defproject dummy "" :dependencies [[org.clojure/clojure "1.9.0"]])' > project.clj \
29 | && lein deps && rm project.clj
30 |
31 |
32 | RUN mkdir -p /usr/src/app
33 | WORKDIR /usr/src/app
34 | COPY project.clj /usr/src/app/
35 | RUN lein deps
36 | COPY . /usr/src/app
37 |
38 | CMD ["lein", "test"]
39 |
--------------------------------------------------------------------------------
/Dockerfile.jdk11.test:
--------------------------------------------------------------------------------
1 | FROM openjdk:11
2 |
3 | ENV LEIN_VERSION=2.8.1
4 | ENV LEIN_INSTALL=/usr/local/bin/
5 |
6 | WORKDIR /tmp
7 |
8 | # Download the whole repo as an archive
9 | RUN mkdir -p $LEIN_INSTALL \
10 | && wget -q https://raw.githubusercontent.com/technomancy/leiningen/$LEIN_VERSION/bin/lein-pkg \
11 | && echo "Comparing lein-pkg checksum ..." \
12 | && echo "019faa5f91a463bf9742c3634ee32fb3db8c47f0 *lein-pkg" | sha1sum -c - \
13 | && mv lein-pkg $LEIN_INSTALL/lein \
14 | && chmod 0755 $LEIN_INSTALL/lein \
15 | && wget -q https://github.com/technomancy/leiningen/releases/download/$LEIN_VERSION/leiningen-$LEIN_VERSION-standalone.zip \
16 | && wget -q https://github.com/technomancy/leiningen/releases/download/$LEIN_VERSION/leiningen-$LEIN_VERSION-standalone.zip.asc \
17 | && gpg --keyserver ha.pool.sks-keyservers.net --recv-key 2B72BF956E23DE5E830D50F6002AF007D1A7CC18 \
18 | && echo "Verifying Jar file signature ..." \
19 | && gpg --verify leiningen-$LEIN_VERSION-standalone.zip.asc \
20 | && rm leiningen-$LEIN_VERSION-standalone.zip.asc \
21 | && mkdir -p /usr/share/java \
22 | && mv leiningen-$LEIN_VERSION-standalone.zip /usr/share/java/leiningen-$LEIN_VERSION-standalone.jar
23 |
24 | ENV PATH=$PATH:$LEIN_INSTALL
25 | ENV LEIN_ROOT 1
26 |
27 | # Install clojure 1.9.0 so users don't have to download it every time
28 | RUN echo '(defproject dummy "" :dependencies [[org.clojure/clojure "1.9.0"]])' > project.clj \
29 | && lein deps && rm project.clj
30 |
31 |
32 | RUN mkdir -p /usr/src/app
33 | WORKDIR /usr/src/app
34 | COPY project.clj /usr/src/app/
35 | RUN lein deps
36 | COPY . /usr/src/app
37 |
38 | CMD ["lein", "test"]
39 |
--------------------------------------------------------------------------------
/Dockerfile.jdk7.test:
--------------------------------------------------------------------------------
1 | FROM openjdk:7
2 |
3 | ENV LEIN_VERSION=2.8.1
4 | ENV LEIN_INSTALL=/usr/local/bin/
5 |
6 | WORKDIR /tmp
7 |
8 | # Download the whole repo as an archive
9 | RUN mkdir -p $LEIN_INSTALL \
10 | && wget -q https://raw.githubusercontent.com/technomancy/leiningen/$LEIN_VERSION/bin/lein-pkg \
11 | && echo "Comparing lein-pkg checksum ..." \
12 | && echo "019faa5f91a463bf9742c3634ee32fb3db8c47f0 *lein-pkg" | sha1sum -c - \
13 | && mv lein-pkg $LEIN_INSTALL/lein \
14 | && chmod 0755 $LEIN_INSTALL/lein \
15 | && wget -q https://github.com/technomancy/leiningen/releases/download/$LEIN_VERSION/leiningen-$LEIN_VERSION-standalone.zip \
16 | && wget -q https://github.com/technomancy/leiningen/releases/download/$LEIN_VERSION/leiningen-$LEIN_VERSION-standalone.zip.asc \
17 | && gpg --keyserver ha.pool.sks-keyservers.net --recv-key 2B72BF956E23DE5E830D50F6002AF007D1A7CC18 \
18 | && echo "Verifying Jar file signature ..." \
19 | && gpg --verify leiningen-$LEIN_VERSION-standalone.zip.asc \
20 | && rm leiningen-$LEIN_VERSION-standalone.zip.asc \
21 | && mkdir -p /usr/share/java \
22 | && mv leiningen-$LEIN_VERSION-standalone.zip /usr/share/java/leiningen-$LEIN_VERSION-standalone.jar
23 |
24 | ENV PATH=$PATH:$LEIN_INSTALL
25 | ENV LEIN_ROOT 1
26 |
27 | # Install clojure 1.9.0 so users don't have to download it every time
28 | RUN echo '(defproject dummy "" :dependencies [[org.clojure/clojure "1.9.0"]])' > project.clj \
29 | && lein deps && rm project.clj
30 |
31 |
32 | RUN mkdir -p /usr/src/app
33 | WORKDIR /usr/src/app
34 | COPY project.clj /usr/src/app/
35 | RUN lein deps
36 | COPY . /usr/src/app
37 |
38 | CMD ["lein", "test"]
39 |
--------------------------------------------------------------------------------
/Dockerfile.jdk8.test:
--------------------------------------------------------------------------------
1 | FROM clojure:alpine
2 | RUN mkdir -p /usr/src/app
3 | WORKDIR /usr/src/app
4 | COPY project.clj /usr/src/app/
5 | RUN lein deps
6 | COPY . /usr/src/app
7 |
8 | CMD ["lein", "test"]
--------------------------------------------------------------------------------
/Dockerfile.jdk9.test:
--------------------------------------------------------------------------------
1 | FROM openjdk:9
2 |
3 | ENV LEIN_VERSION=2.8.1
4 | ENV LEIN_INSTALL=/usr/local/bin/
5 |
6 | WORKDIR /tmp
7 |
8 | # Download the whole repo as an archive
9 | RUN mkdir -p $LEIN_INSTALL \
10 | && wget -q https://raw.githubusercontent.com/technomancy/leiningen/$LEIN_VERSION/bin/lein-pkg \
11 | && echo "Comparing lein-pkg checksum ..." \
12 | && echo "019faa5f91a463bf9742c3634ee32fb3db8c47f0 *lein-pkg" | sha1sum -c - \
13 | && mv lein-pkg $LEIN_INSTALL/lein \
14 | && chmod 0755 $LEIN_INSTALL/lein \
15 | && wget -q https://github.com/technomancy/leiningen/releases/download/$LEIN_VERSION/leiningen-$LEIN_VERSION-standalone.zip \
16 | && wget -q https://github.com/technomancy/leiningen/releases/download/$LEIN_VERSION/leiningen-$LEIN_VERSION-standalone.zip.asc \
17 | && gpg --keyserver ha.pool.sks-keyservers.net --recv-key 2B72BF956E23DE5E830D50F6002AF007D1A7CC18 \
18 | && echo "Verifying Jar file signature ..." \
19 | && gpg --verify leiningen-$LEIN_VERSION-standalone.zip.asc \
20 | && rm leiningen-$LEIN_VERSION-standalone.zip.asc \
21 | && mkdir -p /usr/share/java \
22 | && mv leiningen-$LEIN_VERSION-standalone.zip /usr/share/java/leiningen-$LEIN_VERSION-standalone.jar
23 |
24 | ENV PATH=$PATH:$LEIN_INSTALL
25 | ENV LEIN_ROOT 1
26 |
27 | # Install clojure 1.9.0 so users don't have to download it every time
28 | RUN echo '(defproject dummy "" :dependencies [[org.clojure/clojure "1.9.0"]])' > project.clj \
29 | && lein deps && rm project.clj
30 |
31 |
32 | RUN mkdir -p /usr/src/app
33 | WORKDIR /usr/src/app
34 | COPY project.clj /usr/src/app/
35 | RUN lein deps
36 | COPY . /usr/src/app
37 |
38 | CMD ["lein", "test"]
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2019 Tokenmill, UAB
2 |
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 |
7 | http://www.apache.org/licenses/LICENSE-2.0
8 |
9 | Unless required by applicable law or agreed to in writing, software
10 | distributed under the License is distributed on an "AS IS" BASIS,
11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | See the License for the specific language governing permissions and
13 | limitations under the License.
14 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | run-tests-jdk-7:
2 | docker build -f Dockerfile.jdk7.test -t timewords:jdk7 . && docker run timewords:jdk7
3 |
4 | run-tests-jdk-8:
5 | docker build -f Dockerfile.jdk8.test -t timewords:jdk8 . && docker run timewords:jdk8
6 |
7 | run-tests-jdk-10:
8 | docker build -f Dockerfile.jdk10.test -t timewords:jdk10 . && docker run timewords:jdk10
9 |
10 | run-tests-jdk-11:
11 | docker build -f Dockerfile.jdk11.test -t timewords:jdk11 . && docker run timewords:jdk11
12 |
13 | run-tests-from-8-to-11-till-fail:
14 | make run-tests-jdk-8 && make run-tests-jdk-10 && make run-tests-jdk-11
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # timewords
6 |
7 | [](https://clojars.org/lt.tokenmill/timewords)
8 |
9 | Library to parse a date string to java.util.Date object. For example:
10 |
11 | * "2 weeks from now" -> 2018-11-29T09:52:23.000-00:00
12 | * "28th February 2019" -> 2019-02-28T00:00:00.000-00:00
13 |
14 | When the library cannot parse the input string it returns *`nil`*.
15 |
16 | More formally, from four types of temporal expressions: *time*, *duration*, *interval*, and *set*; only one type is of interest: *time*. Also, *time* type can be divided into two subtypes: fuzzy (e.g. last Sunday) and absolute (1st of January, 2019). To parse a fuzzy time string a *reference time* (i.e. a `java.util.Date object`) is required. By default, reference time is ``now``.
17 |
18 | The library is designed to support multiple languages. Currently two languages are supported: English and Lithuanian. Default language is English.
19 |
20 | # Usage
21 |
22 | ## Clojure
23 |
24 | Add a dependency to your
25 | * *project.clj* - `[lt.tokenmill/timewords "0.5.0"]`
26 | * *deps.edn* - `lt.tokenmill/timewords {:mvn/version "0.5.0"}`
27 |
28 | ```clojure
29 | (require '[timewords.core :refer [parse]])
30 | => nil
31 | (parse "2001-01-01")
32 | => #inst"2001-01-01T00:00:00.000-00:00"
33 | (timewords.core/parse "now")
34 | => #inst"2016-12-13T09:52:02.000-00:00"
35 | (timewords.core/parse "2 weeks ago")
36 | => #inst"2016-11-29T09:52:23.000-00:00"
37 | (timewords.core/parse "2 weeks from now")
38 | => #inst"2016-12-29T09:54:23.000-00:00"
39 | (timewords.core/parse "last monday")
40 | => #inst"2016-12-12T09:54:23.000-00:00"
41 | (timewords.core/parse "last june")
42 | => #inst"2016-06-12T09:54:23.000-00:00"
43 | (timewords.core/parse "last spring")
44 | => #inst"2016-05-12T09:54:23.000-00:00"
45 |
46 | (timewords.core/parse "29th February 2016")
47 | => #inst"2016-02-29T00:00:00.000-00:00"
48 | (timewords.core/parse "29th February 2017")
49 | => #inst"2017-02-01T00:00:00.000-00:00"
50 | (timewords.core/parse "Sunday, 1st January 2017")
51 | => #inst"2017-01-01T00:00:00.000-00:00"
52 |
53 | (timewords.core/parse "2016 m. gruodžio 22 d. 11:10" nil "lt")
54 | => #inst"2016-12-22T11:10:00.000-00:00"
55 | ```
56 |
57 | ## Java
58 |
59 | As of now the JAR is stored in Clojars, therefore maven is not going to find the artifact.
60 | You should add the repository information to your `pom.xml`:
61 | ```xml
62 |
63 |
64 | clojars.org
65 | http://clojars.org/repo
66 |
67 |
68 |
69 | ```
70 |
71 | Add a maven dependency to your `pom.xml`:
72 |
73 | ```xml
74 |
75 | lt.tokenmill
76 | timewords
77 | 0.4.0
78 |
79 | ```
80 |
81 | ```java
82 | import lt.tokenmill.timewords.Timewords;
83 |
84 | public static void main(String[] args) {
85 | Timewords timewords = new Timewords();
86 | Date d1 = timewords.parse("2001-01-01");
87 | Date d2 = timewords.parse("2001-01-01", new Date());
88 | Date d3 = timewords.parse("2001-01-01", new Date(), "en");
89 | }
90 | ```
91 | Note that `timewords` depends on `org.clojure/clojure` which must be provided.
92 |
93 | # Notes
94 |
95 | Relative dates that can be understood as a time period, e.g. `last December` are rounded to the beginning of the period, e.g. `last December` translates to `2016-12-01T00:00:00Z`.
96 |
97 | Timewords of the form `in monthname` is interpreted as if it refers to the past, i.e. `in December` means `last December`.
98 |
99 | Timewords of the form `this monthname` is interpreted as if it refers to the future, i.e. `in December` means `next December`.
100 |
101 | Timeword which is only a name of a weekday, e.g. `Monday`, is interpreted as if it refers to the past, i.e. `Monday` means the same as `last Monday`.
102 |
103 | Timeword of the form `next weekday` means the first day in the future which which weekday is the one mentioned, e.g. `next Monday` means the first Monday to come. If today is Monday and we are parsing `next Monday` then it means a date after 7 days.
104 |
105 | Timeword of a form `this weekday`, e.g. `this Monday`, is interpreted as if it refers to the future, i.e. `this Monday` means the same as `next Monday`.
106 |
107 | # TODO
108 |
109 | TODO:
110 | - [ ] relative Lithuanian dates.
111 |
112 | ## License
113 |
114 | Copyright © 2019 [TokenMill UAB](http://www.tokenmill.lt).
115 |
116 | Distributed under the The Apache License, Version 2.0.
117 |
--------------------------------------------------------------------------------
/deps.edn:
--------------------------------------------------------------------------------
1 | {:deps {org.clojure/clojure {:mvn/version "1.10.0"}
2 | clj-time {:mvn/version "0.14.4"}}
3 | :paths ["src" "classes"]
4 | :aliases {:test
5 | {:extra-paths ["test"]
6 | :extra-deps {com.cognitect/test-runner {:git/url "https://github.com/cognitect-labs/test-runner.git"
7 | :sha "028a6d41ac9ac5d5c405dfc38e4da6b4cc1255d5"}}
8 | :main-opts ["-m" "cognitect.test-runner"]}}}
9 |
--------------------------------------------------------------------------------
/project.clj:
--------------------------------------------------------------------------------
1 | (defproject lt.tokenmill/timewords "0.5.0"
2 | :description "Library to parse time strings."
3 |
4 | :dependencies [[clj-time "0.14.4"]]
5 |
6 | :aot [timewords.core]
7 |
8 | :profiles {:dev {:dependencies []}
9 | :provided {:dependencies [[org.clojure/clojure "1.8.0"]]}})
10 |
--------------------------------------------------------------------------------
/src/timewords/core.clj:
--------------------------------------------------------------------------------
1 | (ns timewords.core
2 | (:require [clojure.string :as s]
3 | [clj-time.core :as joda]
4 | [clj-time.coerce :as jco]
5 | [timewords.standard.standard :as standard]
6 | [timewords.fuzzy.fuzzy :as fuzzy])
7 | (:import (java.util Date)
8 | (org.joda.time DateTime))
9 | (:gen-class
10 | :name lt.tokenmill.timewords.Timewords
11 | :methods [[parse [java.lang.String] java.util.Date]
12 | [parse [java.lang.String java.util.Date] java.util.Date]
13 | [parse [java.lang.String java.util.Date java.lang.String] java.util.Date]]))
14 |
15 | (defn parse
16 | "Given a string that represents date, returns a java.util.Date object.
17 | Cases that are not handled return nil.
18 | Second (optional) parameter must be a language code, e.g. `en`.
19 | Third (optional) parameter is a document-time which must be nil or java.util.Date.
20 | `document-time` is used to parse relative timewords like 'yesterday'."
21 | ^Date [^String date-string & [^Date document-time ^String language]]
22 | (try
23 | (when-not (or (nil? document-time) (instance? Date document-time))
24 | (throw (Exception. "document-time is not either nil or java.util.Date.")))
25 | (when-not (or (nil? language) (string? language))
26 | (throw (Exception. "language parameter is not either nil or java.lang.String.")))
27 | (let [date-string (.replaceAll date-string "[\\u00A0\\u202F\\uFEFF\\u2007\\u180E]" " ")
28 | ^String language (or language "en")
29 | ^DateTime document-time (or (DateTime. document-time) (joda/now))]
30 | (when (not (s/blank? date-string))
31 | (jco/to-date
32 | (or
33 | (standard/to-date date-string language document-time)
34 | (fuzzy/to-date date-string language document-time)))))
35 | (catch Exception e
36 | (prn (str "Caught exception: '" (.getMessage e) "', while parsing timeword '" date-string
37 | "' with language '" language "' and with document-time '" document-time "'."))
38 | nil)))
39 |
40 | (defn -parse
41 | ^Date [_ ^String date-string & [^Date document-time ^String language]]
42 | (parse date-string document-time language))
43 |
--------------------------------------------------------------------------------
/src/timewords/fuzzy/en/absolute.clj:
--------------------------------------------------------------------------------
1 | (ns timewords.fuzzy.en.absolute
2 | (:require [clojure.string :as s]
3 | [clj-time.core :as joda])
4 | (:import (org.joda.time DateTime)))
5 |
6 | (def months
7 | {#"(january)|(jan.)" "1"
8 | #"(fabruary)|(feb.)" "2"
9 | #"(march)|(mar.)" "3"
10 | #"(april)|(apr.)" "4"
11 | #"(may)|(may.)" "5"
12 | #"(june)|(jun.)" "6"
13 | #"(july)|(jul.)" "7"
14 | #"(august)|(aug.)" "8"
15 | #"(september)|(sep.)" "9"
16 | #"(october)|(oct.)" "10"
17 | #"(november)|(nov.)" "11"
18 | #"(december)|(dec.)" "12"})
19 |
20 | (defn- re [r s] (let [f (re-find r s)] (if (coll? f) (first f) f)))
21 |
22 | (defn is-pm?
23 | "Checks if fuzzy date represents a PM time."
24 | [fuzzy-date]
25 | (if (re-find #"(?i)\b(\d{1,2}):\d{2}[\s]?pm" fuzzy-date)
26 | true
27 | false))
28 |
29 | (defn fix-am-hours
30 | "Convert civil am hours to military hours."
31 | [^String am-hour]
32 | (if am-hour
33 | (str (let [hour (Integer/parseInt am-hour)]
34 | (if (= 12 hour)
35 | 0
36 | hour)))
37 | nil))
38 |
39 | (defn fix-pm-hours
40 | "Convert civil pm hours to military hours."
41 | [^long pm-hour]
42 | (if (< pm-hour 12)
43 | (+ 12 pm-hour)
44 | pm-hour))
45 |
46 | (defn safe-parse [fuzzy-date] (if fuzzy-date (Integer/parseInt fuzzy-date) 0))
47 |
48 | (defn year [fuzzy-date] (first (re-find #"\b(19|20)\d{2}\b" fuzzy-date)))
49 |
50 | (defn month [fuzzy-date]
51 | (if (re-matches #"\b\d{2}/\d{2}/\d{4}\b.*" fuzzy-date)
52 | (let [date-part (re-find #"\b\d{2}/\d{2}/\d{4}\b" fuzzy-date)
53 | month-and-day (->> (s/split date-part #"/")
54 | (map #(Integer/parseInt %))
55 | (filter #(>= 31 %)))]
56 | (str
57 | (if (seq (filter #(> 12 %) month-and-day))
58 | (first (filter #(> 12 %) month-and-day))
59 | (first month-and-day))))
60 | (some (fn [[re nr]] (when (re-find re fuzzy-date) nr)) months)))
61 |
62 | (defn day [fuzzy-date] (re-find #"\b\d{1,2}\b" fuzzy-date))
63 | (defn hour [fuzzy-date] (if (is-pm? fuzzy-date)
64 | (-> (second (re-find #"\b(\d{1,2}):\d{2}[\\b]?" fuzzy-date))
65 | (safe-parse)
66 | (fix-pm-hours)
67 | str)
68 | (-> (second (re-find #"\b(\d{1,2})[:\.]\d{2}[\\b]?" fuzzy-date))
69 | (fix-am-hours))))
70 | (defn minute [fuzzy-date] (second (re-find #"\b\d{1,2}[:|\.](\d{2})[\\b]?" fuzzy-date)))
71 | (defn zecond [fuzzy-date] (second (re-find #"\b\d{2}:\d{2}:(\d{2})\b" fuzzy-date)))
72 |
73 | (defn parse-absolute-date
74 | [^String ls & [^DateTime document-time]]
75 | (letfn [(if-conj [coll x] (if x (conj coll x) coll))
76 | (now-part [time-part] (time-part document-time))
77 | (add-years [date-parts]
78 | (conj date-parts
79 | (if-let [y (year ls)]
80 | y
81 | (str (now-part joda/year)))))
82 | (add-months [date-parts] (if-conj date-parts (month ls)))
83 | (add-days [date-parts] (if-conj date-parts (day ls)))
84 | (add-hours [date-parts] (if-conj date-parts (hour ls)))
85 | (add-minutes [date-parts] (if-conj date-parts (minute ls)))
86 | (add-seconds [date-parts] (if-conj date-parts (zecond ls)))]
87 | (-> []
88 | add-years
89 | add-months
90 | add-days
91 | add-hours
92 | add-minutes
93 | add-seconds)))
--------------------------------------------------------------------------------
/src/timewords/fuzzy/en/en.clj:
--------------------------------------------------------------------------------
1 | (ns timewords.fuzzy.en.en
2 | (:require [clojure.string :as s]
3 | [clj-time.core :as joda]
4 | [timewords.fuzzy.en.relative :refer [parse-relative-date]]
5 | [timewords.fuzzy.en.absolute :refer [parse-absolute-date]]
6 | [timewords.fuzzy.en.utils :as utils])
7 | (:import (org.joda.time DateTime)))
8 |
9 | (defn is-relative-date? [s]
10 | (if (or (not (re-find #"\d" s))
11 | (re-find #"ago|yesterday|tomorrow|last|previous|next|now" s))
12 | true
13 | false))
14 |
15 | (defn parse-date [^String s & [^DateTime document-time]]
16 | (let [ls (-> s (utils/clean) (s/lower-case))
17 | document-time (or document-time (joda/now))]
18 | (if (is-relative-date? ls)
19 | (parse-relative-date ls document-time)
20 | (parse-absolute-date ls document-time))))
21 |
--------------------------------------------------------------------------------
/src/timewords/fuzzy/en/relative.clj:
--------------------------------------------------------------------------------
1 | (ns timewords.fuzzy.en.relative
2 | (:require [clojure.string :as s]
3 | [clj-time.core :as joda]
4 | [clj-time.coerce :as tc])
5 | (:import (org.joda.time DateTime)))
6 |
7 | (def special-to-normal {"a sec" "1 second"
8 | "a second" "1 second"
9 | "a min" "1 minute"
10 | "a minute" "1 minute"
11 | "a hour" "1 hour"
12 | "an hour" "1 hour"
13 | "a day" "1 day"
14 | "a week" "7 days"
15 | "a month" "1 month"
16 | "a year" "1 year"
17 | "yesterday" "1 day ago"
18 | "tomorrow" "1 day from now"
19 | "this week" "last monday"
20 | "last month" "1 month ago"
21 | "previous month" "1 month ago"
22 | "next month" "1 month from now"
23 | "this month" "0 months ago"
24 | "last year" "1 year ago"
25 | "next year" "1 year from now"
26 | "this year" "0 years ago"
27 | "previous year" "1 year ago"
28 | "now" "0 seconds ago"
29 | "today" "0 days ago"})
30 |
31 | (defn get-seconds [joda-datetime]
32 | (-> joda-datetime (tc/to-long) (mod 60000) (quot 1000)))
33 |
34 | (defn date-to-str-seq
35 | [joda-datetime]
36 | (when (not (nil? joda-datetime))
37 | (map str [(joda/year joda-datetime)
38 | (joda/month joda-datetime)
39 | (joda/day joda-datetime)
40 | (joda/hour joda-datetime)
41 | (joda/minute joda-datetime)
42 | (get-seconds joda-datetime) ;to avoid using joda/seconds
43 | ])))
44 |
45 |
46 | (defn ^DateTime floor
47 | "Floors the given date-time dt to the given time unit dt-fn,
48 | e.g. (floor (now) hour) returns (now) for all units
49 | up to and including the hour"
50 | [^DateTime dt dt-fn]
51 | (let [dt-fns [joda/year joda/month joda/day joda/hour joda/minute get-seconds joda/milli]]
52 | (apply joda/date-time
53 | (map apply
54 | (concat (take-while (partial not= dt-fn) dt-fns) [dt-fn])
55 | (repeat [dt])))))
56 |
57 | (defn parse-relative-time [cleaned-timeword document-time plus-or-minus]
58 | (let [amount (Integer/parseInt (re-find #"\d+" cleaned-timeword))]
59 | (cond
60 | (re-find #"\d+s$|sec|secs|second|seconds" cleaned-timeword) (-> (plus-or-minus document-time (joda/millis (* amount 1000))) (floor get-seconds))
61 | (re-find #"\d+m$|min|mins|minute|minutes" cleaned-timeword) (-> (plus-or-minus document-time (joda/minutes amount)) (floor joda/minute))
62 | (re-find #"\d+h$|hour|hr|hours|hrs" cleaned-timeword) (-> (plus-or-minus document-time (joda/hours amount)) (floor joda/hour))
63 | (re-find #"\d+d$|day|days" cleaned-timeword) (-> (plus-or-minus document-time (joda/days amount)) (floor joda/day))
64 | (re-find #"\d+w$|week|weeks" cleaned-timeword) (-> (plus-or-minus document-time (joda/days (* 7 amount))) (floor joda/day))
65 | (re-find #"month|months" cleaned-timeword) (-> (plus-or-minus document-time (joda/months amount)) (floor joda/month))
66 | (re-find #"year|years" cleaned-timeword) (-> (plus-or-minus document-time (joda/years amount)) (floor joda/year))
67 | :else nil)))
68 |
69 | (defn parse-some-time-ago
70 | "Handle strings like '32 mins ago'."
71 | ([s] (parse-some-time-ago s (joda/now)))
72 | ([s document-time]
73 | (let [cleaned-timeword (-> s (s/replace "ago" "") (s/trim))]
74 | (if (re-find #"\d+" s)
75 | (parse-relative-time cleaned-timeword document-time joda/minus)
76 | (when-let [normalized (get special-to-normal cleaned-timeword)]
77 | (parse-some-time-ago (str normalized " ago") document-time))))))
78 |
79 | (defn parse-some-time-from-now
80 | "Handle strings like '32 mins from now'."
81 | ([s] (parse-some-time-from-now s (joda/now)))
82 | ([^String s ^DateTime document-time]
83 | (let [cleaned-timeword (-> s (s/replace "from now" "") (s/trim))]
84 | (if (re-find #"\d+" s)
85 | (parse-relative-time cleaned-timeword document-time joda/plus)
86 | (when-let [normalized (get special-to-normal cleaned-timeword)]
87 | (parse-some-time-from-now (str normalized " from now") document-time))))))
88 |
89 | (defn parse-relative-weekday
90 | [^String s ^DateTime document-time plus-or-minus]
91 | (let [required-weekday (cond
92 | (re-find #"monday" s) 1
93 | (re-find #"tuesday" s) 2
94 | (re-find #"wednesday" s) 3
95 | (re-find #"thursday" s) 4
96 | (re-find #"friday" s) 5
97 | (re-find #"saturday" s) 6
98 | (re-find #"sunday" s) 7)
99 | in-required-weekday (if (= required-weekday (joda/day-of-week document-time))
100 | (plus-or-minus document-time (joda/days 7))
101 | (loop [datetime document-time]
102 | (if (= required-weekday (joda/day-of-week datetime))
103 | datetime
104 | (recur (plus-or-minus datetime (joda/days 1))))))]
105 | (floor in-required-weekday joda/day)))
106 |
107 | (defn parse-last-weekday [s document-time]
108 | (parse-relative-weekday s document-time joda/minus))
109 |
110 | (defn parse-next-weekday [s document-time]
111 | (parse-relative-weekday s document-time joda/plus))
112 |
113 | (defn parse-relative-month [s document-time joda-minus-or-plus]
114 | (let [required-month (cond
115 | (re-find #"january" s) 1
116 | (re-find #"february" s) 2
117 | (re-find #"march" s) 3
118 | (re-find #"april" s) 4
119 | (re-find #"may" s) 5
120 | (re-find #"june" s) 6
121 | (re-find #"july" s) 7
122 | (re-find #"august" s) 8
123 | (re-find #"september" s) 9
124 | (re-find #"october" s) 10
125 | (re-find #"november" s) 11
126 | (re-find #"december" s) 12)
127 | datetime-with-adjusted-month (if (= required-month (joda/month document-time))
128 | (joda-minus-or-plus document-time (joda/months 12))
129 | (loop [datetime document-time]
130 | (if (= required-month (joda/month datetime))
131 | datetime
132 | (recur (joda-minus-or-plus datetime (joda/months 1))))))]
133 | (floor datetime-with-adjusted-month joda/month)))
134 |
135 | (defn parse-last-month [s document-time]
136 | (parse-relative-month s document-time joda/minus))
137 |
138 | (defn parse-next-month [s document-time]
139 | (parse-relative-month s document-time joda/plus))
140 |
141 | (defn parse-last-season
142 | ([^String s] (parse-last-season s (joda/now)))
143 | ([^String s ^DateTime document-time]
144 | (let [required-season (cond
145 | (re-find #"spring" s) 1
146 | (re-find #"summer" s) 2
147 | (re-find #"autumn" s) 3
148 | (re-find #"fall" s) 3
149 | (re-find #"winter" s) 4)
150 | month->season (fn [month]
151 | (cond
152 | (<= 3 month 5) 1
153 | (<= 6 month 8) 2
154 | (<= 9 month 11) 3
155 | (<= 1 month 2) 4
156 | (= 12 month) 4))]
157 | (if (= required-season (month->season (joda/month document-time)))
158 | (joda/minus document-time (joda/months 12))
159 | (loop [datetime document-time]
160 | (if (= required-season (month->season (joda/month datetime)))
161 | datetime
162 | (recur (joda/minus datetime (joda/months 1)))))))))
163 |
164 | (defn parse-relative-date
165 | ([^String s] (parse-relative-date s (joda/now)))
166 | ([^String s ^DateTime document-time]
167 | (let [s (or (get special-to-normal s) s)]
168 | (date-to-str-seq
169 | (cond
170 | (re-find #"ago" s) (-> s (parse-some-time-ago))
171 | (re-find #"from now" s) (-> s (parse-some-time-from-now))
172 | (re-find #"^(monday|tuesday|wednesday|thursday|friday|saturday|sunday)$" s) (-> s (parse-last-weekday document-time))
173 | (re-find #"last (monday|tuesday|wednesday|thursday|friday|saturday|sunday)" s) (-> s (parse-last-weekday document-time))
174 | (re-find #"this (monday|tuesday|wednesday|thursday|friday|saturday|sunday)" s) (-> s (parse-next-weekday document-time))
175 | (re-find #"next (monday|tuesday|wednesday|thursday|friday|saturday|sunday)" s) (-> s (parse-next-weekday document-time))
176 | (re-find #"last (january|february|march|april|may|june|july|august|september|october|november|december)" s) (-> s (parse-last-month document-time))
177 | (re-find #"^in\s(early|late)?\s?(january|february|march|april|may|june|july|august|september|october|november|december)$" s) (-> s (parse-last-month document-time))
178 | (re-find #"this (january|february|march|april|may|june|july|august|september|october|november|december)" s) (-> s (parse-next-month document-time))
179 | (re-find #"next (january|february|march|april|may|june|july|august|september|october|november|december)" s) (-> s (parse-next-month document-time))
180 | (re-find #"last (spring|summer|autumn|fall|winter)" s) (-> s (parse-last-season document-time))
181 | :else nil)))))
--------------------------------------------------------------------------------
/src/timewords/fuzzy/en/utils.clj:
--------------------------------------------------------------------------------
1 | (ns timewords.fuzzy.en.utils
2 | (:require [clojure.string :as s]))
3 |
4 | (defn- remove-day-names
5 | "Remove all language specific patterns which usualy accompany publication date"
6 | [date]
7 | (-> date
8 | (s/replace #"(sunday|monday|tuesday|wednesday|thursday|friday|saturday)" "")))
9 |
10 | (defn- clean-with-regex [^String date]
11 | (reduce #(s/replace %1 %2 "") date [#"[|>;]" #"^[,;\.]"]))
12 |
13 | (defn clean [date]
14 | (-> date
15 | ;(remove-day-names)
16 | (clean-with-regex)
17 | (s/trim)))
--------------------------------------------------------------------------------
/src/timewords/fuzzy/fuzzy.clj:
--------------------------------------------------------------------------------
1 | (ns timewords.fuzzy.fuzzy
2 | (:require [clj-time.core :refer [date-time]]
3 | [timewords.fuzzy.en.en :as en]
4 | [timewords.fuzzy.lt.lt :as lt])
5 | (:import (org.joda.time DateTime)))
6 |
7 | (defn to-date
8 | "Parses string dates into components represented by numeric values:
9 | 1-12 for months
10 | 1-31 for days
11 | 19??-20?? for years.
12 |
13 | Dispatches parsing by language. Default language is en."
14 | ^DateTime [^String fuzzy-date & [^String language ^DateTime document-time]]
15 | (cond
16 | (= language "en") (if-let [date-parts (en/parse-date fuzzy-date document-time)]
17 | (apply date-time (map #(Integer/parseInt %) date-parts)))
18 | (= language "lt") (if-let [date-parts (lt/parse-date fuzzy-date document-time)]
19 | (apply date-time (map #(Integer/parseInt %) date-parts)))
20 | :else nil))
21 |
--------------------------------------------------------------------------------
/src/timewords/fuzzy/lt/absolute.clj:
--------------------------------------------------------------------------------
1 | (ns timewords.fuzzy.lt.absolute
2 | (:require [clojure.string :as s]
3 | [clj-time.core :as joda])
4 | (:import (org.joda.time DateTime)))
5 |
6 | (def months
7 | {#"sausi[so]" "1"
8 | #"vasari[so]" "2"
9 | #"kovas|kovo" "3"
10 | #"balandis|balandžio" "4"
11 | #"geguž[ės]" "5"
12 | #"birželi[so]" "6"
13 | #"liepa|liepos" "7"
14 | #"rugpjūtis|rugpjūčio" "8"
15 | #"rugsėjis|rugsėjo" "9"
16 | #"spali[so]" "10"
17 | #"lapkritis|lapkričio" "11"
18 | #"gruodis|gruodžio" "12"})
19 |
20 | (defn- re [r s] (let [f (re-find r s)] (if (coll? f) (first f) f)))
21 |
22 | (defn month [fuzzy-date]
23 | (some (fn [[re nr]] (when (re-find re fuzzy-date) nr)) months))
24 |
25 | (defn year [fuzzy-date] (first (re-find #"\b(19|20)\d{2}\b" fuzzy-date)))
26 | (defn day [fuzzy-date] (re-find #"\b\d{1,2}\b" fuzzy-date))
27 | (defn hour [fuzzy-date] (second (re-find #"\b(\d{2}):\d{2}\b" fuzzy-date)))
28 | (defn minute [fuzzy-date] (second (re-find #"\b\d{2}:(\d{2})\b" fuzzy-date)))
29 | (defn zecond [fuzzy-date] (second (re-find #"\b\d{2}:\d{2}:(\d{2})\b" fuzzy-date)))
30 |
31 | (defn parse-date [^String s & [^DateTime document-time]]
32 | (let [ls (s/lower-case s)]
33 | (letfn [(if-conj [coll x] (if x (conj coll x) coll))
34 | (now-part [time-part] (time-part document-time))
35 | (add-years [date-parts]
36 | (conj date-parts
37 | (if-let [y (year ls)]
38 | y
39 | (str (now-part joda/year)))))
40 | (add-months [date-parts] (if-conj date-parts (month ls)))
41 | (add-days [date-parts] (if-conj date-parts (day ls)))
42 | (add-hours [date-parts] (if-conj date-parts (hour ls)))
43 | (add-minutes [date-parts] (if-conj date-parts (minute ls)))
44 | (add-seconds [date-parts] (if-conj date-parts (zecond ls)))]
45 | (-> []
46 | add-years
47 | add-months
48 | add-days
49 | add-hours
50 | add-minutes
51 | add-seconds))))
52 |
53 | (defn parse-absolute-date [^String s & [^DateTime document-time]]
54 | (parse-date s document-time))
55 |
--------------------------------------------------------------------------------
/src/timewords/fuzzy/lt/lt.clj:
--------------------------------------------------------------------------------
1 | (ns timewords.fuzzy.lt.lt
2 | (:require [clj-time.core :as joda]
3 | [timewords.fuzzy.lt.absolute :refer [parse-absolute-date]]
4 | [timewords.fuzzy.lt.relative :refer [parse-relative-date]]
5 | [timewords.fuzzy.lt.utils :refer [clean]])
6 | (:import (org.joda.time DateTime)))
7 |
8 | (defn relative-date? [s]
9 | (if (or (not (re-find #"\d" s))
10 | (re-find #"prieš" s)
11 | (re-find #"vakar" s)
12 | (re-find #"šiandien" s))
13 | true
14 | false))
15 |
16 | (defn parse-date [^String s & [^DateTime document-time]]
17 | (let [document-time (or document-time (joda/now))
18 | s (clean s)]
19 | (if (relative-date? s)
20 | (parse-relative-date s document-time)
21 | (parse-absolute-date s document-time))))
22 |
--------------------------------------------------------------------------------
/src/timewords/fuzzy/lt/relative.clj:
--------------------------------------------------------------------------------
1 | (ns timewords.fuzzy.lt.relative
2 | (:require [clojure.string :as s]
3 | [clj-time.core :as joda]
4 | [clj-time.coerce :as tc])
5 | (:import (org.joda.time DateTime)))
6 |
7 | (defn get-seconds [joda-datetime]
8 | (-> joda-datetime (tc/to-long) (mod 60000) (quot 1000)))
9 |
10 | (defn date-to-str-seq
11 | [joda-datetime]
12 | (when (not (nil? joda-datetime))
13 | (map str [(joda/year joda-datetime)
14 | (joda/month joda-datetime)
15 | (joda/day joda-datetime)
16 | (joda/hour joda-datetime)
17 | (joda/minute joda-datetime)
18 | (get-seconds joda-datetime) ;to avoid using joda/seconds
19 | ])))
20 |
21 | (defn parse-days-ago [s document-time]
22 | (let [days (Integer/parseInt (re-find #"\d+" s))]
23 | (joda/floor
24 | (joda/minus document-time (joda/days days))
25 | joda/day)))
26 |
27 | (defn parse-today-time [s document-time]
28 | (let [[hh mm] (map #(Integer/parseInt %) (re-seq #"\d{2}" s))]
29 | (joda/floor
30 | (-> (joda/floor document-time joda/day)
31 | (joda/plus (joda/hours hh))
32 | (joda/plus (joda/minutes mm)))
33 | joda/minute)))
34 |
35 | (defn parse-hours-ago [s document-time]
36 | (prn s document-time)
37 | (let [hours (Integer/parseInt (re-find #"\d+" s))]
38 | (joda/floor
39 | (joda/minus document-time (joda/hours hours))
40 | joda/hour)))
41 |
42 | (defn parse-weeks-ago [s document-time]
43 | (let [days (Integer/parseInt (re-find #"\d+" s))]
44 | (joda/floor
45 | (joda/minus document-time (joda/days (* 7 days)))
46 | joda/day)))
47 |
48 | (defn parse-yesterday-time [s document-time]
49 | (let [[hh mm] (map #(Integer/parseInt %) (re-seq #"\d{2}" s))]
50 | (joda/floor
51 | (-> document-time
52 | (joda/floor joda/day)
53 | (joda/minus (joda/days 1))
54 | (joda/plus (joda/hours hh))
55 | (joda/plus (joda/minutes mm)))
56 | joda/minute)))
57 |
58 | (defn parse-relative-date
59 | [^String s ^DateTime document-time]
60 | (date-to-str-seq
61 | (cond
62 | (re-matches #"prieš \d+ d.?" s) (parse-days-ago s document-time)
63 | (re-matches #"\d+ d.? prieš" s) (parse-days-ago s document-time)
64 | (re-matches #"\d+ sav.? prieš" s) (parse-weeks-ago s document-time)
65 | (re-matches #"\d+ val.? prieš" s) (parse-hours-ago s document-time)
66 | (re-matches #"šiandien \d{2}:\d{2}" s) (parse-today-time s document-time)
67 | (re-matches #"vakar \d{2}:\d{2}" s) (parse-yesterday-time s document-time)
68 | :else nil)))
--------------------------------------------------------------------------------
/src/timewords/fuzzy/lt/utils.clj:
--------------------------------------------------------------------------------
1 | (ns timewords.fuzzy.lt.utils
2 | (:require [clojure.string :as s]))
3 |
4 | (defn clean
5 | "Remove all language specific patterns which usualy accompany publication date"
6 | [^String d]
7 | (-> d
8 | (s/replace #"(Autorius|Publikuota):" "")
9 | (s/replace #", atnaujinta 20.*" "")
10 | (s/trim)))
11 |
--------------------------------------------------------------------------------
/src/timewords/standard/formats.clj:
--------------------------------------------------------------------------------
1 | (ns timewords.standard.formats
2 | (:require [clojure.string :as str])
3 | (:import (java.util Locale TimeZone Date)
4 | (org.joda.time.format ISODateTimeFormat DateTimeFormat DateTimeFormatter)
5 | (org.joda.time DateTime LocalDateTime)
6 | (org.joda.time.chrono LenientChronology ISOChronology)))
7 |
8 | (def lenient-chronology (LenientChronology/getInstance (ISOChronology/getInstance)))
9 |
10 | (defn fmt [pattern & [locale default-year lenient]]
11 | (cond-> (DateTimeFormat/forPattern pattern)
12 | locale (.withLocale locale)
13 | default-year (.withDefaultYear default-year)
14 | lenient (.withChronology lenient-chronology)))
15 |
16 | (defn common-formatters [locale]
17 | [(ISODateTimeFormat/dateTimeParser)
18 | (fmt "MMddyyyy" locale)
19 | (fmt "yyyyMMdd'T'HH:mm:ss'Z'" locale)
20 | (fmt "yy-MM-dd" locale)
21 | (fmt "MMM dd HH:mm yyyy" locale)
22 | (fmt "yyyy-MM-dd HH:mm" locale)
23 | (fmt "yyyy-MM-dd HH:mm:ss" locale)
24 | (fmt "yyyy-MM-dd, HH:mm" locale)
25 | (fmt "yyyy/MM/dd" locale)
26 | (fmt "dd/MM/yyyy, HH:mm" locale)
27 | (fmt "dd/MM/yyyy" locale)
28 | (fmt "MM/dd/yyyy" locale)
29 | (fmt "MMMM yyyy" locale)
30 | (fmt "dd'st' MMMM yyyy" locale)
31 | (fmt "dd'nd' MMMM yyyy" locale)
32 | (fmt "dd'rd' MMMM yyyy" locale)
33 | (fmt "dd'th' MMMM yyyy" locale)
34 | (fmt "EEE, dd'st' MMMM yyyy" locale)
35 | (fmt "EEE, dd'nd' MMMM yyyy" locale)
36 | (fmt "EEE, dd'rd' MMMM yyyy" locale)
37 | (fmt "EEE, dd'th' MMMM yyyy" locale)
38 | (fmt "EEE MMMM dd, yyyy" locale)
39 | (fmt "EEE MMMM dd, yyyy h:mma" locale)
40 | (fmt "EEE MMMM dd, yyyy h:mma z" locale)
41 | (fmt "EEE MMMM dd yyyy HH:mm 'UTC'Z" locale)
42 | (fmt "dd MMM yyyy HH:mm" locale)
43 | (fmt "MMM dd, yyyy HH:mm z" locale)
44 | (fmt "dd MMM yyyy" locale)
45 | (fmt "MMMM dd, yyyy HH:mm" locale)
46 | (fmt "MMMM dd, yyyy - HH:mm" locale)
47 | (fmt "EEE, MMMM dd, yyyy" locale)
48 | (fmt "yyyy-MM-dd'T'HH:mm:ssZ" locale)
49 | (fmt "yyyy-MM-dd'T'HH:mm:ss" locale)
50 | (fmt "MMMM dd, yyyy h:mm a" locale)
51 | (fmt "MMMM dd, yyyy" locale)
52 | (fmt "MMMM. dd, yyyy" locale)
53 | (fmt "EEE MMMM dd HH:mm:ss z yyyy" locale)
54 | (fmt "yyyy-MM-dd HH:mm:ss z" locale)
55 | (fmt "EEE dd MMMM yyyy HH:mm" locale)
56 | (fmt "yyyy/MM/dd HH:mm:ss" locale)
57 | (fmt "yyyy-MM-dd HH:mm:ss.S" locale)
58 | (fmt "MM/dd/yyyy HH:mm" locale)
59 | (fmt "MM/dd/yyyy HH:mm:ss a z" locale)
60 | (fmt "MMMM dd, yyyy, h:mm a" locale)
61 | (fmt "EEE, MMMM dd, yyyy, h:mm a" locale)
62 | (fmt "EEEE dd MMMM yyyy HH.mm z" locale)
63 | (fmt "yyyy-MM-dd '/' HH:mm" locale)
64 | (fmt "yyyy.MM.dd HH:mm" locale)
65 | (fmt "HH:mm yyyy.MM.dd" locale)
66 | (fmt "yyyy.MM.dd" locale)
67 | (fmt "EEEE, dd. MMMM yyyy, HH:mm 'Uhr'" locale)
68 | (fmt "dd. MMMM yyyy, HH:mm 'Uhr'" locale)
69 | (fmt "EEEE, dd. MMMM yyyy" locale)
70 | (fmt "dd. MMMM yyyy" locale)
71 | (fmt "EEEE, dd.MM.yyyy, HH:mm" locale)
72 | (fmt "EEEE, dd.MM.yyyy" locale)
73 | (fmt "HH:mm dd.MM.yyyy" locale)
74 | (fmt "HH:mm dd MMMM yyyy" locale)
75 | (fmt "dd.MM.yyyy" locale)
76 | (fmt "MMMM dd" locale (.getYear (DateTime.)))
77 | (fmt "MMMM dd 'd.' HH:mm" locale (.getYear (DateTime.)))
78 | (fmt "yyyy/MM/dd HH:mm" locale)
79 | (fmt "yyyy MM dd HH:mm" locale)
80 | (fmt "yyyy MMMM dd" locale)
81 | (fmt "yyyy MMMM dd, EEEE" locale)
82 | (fmt "yyyy MMMM dd'd.'" locale)
83 | (fmt "yyyy MMMM dd 'd.'" locale)
84 | (fmt "yyyy MMMM dd'd.' HH:mm" locale)
85 | (fmt "yyyy MMMM dd 'd.' HH:mm" locale)
86 | (fmt "yyyy 'metų' MMMM dd" locale)
87 | (fmt "yyyy MMMM dd HH:mm" locale)
88 | (fmt "yyyy MMMM dd HH:mm:ss" locale)
89 | (fmt "yyyy 'm.' MMMM dd 'd.' HH:mm" locale)
90 | (fmt "yyyy MMMM dd'd.' HH:mm" locale)
91 | (fmt "yyyy MMMM dd 'd.' HH:mm" locale)
92 | (fmt "yyyy MMMM 'mėn.' dd 'd.' HH:mm:ss" locale)
93 | (fmt "yyyy-MM-dd '/' HH:mm" locale)
94 | (fmt "yyyy.MM.dd HH:mm" locale)
95 | (fmt "HH:mm yyyy.MM.dd" locale)
96 | (fmt "dd.MM.yyyy - HH:mm'h'" locale)
97 | (fmt "dd.MM.yyyy – HH:mm 'H.'" locale)
98 | (fmt "dd.MM.yyyy, HH:mm" locale)
99 | (fmt "dd.MM.yyyy в HH:mm" locale)
100 | (fmt "dd.MM.yyyy HH:mm" locale)
101 | (fmt "dd.MM.yyyy - HH:mm" locale)
102 | (fmt "yyyy-MM-dd HH:mm:ss 'H'" locale)
103 | (fmt "dd/MM/yyyy HH:mm 'h'" locale)
104 | (fmt "dd/MM/yyyy - HH:mm" locale)
105 | (fmt "dd/MM/yyyy HH:mm" locale)
106 | (fmt "dd MMMM yyyy - HH:mm 'CEST'" locale)
107 | (fmt "dd MMMM yyyy - HH:mm" locale)
108 | (fmt "dd MMMM yyyy 'г.,' HH:mm" locale)
109 | (fmt "dd MMMM yyyy HH:mm'h' 'CEST'" locale)
110 | (fmt "dd MMMM'.' yyyy HH:mm" locale)
111 | (fmt "dd MMMM, yyyy" locale)
112 | (fmt "dd/MM/yyyy HH:mm'h'" locale)
113 | (fmt "dd/MM/yyyy HH:mm:ss 'CET'" locale)
114 | (fmt "dd/MM/yyyy HH:mm:ss" locale)
115 | (fmt "HH:mm - dd/MM/yy" locale)
116 | (fmt "dd/MM/yy" locale)
117 | (fmt "dd/MM/yy HH:mm" locale)
118 | (fmt "dd/MM HH:mm" locale (.getYear (DateTime.)))
119 | (fmt "EEEE, dd MMMM yyyy, HH:mm" locale)
120 | (fmt "EEEE, dd MMMM yyyy HH:mm" locale)
121 | (fmt "dd MMMM yyyy, HH:mm" locale)
122 | (fmt "dd MMMM yyyy : HH:mm" locale)
123 | (fmt "EEEE, dd/MM/yyyy" locale)
124 | (fmt "HH:mm dd/MM/yyyy" locale)
125 | (fmt "EEEE dd MMMM yyyy" locale)
126 | (fmt "dd 'de' MMMM 'de' yyyy'.' HH:mm'h'" locale)
127 | (fmt "dd 'de' MMMM 'de' yyyy" locale)
128 | (fmt "yyyy-MM-dd HH:mm:ss Z" locale)
129 | (fmt "HH:mm, dd MMMM yyyy" locale)
130 | (fmt "MM/dd/yyyy hh:mm:ss a" locale)
131 | (fmt "hh : mm a - dd/MM/yyyy" locale)
132 | (fmt "MM/dd/yyyy hh:mm a" locale)
133 | (fmt "HH:mm MMMM dd, yyyy" locale)
134 | (fmt "dd MMMM HH:mm" locale (.getYear (DateTime.)))
135 | (fmt "dd MMMM, HH:mm" locale (.getYear (DateTime.)))])
136 |
137 | (defn ninth-or-newer-jdk? []
138 | (not (re-matches #"1\..*" (System/getProperty "java.version"))))
139 |
140 | (def normalize-lt
141 | (if (ninth-or-newer-jdk?)
142 | (fn [text]
143 | (-> text
144 | (str/replace #"sau " "sausio ")
145 | (str/replace #"(^vas |vasario )" "vasario ")
146 | (str/replace #"(kov |kovo )" "kovo ")
147 | (str/replace #"(bal |balandžio )" "balandžio ")
148 | (str/replace #"(geg |gegužės )" "gegužės ")
149 | (str/replace #"(bir |birželio )" "birželio ")
150 | (str/replace #"(lie |liepos )" "liepos ")
151 | (str/replace #"(rgp |rugpjūčio )" "rugpjūčio ")
152 | (str/replace #"(rgs |rugsėjo )" "rugsėjo ")
153 | (str/replace #"(spa |spalio )" "spalio ")
154 | (str/replace #"(lap |lapkričio )" "lapkričio ")
155 | (str/replace #"(gru |gruodžio )" "gruodžio ")))
156 | (fn [text]
157 | (-> text
158 | (str/replace #"sau " "sausio ")
159 | (str/replace #"(^vas |vasario )" "vasaris ")
160 | (str/replace #"(kov |kovo )" "kovas ")
161 | (str/replace #"(bal |balandžio )" "balandis ")
162 | (str/replace #"(geg |gegužės )" "gegužė ")
163 | (str/replace #"(bir |birželio )" "birželis ")
164 | (str/replace #"(lie |liepos )" "liepa ")
165 | (str/replace #"(rgp |rugpjūčio )" "rugpjūtis ")
166 | (str/replace #"(rgs |rugsėjo )" "rugsėjis ")
167 | (str/replace #"(spa |spalio )" "spalis ")
168 | (str/replace #"(lap |lapkričio )" "lapkritis ")
169 | (str/replace #"(gru |gruodžio )" "gruodis ")))))
170 |
171 | (defn normalize [text]
172 | (-> text
173 | (str/lower-case)
174 | (normalize-lt)
175 |
176 | (str/replace #"(.*\d+)(h)(\d{2})" "$1:$3")
177 | (str/replace #"([Ll]e\s)(\d+.*)" "$2")
178 | (str/replace #"(.*\d{2,})(\sà\s)(\d+.*)" "$1 $3")
179 | (str/replace #"(.*\d+\s[a-zA-Z]+)(\sà\s)(\d+.*)" "$1 $3")
180 |
181 | (str/replace "pst" "PST")
182 | (str/replace "edt" "EDT")
183 | (str/replace "bst" "GMT")
184 | (str/replace " gmt" " GMT")
185 | (str/replace "p.m." "pm")
186 | (str/replace "utc" "UTC")
187 | (str/replace "est" "EST")
188 | (str/replace "mar." "marzo")
189 | (str/replace " mar " " marzo ")
190 |
191 | (str/replace "январь" "января")
192 | (str/replace "февраль" "февраля")
193 | (str/replace "март " "марта ")
194 | (str/replace "апрель" "апреля")
195 | (str/replace "май" "мая")
196 | (str/replace "июнь" "июня")
197 | (str/replace "июль" "июля")
198 | (str/replace "август " "августа ")
199 | (str/replace "сентябрь" "сентября")
200 | (str/replace "октябрь" "октября")
201 | (str/replace "ноябрь" "ноября")
202 | (str/replace "декабрь" "декабря")))
203 |
204 | (defn special-cases [text locale document-time]
205 | (let [document-time (or document-time (DateTime.))
206 | fmts [(fmt "hh:mm a z")
207 | (fmt "hh:mm a")
208 | (fmt "hh:mma z")
209 | (fmt "hh:mma")
210 | (fmt "HH:mm z")
211 | (fmt "HH:mm")]]
212 | (first
213 | (for [^DateTimeFormatter formatter fmts
214 | :let [parsed (try
215 | (.toDate (-> (.parseLocalDateTime formatter text)
216 | (.withYear (.getYear document-time))
217 | (.withMonthOfYear (.getMonthOfYear document-time))
218 | (.withDayOfMonth (.getDayOfMonth document-time)))
219 | (TimeZone/getTimeZone "GMT"))
220 | (catch Exception _ nil))]
221 | :when parsed] parsed))))
222 |
223 | (def used-locales (atom {}))
224 |
225 | (defn formatters [locale]
226 | (if-let [used-formatters (get @used-locales locale)]
227 | used-formatters
228 | (let [locale-formatters (common-formatters locale)]
229 | (swap! used-locales assoc locale locale-formatters)
230 | locale-formatters)))
231 |
232 | (defn parse
233 | ([^String text]
234 | (parse text Locale/ENGLISH nil))
235 | ([^String text ^Locale locale]
236 | (parse text locale nil))
237 | ([^String text ^Locale locale ^DateTime document-time]
238 | (let [text (normalize text)
239 | locale-formatters (formatters locale)
240 | parsed-dates (for [^DateTimeFormatter formatter locale-formatters
241 | :let [parsed (try
242 | (let [^LocalDateTime pdate (.parseLocalDateTime formatter text)]
243 | (when (< 10000 (.getYear pdate))
244 | (throw (Exception.)))
245 | (.toDate (if (and (not (nil? document-time))
246 | (not= (.getYear document-time) (.getYear (DateTime.))))
247 | (.minusYears pdate (- (.getYear pdate) (.getYear document-time)))
248 | pdate)
249 | (TimeZone/getTimeZone "GMT")))
250 | (catch Exception _ nil))]
251 | :when parsed] parsed)]
252 | (if (empty? parsed-dates)
253 | (conj parsed-dates (special-cases text locale document-time))
254 | parsed-dates))))
255 |
--------------------------------------------------------------------------------
/src/timewords/standard/standard.clj:
--------------------------------------------------------------------------------
1 | (ns timewords.standard.standard
2 | (:require [clojure.string :as s]
3 | [clj-time.coerce :refer [from-date]]
4 | [timewords.standard.formats :as formats]
5 | [timewords.standard.utils :as utils])
6 | (:import (org.joda.time DateTime)
7 | (java.util Locale)))
8 |
9 | (def date-part-normalizations
10 | {#"(?i)p\.m\." "PM"
11 | #"(?i)a\.m\." "AM"
12 | #"([ \d])ET$" "$1 EST"})
13 |
14 | (defn normalize-date-parts
15 | [^String date]
16 | (reduce
17 | (fn [date [match replacement]]
18 | (s/replace date match replacement))
19 | date
20 | date-part-normalizations))
21 |
22 | (defn clean-date-string [^String date]
23 | (-> date
24 | s/trim
25 | (s/replace #"\s+" " ")))
26 |
27 | (defn multi-format-parse [^String date ^String language ^DateTime document-time]
28 | (let [locale (Locale/forLanguageTag language)]
29 | (->> (formats/parse date locale document-time)
30 | (map from-date)
31 | ; for cases where multiple patterns match
32 | (sort)
33 | (reverse)
34 | (first))))
35 |
36 | (defn to-date
37 | [^String date & [^String language ^DateTime document-time]]
38 | (when-not (empty? date)
39 | (-> date
40 | utils/clean
41 | clean-date-string
42 | normalize-date-parts
43 | (multi-format-parse language document-time))))
44 |
--------------------------------------------------------------------------------
/src/timewords/standard/utils.clj:
--------------------------------------------------------------------------------
1 | (ns timewords.standard.utils
2 | (:require [clojure.string :as s]))
3 |
4 | (defn- clean-with-regex [^String date]
5 | (reduce #(s/replace %1 %2 "") date [#"[|>;]" #"^[,;\.]"]))
6 |
7 | (defn clean [^String date]
8 | (-> date
9 | ;(remove-day-names)
10 | (clean-with-regex)
11 | (s/trim)))
--------------------------------------------------------------------------------
/test/timewords/cleaner_test.clj:
--------------------------------------------------------------------------------
1 | (ns timewords.cleaner-test
2 | (:require [clojure.test :refer :all]
3 | [timewords.fuzzy.en.utils :refer [clean]]))
4 |
5 | (deftest cleaner-test
6 | (testing "Dirty dates"
7 | (is (= "2015-02-02" (clean "2015-02-02")))
8 | (is (= "2015-02-02, 12:13" (clean "2015-02-02, 12:13")))
9 | (is (= "2015-02-02, 12:13" (clean ",2015-02-02, 12:13")))))
10 |
--------------------------------------------------------------------------------
/test/timewords/core_test.clj:
--------------------------------------------------------------------------------
1 | (ns timewords.core-test
2 | (:require [clojure.test :refer :all]
3 | [clj-time.core :as joda :refer [date-time]]
4 | [timewords.core :refer :all])
5 | (:import (java.util Date)))
6 |
7 | (defn date [& xs] (.toDate (apply date-time xs)))
8 |
9 | (deftest core-test
10 |
11 | (testing "Edge cases"
12 | (is (nil? (parse nil)))
13 | (is (nil? (parse ""))))
14 |
15 | (testing "Standard date parsing"
16 | (is (= (date 2013 3 12) (parse " 2013-03-12 ")))
17 | (is (= (date 2012 3 14 16 40) (parse "2012-03-14 16:40")))
18 | (is (= (date 2016 2 8 9 41) (parse "08/02/16 09:41")))
19 | (is (= (date 2000 9 14 13 19) (parse "14/09/2000, 13:19")))
20 | (is (= (date 2000 9 14) (parse "14/09/2000")))
21 | (is (= (date 2015 7 24 9 38) (parse "2015-07-24, 09:38")))
22 | (is (= (date 2012 3 14 16 40 10) (parse "2012-03-14 16:40:10")))
23 | (is (= (date 2014 12 22 22 19 48) (parse "2014-12-22T22:19:48+02:00")))
24 | ;;; to fix
25 | (is (= (date 1865 04 27 0 0 0) (parse "1865-04-27T00:00:00-05:00")))
26 | (is (= (date 2009 1 8) (parse "2009/01/08")))
27 | (is (= (date 2013 3 12) (parse "2013-03-12")))
28 | (is (= (date 2010 8) (parse "August 2010")))
29 | (is (= (date 2013) (parse "2013")))
30 | (is (= (date 2010 7 1 0 0 0) (parse "1st July 2010")))
31 | (is (= (date 2010 7 2 0 0 0) (parse "2nd July 2010")))
32 | (is (= (date 2010 7 3 0 0 0) (parse "3rd July 2010")))
33 | (is (= (date 2010 7 8 0 0 0) (parse "8th July 2010")))
34 | (is (= (date 2016 2 29 0 0 0) (parse "29th February 2016")))
35 | (is (= (date 2017 1 1 0 0 0) (parse "Sunday, 1st January 2017")))
36 | (is (= (date 2017 1 2 0 0 0) (parse "Monday, 2nd January 2017")))
37 | (is (= (date 2017 1 3 0 0 0) (parse "Tuesday, 3rd January 2017")))
38 | (is (= (date 2017 1 8 0 0 0) (parse "Sunday, 8th January 2017"))))
39 |
40 | (testing "EN date parsing"
41 | (is (= (date 2015 10 19) (parse "Monday October 19, 2015")))
42 | (is (= (date 2015 10 19) (parse "Monday Oct 19, 2015")))
43 | (is (= (date 2015 10 19) (parse "Mon Oct 19, 2015")))
44 | (is (= (date 2015 10 19 5 44) (parse "Mon Oct 19, 2015 5:44am")))
45 | (is (= (date 2015 10 19 17 44) (parse "Mon Oct 19, 2015 5:44pm")))
46 | (is (= (date 2015 10 19 5 44) (parse "Mon Oct 19, 2015 5:44am PST")))
47 | ;(is (= (date 2015 10 19 21 44) (parse "Mon Oct 19, 2015 5:44pm EDT")))
48 | ;(is (= (date 2015 10 19 22 44) (parse "Mon Oct 19, 2015 5:44pm EST")))
49 | ;(is (= (date 2015 10 19 21 44) (parse "Mon Oct 19, 2015 5:44PM EDT")))
50 | ;(is (= (date 2015 10 19 16 44) (parse "Mon Oct 19, 2015 12:44pm EDT")))
51 | (is (= (date 2015 10 19 13 30) (parse "Monday 19 October 2015 13.30 BST")))
52 | (is (= (date 2015 10 20) (parse "20 October 2015")))
53 | (is (= (date 2013 1 18 13 8 57) (parse "Fri Jan 18 13:08:57 UTC 2013")))
54 | (is (= (date 2013 2 9) (parse "February 9, 2013")))
55 | (is (= (date 2013 2 12 14) (parse "Tuesday, February 12, 2013, 2:00 PM")))
56 | (is (= (date 2013 3 13 13 32) (parse "March 13, 2013 - 1:32PM")))
57 | (is (= (date 2013 2 9) (parse "February 9, 2013")))
58 | (is (= (date 2013 2 5 13 2) (parse "February 5, 2013, 1:02 PM")))
59 | (is (= (date 2013 2 6) (parse "2013-02-06")))
60 | (is (= (date 2013 2 7) (parse "February 07, 2013")))
61 | (is (= (date 2012 12 9 9 54 7) (parse "12/09/2012 09:54:07 AM PST")))
62 | (is (= (date 2013 2 4) (parse "Monday February 4, 2013")))
63 | (is (= (date 2013 1 30 9 14) (parse "01/30/2013 09:14")))
64 | (is (= (date 2013 1 28) (parse "2013-01-28")))
65 | (is (= (date 2013 1 25 5 58) (parse "2013-01-25 05:58:00.0")))
66 | (is (= (date 2013 3 26 9 28) (parse "2013-03-26T09:28-05")))
67 | (is (= (date 2013 1 24) (parse "2013/01/24")))
68 | (is (= (date 2013 1 24 8 46 54) (parse "2013-01-24T08:46:54Z")))
69 | (is (= (date 2013 1 24 1 24 31) (parse "2013/01/24 01:24:31")))
70 | (is (= (date 2013 1 22 1 31 5) (parse "2013/01/22 01:31:05")))
71 | (is (= (date 2013 1 23) (parse "2013-01-23T00:00:00+00:00")))
72 | (is (= (date 2013 1 20 0 1) (parse "Sun Jan 20 2013 00:01 UTC+0000")))
73 | (is (= (date 2013 1 20) (parse "Sunday 20 January 2013 00:00")))
74 | (is (= (date 2013 1 16 8 11 37) (parse "2013-01-16 08:11:37 EST")))
75 | (is (= (date 2013 1 17 17 17 23) (parse "2013-01-17T17:17:23Z")))
76 | (is (= (date 2013 1 18 12 21 41) (parse "2013-01-18 12:21:41 UTC")))
77 | (is (= (date 2013 1 18) (parse "Jan. 18, 2013")))
78 | (is (= (date 2013 1 18 13 8 57) (parse "Fri Jan 18 13:08:57 UTC 2013")))
79 | (is (= (date 2013 1 18) (parse "January 18, 2013")))
80 | (is (= (date 2013 1 12) (parse "2013/01/12")))
81 | (is (= (date 2015 6 1 23 53) (parse "June 1, 2015 11:53 pm")))
82 | (is (= (date 2013 1 18 4 57 51) (parse "2013-01-18T04:57:51.0000000Z")))
83 | ;(is (= (date 2015 11 16 20 13) (parse "Nov 16, 2015 3:13 p.m. ET")))
84 | (is (= (date 2015 10 19 11 51) (parse "Oct. 19, 2015 11:51 AM ET")))
85 | (is (= (date 2015 11 16 15 13) (parse "Nov 16, 2015 3:13 p.m.")))
86 | (is (= (date 2015 11 13) (parse "13 Nov 2015")))
87 | (is (= (date 2015 11 16) (parse "Houston (Platts)--16 Nov 2015 517 pm EST/2217 GMT")))
88 | (is (= (date 2015 11 14 0 0) (parse "2015-11-14T00:00:00-05:00")))
89 | (is (= (date 2015 11 17 5 17) (parse "2015-11-17T05:17:00-05:00")))
90 | (is (= (date 2015 11 18) (parse "Wednesday, November 18, 2015")))
91 | (is (= (date 2015 11 18) (parse "11/18/2015")))
92 | (is (= (date 2015 11 20 10 33 36) (parse "2015-11-20T10:33:36")))
93 | (is (= (date 2015 11 12 13 35) (parse "November 12, 2015 1:35pm")))
94 | (is (= (date 2015 11 19) (parse "19 November 2015")))
95 | (is (= (date 2016 1 12 5 53) (parse "2016-01-12T05:53:00.000Z")))
96 | (is (= (date 2016 1 12 11 20) (parse "2016-01-12T11:20Z")))
97 | (is (= (date 2015 11 19 9 23) (parse "Nov 19, 2015 09:23 GMT")))
98 | (is (= (date 2015 11 17 0 10) (parse "17 Nov 2015 00:10")))
99 | (is (= (date 2015 11 17 16 1 37) (parse "2015-11-17T16:01:37+0000")))
100 | (is (= (date 2016 2 24 0 1) (parse "Wed Feb 24 2016 00:01 UTC+1201")))
101 | (is (= (date 2018 3 25 12) (parse "2018.03.25 12:00")))
102 | (is (= (date 2017 2 14) (parse "02142017")))
103 | (is (= (date 2017 1 16 1 45 5) (parse "2017-01-16 01:45:05 -0500")))
104 | (is (= (date 2018 4 24 2 6 14) (parse "20180424T02:06:14Z")))
105 | (let [document-time (.toDate (apply date-time [2018 05 24]))]
106 | (is (= (date 2018 5 24 4 57) (parse "4:57AM EDT" document-time "en")))
107 | (is (= (date 2018 5 24 15 4) (parse "15:04" document-time "en"))))
108 |
109 |
110 | ; nonsenses should return nil
111 | (is (= nil (parse "makes no sense")))
112 | (is (= nil (parse "2013-40-12")))))
--------------------------------------------------------------------------------
/test/timewords/de_test.clj:
--------------------------------------------------------------------------------
1 | (ns timewords.de-test
2 | (:require [clojure.test :refer :all]
3 | [clj-time.core :as joda :refer [date-time]]
4 | [timewords.core :refer [parse]])
5 | (:import (java.util Date)
6 | (org.joda.time DateTime)))
7 |
8 | (defn date [& xs] (.toDate (apply date-time xs)))
9 |
10 | (deftest de-dates-test
11 | (testing "German month names"
12 | (is (= (date 2018 1 28 16 1) (parse "28. Januar 2018, 16:01 Uhr" nil "de")))
13 | (is (= (date 2018 2 28 16 1) (parse "28. Februar 2018, 16:01 Uhr" nil "de")))
14 | (is (= (date 2018 3 28 16 1) (parse "28. März 2018, 16:01 Uhr" nil "de")))
15 | (is (= (date 2018 4 28 16 1) (parse "28. April 2018, 16:01 Uhr" nil "de")))
16 | (is (= (date 2018 5 28 16 1) (parse "28. Mai 2018, 16:01 Uhr" nil "de")))
17 | (is (= (date 2018 6 28 16 1) (parse "28. Juni 2018, 16:01 Uhr" nil "de")))
18 | (is (= (date 2018 7 28 16 1) (parse "28. Juli 2018, 16:01 Uhr" nil "de")))
19 | (is (= (date 2018 8 28 16 1) (parse "28. August 2018, 16:01 Uhr" nil "de")))
20 | (is (= (date 2018 9 28 16 1) (parse "28. September 2018, 16:01 Uhr" nil "de")))
21 | (is (= (date 2018 10 28 16 1) (parse "28. Oktober 2018, 16:01 Uhr" nil "de")))
22 | (is (= (date 2018 11 28 16 1) (parse "28. November 2018, 16:01 Uhr" nil "de")))
23 | (is (= (date 2018 12 28 16 1) (parse "28. Dezember 2018, 16:01 Uhr" nil "de")))
24 | (is (= (date 2018 3 29 12 58) (parse "Donnerstag, 29.03.2018, 12:58" nil "de")))
25 | (is (= (date 2011 10 5) (parse "5.10.2011" nil "de")))
26 | (is (= (date 2011 10 5) (parse "5. Oktober 2011" nil "de")))
27 | (is (= (date 2011 10 4) (parse "Dienstag, 4. Oktober 2011" nil "de")))))
28 |
--------------------------------------------------------------------------------
/test/timewords/en_relative_quantified_test.clj:
--------------------------------------------------------------------------------
1 | (ns timewords.en-relative-quantified-test
2 | (:require [clojure.test :refer :all]
3 | [clj-time.core :as joda]
4 | [clj-time.coerce :as tc]
5 | [timewords.core :refer :all]))
6 |
7 | (deftest en-relative-quantified
8 | (testing "some time ago"
9 | (let [parsed-datetime (tc/to-date-time (parse "now" (tc/to-date (joda/now))))]
10 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/millis 1000))))
11 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/millis 1000)))))
12 | ; past
13 | (let [parsed-datetime (tc/to-date-time (parse "a sec ago"))]
14 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/millis 2000))))
15 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/millis 0))))
16 | (is (= 0 (joda/milli parsed-datetime))))
17 | (let [parsed-datetime (tc/to-date-time (parse "1 sec ago"))]
18 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/millis 2000))))
19 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/millis 0))))
20 | (is (= 0 (joda/milli parsed-datetime))))
21 | (let [parsed-datetime (tc/to-date-time (parse "a second ago"))]
22 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/millis 2000))))
23 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/millis 0))))
24 | (is (= 0 (joda/milli parsed-datetime))))
25 | (let [parsed-datetime (tc/to-date-time (parse "1 second ago"))]
26 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/millis 2000))))
27 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/millis 0))))
28 | (is (= 0 (joda/milli parsed-datetime))))
29 | (let [parsed-datetime (tc/to-date-time (parse "16 secs ago"))]
30 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/millis 17000))))
31 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/millis 15000))))
32 | (is (= 0 (joda/milli parsed-datetime))))
33 | (let [parsed-datetime (tc/to-date-time (parse "16 seconds ago"))]
34 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/millis 17000))))
35 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/millis 15000))))
36 | (is (= 0 (joda/milli parsed-datetime))))
37 | (let [parsed-datetime (tc/to-date-time (parse "16s ago"))]
38 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/millis 17000))))
39 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/millis 15000))))
40 | (is (= 0 (joda/milli parsed-datetime))))
41 | (let [parsed-datetime (tc/to-date-time (parse "a min ago"))]
42 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/minutes 2))))
43 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/minutes 0))))
44 | (is (= 0 (joda/milli parsed-datetime))))
45 | (let [parsed-datetime (tc/to-date-time (parse "1 min ago"))]
46 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/minutes 2))))
47 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/minutes 0))))
48 | (is (= 0 (joda/milli parsed-datetime))))
49 | (let [parsed-datetime (tc/to-date-time (parse "a minute ago"))]
50 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/minutes 2))))
51 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/minutes 0))))
52 | (is (= 0 (joda/milli parsed-datetime))))
53 | (let [parsed-datetime (tc/to-date-time (parse "1 minute ago"))]
54 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/minutes 2))))
55 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/minutes 0))))
56 | (is (= 0 (joda/milli parsed-datetime))))
57 | (let [parsed-datetime (tc/to-date-time (parse "32 mins ago"))]
58 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/minutes 33))))
59 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/minutes 31))))
60 | (is (= 0 (joda/milli parsed-datetime))))
61 | (let [parsed-datetime (tc/to-date-time (parse "32m ago"))]
62 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/minutes 33))))
63 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/minutes 31))))
64 | (is (= 0 (joda/milli parsed-datetime))))
65 | (let [parsed-datetime (tc/to-date-time (parse "32 minutes ago"))]
66 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/minutes 33))))
67 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/minutes 31))))
68 | (is (= 0 (joda/milli parsed-datetime))))
69 | (let [parsed-datetime (tc/to-date-time (parse "a hour ago"))]
70 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/hours 2))))
71 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/hours 0))))
72 | (is (= 0 (joda/minute parsed-datetime)))
73 | (is (= 0 (joda/milli parsed-datetime))))
74 | (let [parsed-datetime (tc/to-date-time (parse "an hour ago"))]
75 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/hours 2))))
76 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/hours 0))))
77 | (is (= 0 (joda/minute parsed-datetime)))
78 | (is (= 0 (joda/milli parsed-datetime))))
79 | (let [parsed-datetime (tc/to-date-time (parse "1h ago"))]
80 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/hours 2))))
81 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/hours 0))))
82 | (is (= 0 (joda/minute parsed-datetime)))
83 | (is (= 0 (joda/milli parsed-datetime))))
84 | (let [parsed-datetime (tc/to-date-time (parse "1 hour ago"))]
85 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/hours 2))))
86 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/hours 0))))
87 | (is (= 0 (joda/minute parsed-datetime)))
88 | (is (= 0 (joda/milli parsed-datetime))))
89 | (let [parsed-datetime (tc/to-date-time (parse "2 hours ago"))]
90 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/hours 3))))
91 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/hours 1))))
92 | (is (= 0 (joda/minute parsed-datetime)))
93 | (is (= 0 (joda/milli parsed-datetime))))
94 | (let [parsed-datetime (tc/to-date-time (parse "3 hrs ago"))]
95 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/hours 4))))
96 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/hours 1))))
97 | (is (= 0 (joda/minute parsed-datetime)))
98 | (is (= 0 (joda/milli parsed-datetime))))
99 | (let [parsed-datetime (tc/to-date-time (parse "yesterday"))]
100 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/days 2))))
101 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/days 0))))
102 | (is (= 0 (joda/hour parsed-datetime)))
103 | (is (= 0 (joda/minute parsed-datetime)))
104 | (is (= 0 (joda/milli parsed-datetime))))
105 | (let [parsed-datetime (tc/to-date-time (parse "a day ago"))]
106 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/days 2))))
107 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/days 0))))
108 | (is (= 0 (joda/hour parsed-datetime)))
109 | (is (= 0 (joda/minute parsed-datetime)))
110 | (is (= 0 (joda/milli parsed-datetime))))
111 | (let [parsed-datetime (tc/to-date-time (parse "1d ago"))]
112 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/days 2))))
113 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/days 0))))
114 | (is (= 0 (joda/hour parsed-datetime)))
115 | (is (= 0 (joda/minute parsed-datetime)))
116 | (is (= 0 (joda/milli parsed-datetime))))
117 | (let [parsed-datetime (tc/to-date-time (parse "1 day ago"))]
118 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/days 2))))
119 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/days 0))))
120 | (is (= 0 (joda/hour parsed-datetime)))
121 | (is (= 0 (joda/minute parsed-datetime)))
122 | (is (= 0 (joda/milli parsed-datetime))))
123 | (let [parsed-datetime (tc/to-date-time (parse "2 days ago"))]
124 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/days 3))))
125 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/days 1))))
126 | (is (= 0 (joda/hour parsed-datetime)))
127 | (is (= 0 (joda/minute parsed-datetime)))
128 | (is (= 0 (joda/milli parsed-datetime))))
129 | (let [parsed-datetime (tc/to-date-time (parse "a week ago"))]
130 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/days 8))))
131 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/days 6))))
132 | (is (= 0 (joda/hour parsed-datetime)))
133 | (is (= 0 (joda/minute parsed-datetime)))
134 | (is (= 0 (joda/milli parsed-datetime))))
135 | (let [parsed-datetime (tc/to-date-time (parse "1 week ago"))]
136 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/days 8))))
137 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/days 6))))
138 | (is (= 0 (joda/hour parsed-datetime)))
139 | (is (= 0 (joda/minute parsed-datetime)))
140 | (is (= 0 (joda/milli parsed-datetime))))
141 | (let [parsed-datetime (tc/to-date-time (parse "3 weeks ago"))]
142 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/days 22))))
143 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/days 20))))
144 | (is (= 0 (joda/hour parsed-datetime)))
145 | (is (= 0 (joda/minute parsed-datetime)))
146 | (is (= 0 (joda/milli parsed-datetime))))
147 | (let [parsed-datetime (tc/to-date-time (parse "last month"))]
148 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/months 3))))
149 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/months 1))))
150 | (is (= 1 (joda/day parsed-datetime)))
151 | (is (= 0 (joda/hour parsed-datetime)))
152 | (is (= 0 (joda/minute parsed-datetime)))
153 | (is (= 0 (joda/milli parsed-datetime))))
154 | (let [parsed-datetime (tc/to-date-time (parse "previous month"))]
155 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/months 3))))
156 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/months 1))))
157 | (is (= 1 (joda/day parsed-datetime)))
158 | (is (= 0 (joda/hour parsed-datetime)))
159 | (is (= 0 (joda/minute parsed-datetime)))
160 | (is (= 0 (joda/milli parsed-datetime))))
161 | (let [parsed-datetime (tc/to-date-time (parse "a month ago"))]
162 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/months 3))))
163 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/months 1))))
164 | (is (= 1 (joda/day parsed-datetime)))
165 | (is (= 0 (joda/hour parsed-datetime)))
166 | (is (= 0 (joda/minute parsed-datetime)))
167 | (is (= 0 (joda/milli parsed-datetime))))
168 | (let [parsed-datetime (tc/to-date-time (parse "1 month ago"))]
169 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/months 3))))
170 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/months 1))))
171 | (is (= 1 (joda/day parsed-datetime)))
172 | (is (= 0 (joda/hour parsed-datetime)))
173 | (is (= 0 (joda/minute parsed-datetime)))
174 | (is (= 0 (joda/milli parsed-datetime))))
175 | (let [parsed-datetime (tc/to-date-time (parse "2 months ago"))]
176 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/months 3))))
177 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/months 1))))
178 | (is (= 1 (joda/day parsed-datetime)))
179 | (is (= 0 (joda/hour parsed-datetime)))
180 | (is (= 0 (joda/minute parsed-datetime)))
181 | (is (= 0 (joda/milli parsed-datetime))))
182 | (let [parsed-datetime (tc/to-date-time (parse "last year"))]
183 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/years 2))))
184 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/years 0))))
185 | (is (= 1 (joda/month parsed-datetime)))
186 | (is (= 1 (joda/day parsed-datetime)))
187 | (is (= 0 (joda/hour parsed-datetime)))
188 | (is (= 0 (joda/minute parsed-datetime)))
189 | (is (= 0 (joda/milli parsed-datetime))))
190 | (let [parsed-datetime (tc/to-date-time (parse "previous year"))]
191 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/years 2))))
192 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/years 0))))
193 | (is (= 1 (joda/month parsed-datetime)))
194 | (is (= 1 (joda/day parsed-datetime)))
195 | (is (= 0 (joda/hour parsed-datetime)))
196 | (is (= 0 (joda/minute parsed-datetime)))
197 | (is (= 0 (joda/milli parsed-datetime))))
198 | (let [parsed-datetime (tc/to-date-time (parse "a year ago"))]
199 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/years 2))))
200 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/years 0))))
201 | (is (= 1 (joda/month parsed-datetime)))
202 | (is (= 1 (joda/day parsed-datetime)))
203 | (is (= 0 (joda/hour parsed-datetime)))
204 | (is (= 0 (joda/minute parsed-datetime)))
205 | (is (= 0 (joda/milli parsed-datetime))))
206 | (let [parsed-datetime (tc/to-date-time (parse "1 year ago"))]
207 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/years 2))))
208 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/years 0))))
209 | (is (= 1 (joda/month parsed-datetime)))
210 | (is (= 1 (joda/day parsed-datetime)))
211 | (is (= 0 (joda/hour parsed-datetime)))
212 | (is (= 0 (joda/minute parsed-datetime)))
213 | (is (= 0 (joda/milli parsed-datetime))))
214 | (let [parsed-datetime (tc/to-date-time (parse "2 years ago"))]
215 | (is (joda/after? parsed-datetime (joda/minus (joda/now) (joda/years 3))))
216 | (is (joda/before? parsed-datetime (joda/minus (joda/now) (joda/years 1))))
217 | (is (= 1 (joda/month parsed-datetime)))
218 | (is (= 1 (joda/day parsed-datetime)))
219 | (is (= 0 (joda/hour parsed-datetime)))
220 | (is (= 0 (joda/minute parsed-datetime)))
221 | (is (= 0 (joda/milli parsed-datetime)))))
222 |
223 | (testing "some time from now"
224 | (let [parsed-datetime (tc/to-date-time (parse "a sec from now"))]
225 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/millis 0))))
226 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/millis 2000))))
227 | (is (= 0 (joda/milli parsed-datetime))))
228 | (let [parsed-datetime (tc/to-date-time (parse "1 sec from now"))]
229 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/millis 0))))
230 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/millis 2000))))
231 | (is (= 0 (joda/milli parsed-datetime))))
232 | (let [parsed-datetime (tc/to-date-time (parse "a second from now"))]
233 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/millis 0))))
234 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/millis 2000))))
235 | (is (= 0 (joda/milli parsed-datetime))))
236 | (let [parsed-datetime (tc/to-date-time (parse "1 second from now"))]
237 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/millis 0))))
238 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/millis 2000))))
239 | (is (= 0 (joda/milli parsed-datetime))))
240 | (let [parsed-datetime (tc/to-date-time (parse "16 secs from now"))]
241 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/millis 15000))))
242 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/millis 17000))))
243 | (is (= 0 (joda/milli parsed-datetime))))
244 | (let [parsed-datetime (tc/to-date-time (parse "16 seconds from now"))]
245 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/millis 15000))))
246 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/millis 17000))))
247 | (is (= 0 (joda/milli parsed-datetime))))
248 | (let [parsed-datetime (tc/to-date-time (parse "16s from now"))]
249 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/millis 15000))))
250 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/millis 17000))))
251 | (is (= 0 (joda/milli parsed-datetime))))
252 | (let [parsed-datetime (tc/to-date-time (parse "a min from now"))]
253 | (is (joda/after? parsed-datetime (joda/now)))
254 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/minutes 2))))
255 | (is (= 0 (joda/milli parsed-datetime))))
256 | (let [parsed-datetime (tc/to-date-time (parse "1 min from now"))]
257 | (is (joda/after? parsed-datetime (joda/now)))
258 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/minutes 2))))
259 | (is (= 0 (joda/milli parsed-datetime))))
260 | (let [parsed-datetime (tc/to-date-time (parse "a minute from now"))]
261 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/minutes 0))))
262 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/minutes 2))))
263 | (is (= 0 (joda/milli parsed-datetime))))
264 | (let [parsed-datetime (tc/to-date-time (parse "1 minute from now"))]
265 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/minutes 0))))
266 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/minutes 2))))
267 | (is (= 0 (joda/milli parsed-datetime))))
268 | (let [parsed-datetime (tc/to-date-time (parse "32 mins from now"))]
269 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/minutes 31))))
270 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/minutes 33))))
271 | (is (= 0 (joda/milli parsed-datetime))))
272 | (let [parsed-datetime (tc/to-date-time (parse "32 minutes from now"))]
273 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/minutes 31))))
274 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/minutes 33))))
275 | (is (= 0 (joda/milli parsed-datetime))))
276 | (let [parsed-datetime (tc/to-date-time (parse "32m from now"))]
277 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/minutes 31))))
278 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/minutes 33))))
279 | (is (= 0 (joda/milli parsed-datetime))))
280 | (let [parsed-datetime (tc/to-date-time (parse "32 minutes from now"))]
281 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/minutes 31))))
282 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/minutes 33))))
283 | (is (= 0 (joda/milli parsed-datetime))))
284 | (let [parsed-datetime (tc/to-date-time (parse "a hour from now"))]
285 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/hours 0))))
286 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/hours 2))))
287 | (is (= 0 (joda/minute parsed-datetime)))
288 | (is (= 0 (joda/milli parsed-datetime))))
289 | (let [parsed-datetime (tc/to-date-time (parse "an hour from now"))]
290 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/hours 0))))
291 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/hours 2))))
292 | (is (= 0 (joda/minute parsed-datetime)))
293 | (is (= 0 (joda/milli parsed-datetime))))
294 | (let [parsed-datetime (tc/to-date-time (parse "1h from now"))]
295 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/hours 0))))
296 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/hours 2))))
297 | (is (= 0 (joda/minute parsed-datetime)))
298 | (is (= 0 (joda/milli parsed-datetime))))
299 | (let [parsed-datetime (tc/to-date-time (parse "1 hour from now"))]
300 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/hours 0))))
301 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/hours 2))))
302 | (is (= 0 (joda/minute parsed-datetime)))
303 | (is (= 0 (joda/milli parsed-datetime))))
304 | (let [parsed-datetime (tc/to-date-time (parse "2 hours from now"))]
305 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/hours 1))))
306 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/hours 3))))
307 | (is (= 0 (joda/minute parsed-datetime)))
308 | (is (= 0 (joda/milli parsed-datetime))))
309 | (let [parsed-datetime (tc/to-date-time (parse "tomorrow"))]
310 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/days 0))))
311 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/days 2))))
312 | (is (= 0 (joda/hour parsed-datetime)))
313 | (is (= 0 (joda/minute parsed-datetime)))
314 | (is (= 0 (joda/milli parsed-datetime))))
315 | (let [parsed-datetime (tc/to-date-time (parse "a day from now"))]
316 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/days 0))))
317 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/days 2))))
318 | (is (= 0 (joda/hour parsed-datetime)))
319 | (is (= 0 (joda/minute parsed-datetime)))
320 | (is (= 0 (joda/milli parsed-datetime))))
321 | (let [parsed-datetime (tc/to-date-time (parse "1d from now"))]
322 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/days 0))))
323 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/days 2))))
324 | (is (= 0 (joda/hour parsed-datetime)))
325 | (is (= 0 (joda/minute parsed-datetime)))
326 | (is (= 0 (joda/milli parsed-datetime))))
327 | (let [parsed-datetime (tc/to-date-time (parse "1 day from now"))]
328 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/days 0))))
329 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/days 2))))
330 | (is (= 0 (joda/hour parsed-datetime)))
331 | (is (= 0 (joda/minute parsed-datetime)))
332 | (is (= 0 (joda/milli parsed-datetime))))
333 | (let [parsed-datetime (tc/to-date-time (parse "2 days from now"))]
334 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/days 1))))
335 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/days 3))))
336 | (is (= 0 (joda/hour parsed-datetime)))
337 | (is (= 0 (joda/minute parsed-datetime)))
338 | (is (= 0 (joda/milli parsed-datetime))))
339 | (let [parsed-datetime (tc/to-date-time (parse "a week from now"))]
340 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/days 6))))
341 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/days 8))))
342 | (is (= 0 (joda/hour parsed-datetime)))
343 | (is (= 0 (joda/minute parsed-datetime)))
344 | (is (= 0 (joda/milli parsed-datetime))))
345 | (let [parsed-datetime (tc/to-date-time (parse "1 week from now"))]
346 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/days 6))))
347 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/days 8))))
348 | (is (= 0 (joda/hour parsed-datetime)))
349 | (is (= 0 (joda/minute parsed-datetime)))
350 | (is (= 0 (joda/milli parsed-datetime))))
351 | (let [parsed-datetime (tc/to-date-time (parse "3 weeks from now"))]
352 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/days 20))))
353 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/days 22))))
354 | (is (= 0 (joda/hour parsed-datetime)))
355 | (is (= 0 (joda/minute parsed-datetime)))
356 | (is (= 0 (joda/milli parsed-datetime))))
357 | (let [parsed-datetime (tc/to-date-time (parse "next month"))]
358 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/months 0))))
359 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/months 2))))
360 | (is (= 1 (joda/day parsed-datetime)))
361 | (is (= 0 (joda/hour parsed-datetime)))
362 | (is (= 0 (joda/minute parsed-datetime)))
363 | (is (= 0 (joda/milli parsed-datetime))))
364 | (let [parsed-datetime (tc/to-date-time (parse "a month from now"))]
365 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/months 0))))
366 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/months 2))))
367 | (is (= 1 (joda/day parsed-datetime)))
368 | (is (= 0 (joda/hour parsed-datetime)))
369 | (is (= 0 (joda/minute parsed-datetime)))
370 | (is (= 0 (joda/milli parsed-datetime))))
371 | (let [parsed-datetime (tc/to-date-time (parse "1 month from now"))]
372 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/months 0))))
373 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/months 2))))
374 | (is (= 1 (joda/day parsed-datetime)))
375 | (is (= 0 (joda/hour parsed-datetime)))
376 | (is (= 0 (joda/minute parsed-datetime)))
377 | (is (= 0 (joda/milli parsed-datetime))))
378 | (let [parsed-datetime (tc/to-date-time (parse "2 months from now"))]
379 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/months 1))))
380 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/months 3))))
381 | (is (= 1 (joda/day parsed-datetime)))
382 | (is (= 0 (joda/hour parsed-datetime)))
383 | (is (= 0 (joda/minute parsed-datetime)))
384 | (is (= 0 (joda/milli parsed-datetime))))
385 | (let [parsed-datetime (tc/to-date-time (parse "a year from now"))]
386 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/years 0))))
387 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/years 2))))
388 | (is (= 1 (joda/month parsed-datetime)))
389 | (is (= 1 (joda/day parsed-datetime)))
390 | (is (= 0 (joda/hour parsed-datetime)))
391 | (is (= 0 (joda/minute parsed-datetime)))
392 | (is (= 0 (joda/milli parsed-datetime))))
393 | (let [parsed-datetime (tc/to-date-time (parse "1 year from now"))]
394 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/years 0))))
395 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/years 2))))
396 | (is (= 1 (joda/month parsed-datetime)))
397 | (is (= 1 (joda/day parsed-datetime)))
398 | (is (= 0 (joda/hour parsed-datetime)))
399 | (is (= 0 (joda/minute parsed-datetime)))
400 | (is (= 0 (joda/milli parsed-datetime))))
401 | (let [parsed-datetime (tc/to-date-time (parse "next year"))]
402 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/years 0))))
403 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/years 2))))
404 | (is (= 1 (joda/month parsed-datetime)))
405 | (is (= 1 (joda/day parsed-datetime)))
406 | (is (= 0 (joda/hour parsed-datetime)))
407 | (is (= 0 (joda/minute parsed-datetime)))
408 | (is (= 0 (joda/milli parsed-datetime))))
409 | (let [parsed-datetime (tc/to-date-time (parse "2 years from now"))]
410 | (is (joda/after? parsed-datetime (joda/plus (joda/now) (joda/years 1))))
411 | (is (joda/before? parsed-datetime (joda/plus (joda/now) (joda/years 3))))
412 | (is (= 1 (joda/month parsed-datetime)))
413 | (is (= 1 (joda/day parsed-datetime)))
414 | (is (= 0 (joda/hour parsed-datetime)))
415 | (is (= 0 (joda/minute parsed-datetime)))
416 | (is (= 0 (joda/milli parsed-datetime))))))
417 |
--------------------------------------------------------------------------------
/test/timewords/en_relative_test.clj:
--------------------------------------------------------------------------------
1 | (ns timewords.en-relative-test
2 | (:require [clojure.test :refer :all]
3 | [clj-time.core :as joda]
4 | [clj-time.coerce :as tc]
5 | [timewords.core :refer :all]))
6 |
7 | (deftest en-relative
8 |
9 | (testing "special cases"
10 | (let [now-datetime (joda/now)
11 | parsed-datetime (tc/to-date-time (parse "today"))]
12 | (is (= (joda/year now-datetime) (joda/year parsed-datetime)))
13 | (is (= (joda/month now-datetime) (joda/month parsed-datetime)))
14 | (is (= (joda/day now-datetime) (joda/day parsed-datetime)))
15 | (is (= 0 (joda/hour parsed-datetime)))
16 | (is (= 0 (joda/minute parsed-datetime)))
17 | (is (= 0 (joda/milli parsed-datetime)))))
18 |
19 | (testing "relative weekdays"
20 | (let [parsed-datetime (tc/to-date-time (parse "monday"))]
21 | (is (joda/before? parsed-datetime (joda/now)))
22 | (is (= 1 (joda/day-of-week parsed-datetime)))
23 | (is (= 0 (joda/hour parsed-datetime)))
24 | (is (= 0 (joda/minute parsed-datetime)))
25 | (is (= 0 (joda/milli parsed-datetime))))
26 | (is (= 2 (joda/day-of-week (tc/to-date-time (parse "tuesday")))))
27 | (is (= 3 (joda/day-of-week (tc/to-date-time (parse "wednesday")))))
28 | (is (= 4 (joda/day-of-week (tc/to-date-time (parse "thursday")))))
29 | (is (= 5 (joda/day-of-week (tc/to-date-time (parse "friday")))))
30 | (is (= 6 (joda/day-of-week (tc/to-date-time (parse "saturday")))))
31 | (is (= 7 (joda/day-of-week (tc/to-date-time (parse "sunday")))))
32 |
33 | (let [parsed-datetime (tc/to-date-time (parse "last monday"))]
34 | (is (joda/before? parsed-datetime (joda/now)))
35 | (is (= 1 (joda/day-of-week parsed-datetime)))
36 | (is (= 0 (joda/hour parsed-datetime)))
37 | (is (= 0 (joda/minute parsed-datetime)))
38 | (is (= 0 (joda/milli parsed-datetime))))
39 | (is (= 2 (joda/day-of-week (tc/to-date-time (parse "last tuesday")))))
40 | (is (= 3 (joda/day-of-week (tc/to-date-time (parse "last wednesday")))))
41 | (is (= 4 (joda/day-of-week (tc/to-date-time (parse "last thursday")))))
42 | (is (= 5 (joda/day-of-week (tc/to-date-time (parse "last friday")))))
43 | (is (= 6 (joda/day-of-week (tc/to-date-time (parse "last saturday")))))
44 | (is (= 7 (joda/day-of-week (tc/to-date-time (parse "last sunday")))))
45 | (let [parsed-datetime (tc/to-date-time (parse "last Sunday"))]
46 | (is (= 7 (joda/day-of-week parsed-datetime)))
47 | (is (= 0 (joda/hour parsed-datetime)))
48 | (is (= 0 (joda/minute parsed-datetime)))
49 | (is (= 0 (joda/milli parsed-datetime))))
50 |
51 | (let [parsed-datetime (tc/to-date-time (parse "next monday"))]
52 | (is (joda/after? parsed-datetime (joda/now)))
53 | (is (= 1 (joda/day-of-week parsed-datetime)))
54 | (is (= 0 (joda/hour parsed-datetime)))
55 | (is (= 0 (joda/minute parsed-datetime)))
56 | (is (= 0 (joda/milli parsed-datetime))))
57 | (is (= 2 (joda/day-of-week (tc/to-date-time (parse "next tuesday")))))
58 | (is (= 3 (joda/day-of-week (tc/to-date-time (parse "next wednesday")))))
59 | (is (= 4 (joda/day-of-week (tc/to-date-time (parse "next thursday")))))
60 | (is (= 5 (joda/day-of-week (tc/to-date-time (parse "next friday")))))
61 | (is (= 6 (joda/day-of-week (tc/to-date-time (parse "next saturday")))))
62 | (is (= 7 (joda/day-of-week (tc/to-date-time (parse "next sunday")))))
63 |
64 | (let [parsed-datetime (tc/to-date-time (parse "this monday"))]
65 | (is (joda/after? parsed-datetime (joda/now)))
66 | (is (= 1 (joda/day-of-week parsed-datetime)))
67 | (is (= 0 (joda/hour parsed-datetime)))
68 | (is (= 0 (joda/minute parsed-datetime)))
69 | (is (= 0 (joda/milli parsed-datetime))))
70 | (is (= 2 (joda/day-of-week (tc/to-date-time (parse "this tuesday")))))
71 | (is (= 3 (joda/day-of-week (tc/to-date-time (parse "this wednesday")))))
72 | (is (= 4 (joda/day-of-week (tc/to-date-time (parse "this thursday")))))
73 | (is (= 5 (joda/day-of-week (tc/to-date-time (parse "this friday")))))
74 | (is (= 6 (joda/day-of-week (tc/to-date-time (parse "this saturday")))))
75 | (is (= 7 (joda/day-of-week (tc/to-date-time (parse "this sunday"))))))
76 |
77 | (testing "relative months"
78 | (let [parsed-datetime (tc/to-date-time (parse "last january"))]
79 | (is (joda/before? parsed-datetime (joda/now)))
80 | (is (= 1 (-> parsed-datetime (joda/month))))
81 | (is (= 1 (-> parsed-datetime (joda/day))))
82 | (is (= 0 (-> parsed-datetime (joda/hour))))
83 | (is (= 0 (-> parsed-datetime (joda/milli)))))
84 | (is (= 2 (-> (parse "last february") (tc/to-date-time) (joda/month))))
85 | (is (= 3 (-> (parse "last march") (tc/to-date-time) (joda/month))))
86 | (is (= 4 (-> (parse "last april") (tc/to-date-time) (joda/month))))
87 | (is (= 5 (-> (parse "last may") (tc/to-date-time) (joda/month))))
88 | (is (= 6 (-> (parse "last june") (tc/to-date-time) (joda/month))))
89 | (is (= 7 (-> (parse "last july") (tc/to-date-time) (joda/month))))
90 | (is (= 8 (-> (parse "last august") (tc/to-date-time) (joda/month))))
91 | (is (= 9 (-> (parse "last september") (tc/to-date-time) (joda/month))))
92 | (is (= 10 (-> (parse "last october") (tc/to-date-time) (joda/month))))
93 | (is (= 11 (-> (parse "last november") (tc/to-date-time) (joda/month))))
94 | (is (= 12 (-> (parse "last december") (tc/to-date-time) (joda/month))))
95 |
96 | (let [parsed-datetime (tc/to-date-time (parse "in january"))]
97 | (is (joda/before? parsed-datetime (joda/now)))
98 | (is (= 1 (-> parsed-datetime (joda/month))))
99 | (is (= 1 (-> parsed-datetime (joda/day))))
100 | (is (= 0 (-> parsed-datetime (joda/hour))))
101 | (is (= 0 (-> parsed-datetime (joda/milli)))))
102 | (is (= 2 (-> (parse "in february") (tc/to-date-time) (joda/month))))
103 | (is (= 3 (-> (parse "in march") (tc/to-date-time) (joda/month))))
104 | (is (= 4 (-> (parse "in april") (tc/to-date-time) (joda/month))))
105 | (is (= 5 (-> (parse "in may") (tc/to-date-time) (joda/month))))
106 | (is (= 6 (-> (parse "in june") (tc/to-date-time) (joda/month))))
107 | (is (= 7 (-> (parse "in july") (tc/to-date-time) (joda/month))))
108 | (is (= 8 (-> (parse "in august") (tc/to-date-time) (joda/month))))
109 | (is (= 9 (-> (parse "in september") (tc/to-date-time) (joda/month))))
110 | (is (= 10 (-> (parse "in october") (tc/to-date-time) (joda/month))))
111 | (is (= 11 (-> (parse "in november") (tc/to-date-time) (joda/month))))
112 | (is (= 12 (-> (parse "in december") (tc/to-date-time) (joda/month))))
113 |
114 | (let [parsed-datetime (-> (parse "next january") (tc/to-date-time))]
115 | (is (joda/after? parsed-datetime (joda/now)))
116 | (is (= 1 (joda/month parsed-datetime)))
117 | (is (= 1 (joda/day parsed-datetime)))
118 | (is (= 0 (joda/hour parsed-datetime)))
119 | (is (= 0 (joda/milli parsed-datetime))))
120 | (is (= 2 (-> (parse "next february") (tc/to-date-time) (joda/month))))
121 | (is (= 3 (-> (parse "next march") (tc/to-date-time) (joda/month))))
122 | (is (= 4 (-> (parse "next april") (tc/to-date-time) (joda/month))))
123 | (is (= 5 (-> (parse "next may") (tc/to-date-time) (joda/month))))
124 | (is (= 6 (-> (parse "next june") (tc/to-date-time) (joda/month))))
125 | (is (= 7 (-> (parse "next july") (tc/to-date-time) (joda/month))))
126 | (is (= 8 (-> (parse "next august") (tc/to-date-time) (joda/month))))
127 | (is (= 9 (-> (parse "next september") (tc/to-date-time) (joda/month))))
128 | (is (= 10 (-> (parse "next october") (tc/to-date-time) (joda/month))))
129 | (is (= 11 (-> (parse "next november") (tc/to-date-time) (joda/month))))
130 | (is (= 12 (-> (parse "next december") (tc/to-date-time) (joda/month))))
131 |
132 | (let [parsed-datetime (-> (parse "this january") (tc/to-date-time))]
133 | (is (joda/after? parsed-datetime (joda/now)))
134 | (is (= 1 (joda/month parsed-datetime)))
135 | (is (= 1 (joda/day parsed-datetime)))
136 | (is (= 0 (joda/hour parsed-datetime)))
137 | (is (= 0 (joda/milli parsed-datetime))))
138 | (is (= 2 (-> (parse "this february") (tc/to-date-time) (joda/month))))
139 | (is (= 3 (-> (parse "this march") (tc/to-date-time) (joda/month))))
140 | (is (= 4 (-> (parse "this april") (tc/to-date-time) (joda/month))))
141 | (is (= 5 (-> (parse "this may") (tc/to-date-time) (joda/month))))
142 | (is (= 6 (-> (parse "this june") (tc/to-date-time) (joda/month))))
143 | (is (= 7 (-> (parse "this july") (tc/to-date-time) (joda/month))))
144 | (is (= 8 (-> (parse "this august") (tc/to-date-time) (joda/month))))
145 | (is (= 9 (-> (parse "this september") (tc/to-date-time) (joda/month))))
146 | (is (= 10 (-> (parse "this october") (tc/to-date-time) (joda/month))))
147 | (is (= 11 (-> (parse "this november") (tc/to-date-time) (joda/month))))
148 | (is (= 12 (-> (parse "this december") (tc/to-date-time) (joda/month)))))
149 |
150 | ; seasons
151 | (is (<= 3 (-> (parse "last spring") (tc/to-date-time) (joda/month)) 5))
152 | (is (<= 6 (-> (parse "last summer") (tc/to-date-time) (joda/month)) 8))
153 | (is (<= 9 (-> (parse "last autumn") (tc/to-date-time) (joda/month)) 11))
154 | (is (<= 9 (-> (parse "last fall") (tc/to-date-time) (joda/month)) 11))
155 | (is (or (<= 1 (-> (parse "last winter") (tc/to-date-time) (joda/month)) 2)
156 | (= 12 (-> (parse "last winter") (tc/to-date-time) (joda/month)))))
157 | (is (= nil (parse "next spring")))
158 | (is (= nil (parse "next summer")))
159 | (is (= nil (parse "next autumn")))
160 | (is (= nil (parse "next fall")))
161 | (is (= nil (parse "next winter"))))
162 |
--------------------------------------------------------------------------------
/test/timewords/en_test.clj:
--------------------------------------------------------------------------------
1 | (ns timewords.en-test
2 | (:require [clojure.test :refer :all]
3 | [timewords.fuzzy.en.en :refer :all]
4 | [timewords.fuzzy.en.absolute :refer :all]))
5 |
6 | (deftest en-test
7 |
8 | (testing "We need to get if it is pm or am for these dates"
9 | (is (false? (is-pm? "October 16, 2015: 8:18 AM ET")))
10 | (is (true? (is-pm? "October 16, 2015: 8:18 PM ET")))
11 | (is (true? (is-pm? "Mon Oct 19, 2015 5:44pm EDT")))
12 | (is (true? (is-pm? "Mon Oct 19, 2015 12:44pm EDT")))
13 | (is (false? (is-pm? "Monday 19 October 2015 13.30 BST")))
14 | (is (false? (is-pm? "Oct. 19, 2015 11:51 AM ET"))))
15 |
16 |
17 | (testing "October 16, 2015: 8:18 AM ET is 8 hours"
18 | (is (= "8" (hour "october 16, 2015: 8:18 am et"))))
19 |
20 | (testing "October 16, 2015: 8:18 PM ET is 20 hours"
21 | (is (= "20" (hour "october 16, 2015: 8:18 pm et"))))
22 |
23 | (testing "money.cnn.com"
24 | (is (= ["2015" "10" "16" "8" "18"] (parse-date "October 16, 2015: 8:18 AM ET"))))
25 |
26 | (testing "money.cnn.com"
27 | (is (= ["2015" "10" "16" "20" "18"] (parse-date "October 16, 2015: 8:18 PM ET"))))
28 |
29 | (testing "bloomberg.com"
30 | (is (= ["2015" "10" "19" "18" "22"] (parse-date "October 19, 2015 — 6:22 PM EEST"))))
31 |
32 | (testing "reuters.com"
33 | (is (= ["2015" "10" "19" "5" "44"] (parse-date "Mon Oct 19, 2015 5:44am EDT"))))
34 |
35 | (testing "reuters.com"
36 | (is (= ["2015" "10" "19" "17" "44"] (parse-date "Mon Oct 19, 2015 5:44pm EDT"))))
37 |
38 | (testing "theguardian.com"
39 | (is (= ["2015" "10" "19" "13" "30"] (parse-date "Monday 19 October 2015 13.30 BST"))))
40 |
41 | (testing "seekingalpha.com"
42 | (is (= ["2015" "10" "19" "11" "51"] (parse-date "Oct. 19, 2015 11:51 AM ET"))))
43 |
44 | (testing "bbc.com"
45 | (is (= ["2015" "10" "20"] (parse-date "20 October 2015")))))
46 |
--------------------------------------------------------------------------------
/test/timewords/es_test.clj:
--------------------------------------------------------------------------------
1 | (ns timewords.es-test
2 | (:require [clojure.test :refer :all]
3 | [clj-time.core :as joda :refer [date-time]]
4 | [timewords.core :refer [parse]])
5 | (:import (java.util Date)
6 | (org.joda.time DateTime)))
7 |
8 | (defn date [& xs] (.toDate (apply date-time xs)))
9 |
10 | (deftest es-dates-test
11 | (testing "spanish month names"
12 | (is (= (date 2018 3 29) (parse "29/03/2018" nil "es")))
13 | (is (= (date 2018 3 29 13 50) (parse "29 MAR 2018 - 13:50 CEST" nil "es")))
14 | (is (= (date 2018 3 29 13) (parse "29 MAR. 2018 13:00" nil "es")))
15 | (is (= (date 2018 3 29 13 21) (parse "29 marzo 2018 13:21h CEST" nil "es")))
16 | (is (= (date 2018 3 29) (parse "29 marzo 2018" nil "es")))
17 | (is (= (date 2018 3 29 13 51) (parse "29/03/2018 13:51h" nil "es")))
18 | (is (= (date 2018 3 29 10 55) (parse "29.03.2018 - 10:55h" nil "es")))
19 | (is (= (date 2018 3 29 11 55) (parse "29/03/2018 11:55" nil "es")))
20 | (is (= (date 2018 3 29 11 52) (parse "29/03/2018 - 11:52" nil "es")))
21 | (is (= (date 2018 3 29 12 41) (parse "12:41 - 29/03/18" nil "es")))
22 | (is (= (date 2018 3 29 11 40) (parse "29.03.2018 – 11:40 H." nil "es")))
23 | (is (= (date 2018 3 29 14 21 40) (parse "2018-03-29 14:21:40 H" nil "es")))
24 | (is (= (date 2018 3 29 12 10 50) (parse "29/03/2018 12:10:50 CET" nil "es")))
25 | (is (= (date 2018 3 29 12 10 50) (parse "29/03/2018 12:10:50" nil "es")))
26 | (is (= (date 2018 3 29 13 39) (parse "29 mar. 2018 - 13:39" nil "es")))
27 | (is (= (date 2018 3 3 4 30) (parse "sábado, 03 marzo 2018, 04:30" nil "es")))
28 | (is (= (date 2018 3 29 14 36) (parse "29/03/2018 14:36 h" nil "es")))
29 | (is (= (date 2018 3 29) (parse "Jueves, 29/03/2018" nil "es")))
30 | (is (= (date 2018 3 29) (parse "jueves 29 marzo 2018" nil "es")))
31 | (is (= (date 2018 3 28 15 42) (parse "28 de marzo de 2018. 15:42h" nil "es")))
32 | (is (= (date 2018 3 28) (parse "28 de marzo de 2018" nil "es")))
33 | ))
34 |
--------------------------------------------------------------------------------
/test/timewords/fr_test.clj:
--------------------------------------------------------------------------------
1 | (ns timewords.fr-test
2 | (:require [clojure.test :refer :all]
3 | [clj-time.core :as joda :refer [date-time]]
4 | [timewords.core :refer [parse]])
5 | (:import (java.util Date)
6 | (org.joda.time DateTime)))
7 |
8 | (defn date [& xs] (.toDate (apply date-time xs)))
9 |
10 | (deftest fr-dates-test
11 | (testing "French month names and common French formats"
12 | (is (= (date 2018 1 14 7 15) (parse "janvier 14, 2018 à 07:15" nil "fr")))
13 | (is (= (date 2018 2 10 9 12) (parse "février 10, 2018 à 09:12" nil "fr")))
14 | (is (= (date 2018 3 4 19 22) (parse "mars 4, 2018 à 19:22" nil "fr")))
15 | (is (= (date 2018 4 18 7 15) (parse "avril 18, 2018 à 7:15" nil "fr")))
16 | (is (= (date 2018 5 9 7 15) (parse "mai 09, 2018 à 07:15" nil "fr")))
17 | (is (= (date 2018 6 7) (parse "juin 7, 2018" nil "fr")))
18 | (is (= (date 2018 7 5 7 15) (parse "juillet 5, 2018 07:15" nil "fr")))
19 | (is (= (date 2018 8 25 7 15) (parse "août 25, 2018 07:15" nil "fr")))
20 | (is (= (date 2018 9 21 7 15) (parse "septembre 21, 2018 - 07:15" nil "fr")))
21 | (is (= (date 2018 10 19 17 15) (parse "octobre 19, 2018 à 17:15" nil "fr")))
22 | (is (= (date 2018 11 9) (parse "le 9 novembre, 2018" nil "fr")))
23 | (is (= (date 2018 12 1) (parse "1 décembre, 2018" nil "fr")))
24 | (is (= (date 2018 5 9 10 40) (parse "09.05.2018 à 10h40" nil "fr")))
25 | (is (= (date 2018 5 8 23 01) (parse "le 8 mai à 23h01" (.toDate (joda/date-time 2018)) "fr")))
26 | (is (= (date 2018 5 9 14 25) (parse "09/05/18 à 14h25" nil "fr")))
27 | (is (= (date 2018 5 9 14 54) (parse "le 09 mai 2018 à 14h54" nil "fr")))
28 | (is (= (date 2018 5 9 13 07) (parse "09 mai 2018, 13h07" nil "fr")))
29 | (is (= (date 2018 5 8 22 29) (parse "Le 08/05 à 22:29" (.toDate (joda/date-time 2018)) "fr")))
30 | (is (= (date 2018 5 9 15 33) (parse "9 mai 2018 à 15:33" nil "fr")))
31 | (is (= (date 2018 5 9 12 43) (parse "09 Mai 2018 : 12h43" nil "fr")))))
32 |
--------------------------------------------------------------------------------
/test/timewords/java_test.clj:
--------------------------------------------------------------------------------
1 | (ns timewords.java-test
2 | (:require [clojure.test :refer :all]
3 | [clj-time.core :refer [date-time]]
4 | [timewords.core :as c])
5 | (:import (org.joda.time DateTime)
6 | (lt.tokenmill.timewords Timewords)
7 | (java.util Date)))
8 |
9 | (defn date [& xs] (.toDate (apply date-time xs)))
10 |
11 | (deftest java-interface-test
12 | (testing "Satandard date string parse testing"
13 | (let [^Timewords timewords-parser (Timewords.)]
14 | (is (= nil (.parse timewords-parser nil)))
15 | (is (= (date 2010 7 8 0 0 0) (.parse timewords-parser "8th July 2010")))
16 | (is (= (date 2013 1 24 8 46 54) (.parse timewords-parser "2013-01-24T08:46:54Z")))
17 | (is (= (date 2013 1 24 8 46 54) (.parse timewords-parser "2013-01-24T08:46:54Z" nil "en")))
18 | (is (= (date 2013 1 24 8 46 54) (.parse timewords-parser "2013-01-24T08:46:54Z" (Date.) "en"))))))
19 |
--------------------------------------------------------------------------------
/test/timewords/lt_relative_test.clj:
--------------------------------------------------------------------------------
1 | (ns timewords.lt-relative-test
2 | (:require [clojure.test :refer :all]
3 | [clj-time.core :as joda :refer [date-time]]
4 | [timewords.core :refer [parse]])
5 | (:import (java.util Date)
6 | (org.joda.time DateTime)))
7 |
8 | (defn date [& xs] (.toDate (apply date-time xs)))
9 |
10 | (deftest lt-relative-timewords
11 | (testing "today variations"
12 | (let [document-time (date 2018 4 7 12 3)]
13 | (is (= (date 2018 4 6) (parse "prieš 1 d." document-time "lt")))
14 | (is (= (date 2018 4 6) (parse "prieš 1 d" document-time "lt")))
15 | (is (= (date 2018 4 5) (parse "prieš 2 d. " document-time "lt")))
16 | (is (= (date 2018 3 28) (parse "prieš 10 d." document-time "lt"))))
17 | (let [document-time (date 2018 4 7 12 3)]
18 | (is (= (date 2018 4 7 13 16) (parse "šiandien 13:16" document-time "lt")))
19 | (is (= (date 2018 4 7 4 47) (parse "šiandien 04:47" document-time "lt")))
20 | (is (= (date 2018 4 7 22 00) (parse "šiandien 22:00" document-time "lt")))
21 | (is (= (date 2018 4 6 22 00) (parse "vakar 22:00" document-time "lt"))))
22 | (let [document-time (date 2018 4 7 12 3)]
23 | (is (= (date 2018 4 6) (parse "1 d. prieš" document-time "lt")))
24 | (is (= (date 2018 4 6) (parse "1 d prieš" document-time "lt")))
25 | (is (= (date 2018 3 31) (parse "1 sav. prieš" document-time "lt")))
26 | (is (= (date 2018 3 31) (parse "1 sav prieš" document-time "lt")))
27 | ; use a timezone for document time setup
28 | #_(is (= (date 2018 4 7 11) (parse "1 val prieš" document-time "lt")))
29 | #_(is (= (date 2018 4 7 11) (parse "7 val prieš" document-time "lt"))))
30 | ;(is (= nil (parse "Publikuota: 21:05" (Date.) "lt")))
31 | ))
32 |
--------------------------------------------------------------------------------
/test/timewords/lt_test.clj:
--------------------------------------------------------------------------------
1 | (ns timewords.lt-test
2 | (:require [clojure.test :refer :all]
3 | [clj-time.core :as joda :refer [date-time]]
4 | [timewords.core :refer [parse]])
5 | (:import (java.util Date)
6 | (org.joda.time DateTime)))
7 |
8 | (defn date [& xs] (.toDate (apply date-time xs)))
9 |
10 | (deftest lt-dates-test
11 |
12 | (testing "lithuanian month names"
13 | (is (= (date 2000 1 3) (parse "2000 sausio 3" nil "lt")))
14 | (is (= (date 2000 1 3) (parse "2000 sausio 3 d." nil "lt")))
15 | (is (= (date 2000 1 3) (parse "2000 sausio 3d." nil "lt")))
16 | (is (= (date 2000 1 3 12 34) (parse "2000 sausio 3 d. 12:34" nil "lt")))
17 | (is (= (date 2000 1 3 12 34) (parse "2000 sausio 3d. 12:34" nil "lt")))
18 | (is (= (date 2000 1 30 12 34) (parse "2000 sausio 30d. 12:34" nil "lt")))
19 | (is (= (date 2000 2 3) (parse "2000 vasario 3" nil "lt")))
20 | (is (= (date 2000 3 3) (parse "2000 kovo 3" nil "lt")))
21 | (is (= (date 2000 3 3) (parse "2000 kovas 3" nil "lt")))
22 | (is (= (date 2000 4 3) (parse "2000 balandžio 3" nil "lt")))
23 | (is (= (date 2000 5 3) (parse "2000 gegužės 3" nil "lt")))
24 | (is (= (date 2000 6 3) (parse "2000 birželis 3" nil "lt")))
25 | (is (= (date 2000 6 3) (parse "2000 birželio 3" nil "lt")))
26 | (is (= (date 2000 7 3) (parse "2000 liepos 3" nil "lt")))
27 | (is (= (date 2000 8 3) (parse "2000 rugpjūčio 3" nil "lt")))
28 | (is (= (date 2000 9 3) (parse "2000 rugsėjo 3" nil "lt")))
29 | (is (= (date 2000 10 3) (parse "2000 spalio 3" nil "lt")))
30 | (is (= (date 2000 11 3) (parse "2000 lapkričio 3" nil "lt")))
31 | (is (= (date 2000 12 3) (parse "2000 gruodžio 3" nil "lt")))
32 | (is (= (date 2013 12 2 8 14) (parse " 2013/12/02 8:14" nil "lt")))
33 | (is (= (date (.getYear (DateTime.)) 3 16 15 17) (parse "Kovo 16 d. 15:17" (Date.) "lt")))
34 | (is (= (date 2016 12 22 11 10) (parse "2016 m. gruodžio 22 d. 11:10" nil "lt")))
35 | (is (= (date 2000 1 3 12 13) (parse "2000 sausio 3 12:13" nil "lt")))
36 | (is (= (date 2000 1 3 12 13 14) (parse "2000 sausio 3 12:13:14" nil "lt")))
37 | (is (= (date 1999 3 13) (parse "1999 metų kovo 13" nil "lt")))
38 | (is (= (date (-> (joda/now) (joda/year)) 3 13) (parse "kovo 13" nil "lt")))
39 | (is (= (date 2005 3 13) (parse "kovo 13" (.toDate (joda/date-time 2005)) "lt")))
40 | (is (= (date 2018 3 20 9 40) (parse "2018 03 20 9:40" nil "lt")))
41 | (is (= (date 2018 03 22 18 30) (parse "2018-03-22 / 18:30" nil "lt")))
42 | (is (= (date 2018 3 22 21 5 43) (parse "2018-03-22T21:05:43+02:00" nil "lt")))
43 | (is (= (date 2018 3 22 7 30 44) (parse "2018 kovo mėn. 22 d. 07:30:44" nil "lt")))
44 | (is (= (date 2018 3 22 16 1) (parse "16:01 2018.03.22" nil "lt")))
45 | (is (= (date 2018 3 20 18 57) (parse "2018 kovo 20d. 18:57" nil "lt")))
46 | (is (= (date 2018 3 12 14 4) (parse "Pirmadienis, 12 Kovas 2018 14:04" nil "lt")))
47 | (is (= (date 2018 3 26) (parse "2018 kovo 26" nil "lt")))
48 | (is (= (date 2018 3 24 10 9) (parse "2018 kovo 24 d. 10:09" nil "lt")))
49 | (is (= (date 2018 3 25 12) (parse "2018.03.25 12:00" nil "lt")))
50 | (is (= (date 2018 3 26) (parse "Kov 26, 2018" nil "lt")))
51 | (is (= (date 2017 9 6) (parse "2017/09/06" nil "lt")))
52 | (is (= (date 2018 4 3) (parse "2018.04.03" nil "lt")))
53 | (is (= (date 2017 8 23) (parse "17-08-23" nil "lt")))
54 | (is (= (date 2017 11 7) (parse "2017 lapkričio 7" nil "lt")))
55 | (is (= (date 2018 1 3 10 8) (parse "SAU 03 10:08 2018" nil "lt")))
56 | (is (= (date 2018 2 3 10 8) (parse "VAS 03 10:08 2018" nil "lt")))
57 | (is (= (date 2018 3 3 10 8) (parse "KOV 03 10:08 2018" nil "lt")))
58 | (is (= (date 2018 4 3 10 8) (parse "BAL 03 10:08 2018" nil "lt")))
59 | (is (= (date 2018 5 3 10 8) (parse "GEG 03 10:08 2018" nil "lt")))
60 | (is (= (date 2018 6 3 10 8) (parse "BIR 03 10:08 2018" nil "lt")))
61 | (is (= (date 2018 7 3 10 8) (parse "LIE 03 10:08 2018" nil "lt")))
62 | (is (= (date 2018 8 3 10 8) (parse "RGP 03 10:08 2018" nil "lt")))
63 | (is (= (date 2018 9 3 10 8) (parse "RGS 03 10:08 2018" nil "lt")))
64 | (is (= (date 2018 10 3 10 8) (parse "SPA 03 10:08 2018" nil "lt")))
65 | (is (= (date 2018 11 3 10 8) (parse "LAP 03 10:08 2018" nil "lt")))
66 | (is (= (date 2018 12 3 10 8) (parse "GRU 03 10:08 2018" nil "lt")))
67 | (is (= (date 2018 4 3) (parse "balandžio 03, 2018" nil "lt")))
68 | (is (= (date 2018 3 27) (parse "2018 kovo 27, antradienis" nil "lt")))))
--------------------------------------------------------------------------------
/test/timewords/ru_test.clj:
--------------------------------------------------------------------------------
1 | (ns timewords.ru-test
2 | (:require [clojure.test :refer :all]
3 | [clj-time.core :as joda :refer [date-time]]
4 | [timewords.core :refer [parse]])
5 | (:import (java.util Date)
6 | (org.joda.time DateTime)))
7 |
8 | (defn date [& xs] (.toDate (apply date-time xs)))
9 |
10 | (deftest ru-dates-test
11 | (testing "russian month names"
12 | (is (= (date 2018 4 7 19 19) (parse "7 April 2018, 19:19" nil "en")))
13 | (is (= (date 2018 4 7 19 19) (parse "7 апреля 2018, 19:19" nil "ru")))
14 | (is (= (date 2018 4 7 22 46) (parse "22:46, 7 апреля 2018" nil "ru")))
15 | (is (= (date 2018 4 6 16 20) (parse "6 апреля 16:20" (.toDate (joda/date-time 2018)) "ru")))
16 | (is (= (date 2018 3 28 16 32) (parse "28 марта 16:32" (.toDate (joda/date-time 2018)) "ru")))
17 | (is (= (date 2018 4 7 20 55) (parse "7 апреля 2018 20:55" nil "ru")))
18 | (is (= (date 2018 4 7 22 22) (parse "7 апреля, 22:22" (.toDate (joda/date-time 2018)) "ru")))
19 | (is (= (date 2018 4 7 21 55) (parse "07.04.2018, 21:55" nil "ru")))
20 | (is (= (date 2018 3 1 22 06) (parse "01.03.2018 в 22:06" nil "ru")))
21 | (is (= (date 2018 4 8 0 01) (parse "08.04.2018 00:01" nil "ru")))
22 | (is (= (date 2018 4 6 20 10) (parse "06.04.2018 - 20:10" nil "ru")))
23 | (is (= (date 2018 4 7 19 0) (parse "19:00 07/04/2018" nil "ru")))
24 | (is (= (date 2018 4 7 17 31) (parse "7 апреля 2018 г., 17:31" nil "ru")))
25 | (is (= (date 2018 4 8 0 38) (parse "00:38 08.04.2018" nil "ru")))
26 | (is (= (date 2018 4 7 23 56) (parse "07 Апрель 2018, 23:56" nil "ru")))
27 | (is (= (date 2018 3 31 17 56) (parse "31 Март 2018, 17:56" nil "ru")))
28 | (is (= (date 2018 2 27 10 0) (parse "27 Февраль 2018, 10:00" nil "ru")))
29 | (is (= (date 2017 11 22 1 42) (parse "22 Ноябрь 2017, 01:42" nil "ru")))
30 | (is (= (date 2017 10 23 10 41) (parse "23 Октябрь 2017, 10:41" nil "ru")))
31 | (is (= (date 2017 9 26 16 33) (parse "26 Сентябрь 2017, 16:33" nil "ru")))
32 | (is (= (date 2017 8 30 19 5) (parse "30 Август 2017, 19:05" nil "ru")))
33 | (is (= (date 2017 7 26 10 0) (parse "26 Июль 2017, 10:00" nil "ru")))
34 | (is (= (date 2017 6 29 10 0) (parse "29 Июнь 2017, 10:00" nil "ru")))
35 | (is (= (date 2017 5 30 10 16) (parse "30 Май 2017, 10:16" nil "ru")))
36 | (is (= (date 2018 1 31 13 8) (parse "31 Январь 2018, 13:08" nil "ru")))
37 | (is (= (date 2017 12 29 19 31) (parse "29 Декабрь 2017, 19:31" nil "ru")))
38 | (is (= (date 2017 4 29 19 31) (parse "19:31 29 апреля 2017" nil "ru")))
39 | ;сегодня15:23
40 | ; вчера в 22:06
41 | ))
42 |
--------------------------------------------------------------------------------
/test/timewords/special_cases_test.clj:
--------------------------------------------------------------------------------
1 | (ns timewords.special-cases-test
2 | (:require [clojure.test :refer :all]
3 | [clj-time.core :as joda :refer [date-time]]
4 | [timewords.standard.formats :as fmts])
5 | (:import (java.util Date Locale)))
6 |
7 | (defn date [& xs] (.toDate (apply date-time xs)))
8 |
9 | (deftest special-cases
10 | (let [document-time (apply date-time [2018 05 24])]
11 | (is (= (date 2018 5 24 4 57) (fmts/special-cases "4:57AM EDT" Locale/ENGLISH document-time)))
12 | (is (= (date 2018 5 24 15 4) (fmts/special-cases "15:04" Locale/ENGLISH document-time)))))
13 |
--------------------------------------------------------------------------------