(ns clj-time.format "Utilities for parsing and unparsing DateTimes as Strings. Parsing and printing are controlled by formatters. You can either use one of the built in ISO 8601 and a single RFC 822 formatters or define your own, e.g.: (def built-in-formatter (formatters :basic-date-time)) (def custom-formatter (formatter \"yyyyMMdd\")) To see a list of available built-in formatters and an example of a date-time printed in their format: (show-formatters) Once you have a formatter, parsing and printing are straightforward: => (parse custom-formatter \"20100311\") # => (unparse custom-formatter (date-time 2010 10 3)) \"20101003\" By default the parse function always returns a DateTime instance with a UTC time zone, and the unparse function always represents a given DateTime instance in UTC. A formatter can be modified to different timezones, locales, etc with the functions with-zone, with-locale, with-chronology, with-default-year and with-pivot-year." (:refer-clojure :exclude [extend second]) (:require [clj-time.core :refer :all] [clojure.set :refer [difference]]) (:import [java.util Locale] [org.joda.time Chronology DateTime DateTimeZone Interval LocalDateTime Period PeriodType LocalDate LocalTime] [org.joda.time.format DateTimeFormat DateTimeFormatter DateTimePrinter DateTimeFormatterBuilder DateTimeParser ISODateTimeFormat])) (declare formatters) ;; The formatters map and show-formatters idea are strait from chrono. (defn formatter "Returns a custom formatter for the given date-time pattern or keyword." (^org.joda.time.format.DateTimeFormatter [fmts] (formatter fmts utc)) (^org.joda.time.format.DateTimeFormatter [fmts ^DateTimeZone dtz] (cond (keyword? fmts) (.withZone ^DateTimeFormatter (get formatters fmts) dtz) (string? fmts) (.withZone (DateTimeFormat/forPattern fmts) dtz) :else (.withZone ^DateTimeFormatter fmts dtz))) (^org.joda.time.format.DateTimeFormatter [^DateTimeZone dtz fmts & more] (let [printer (.getPrinter ^DateTimeFormatter (formatter fmts dtz)) parsers (map #(.getParser ^DateTimeFormatter (formatter % dtz)) (cons fmts more))] (-> (DateTimeFormatterBuilder.) ^DateTimeFormatterBuilder (.append ^DateTimePrinter printer ^"[Lorg.joda.time.format.DateTimeParser;" (into-array DateTimeParser parsers)) (.toFormatter) (.withZone dtz))))) (defn formatter-local "Returns a custom formatter with no time zone info." (^org.joda.time.format.DateTimeFormat [^String fmt] (DateTimeFormat/forPattern fmt))) (defn with-chronology "Return a copy of a formatter that uses the given Chronology." ^org.joda.time.format.DateTimeFormatter [^DateTimeFormatter f ^Chronology c] (.withChronology f c)) (defn with-locale "Return a copy of a formatter that uses the given Locale." ^org.joda.time.format.DateTimeFormatter [^DateTimeFormatter f ^Locale l] (.withLocale f l)) (defn with-pivot-year "Return a copy of a formatter that uses the given pivot year." ^org.joda.time.format.DateTimeFormatter [^DateTimeFormatter f ^Long pivot-year] (.withPivotYear f pivot-year)) (defn with-zone "Return a copy of a formatter that uses the given DateTimeZone." ^org.joda.time.format.DateTimeFormatter [^DateTimeFormatter f ^DateTimeZone dtz] (.withZone f dtz)) (defn with-default-year "Return a copy of a formatter that uses the given default year." ^org.joda.time.format.DateTimeFormatter [^DateTimeFormatter f ^Integer default-year] (.withDefaultYear f default-year)) (def ^{:doc "Map of ISO 8601 and a single RFC 822 formatters that can be used for parsing and, in most cases, printing."} formatters (into {} (map (fn [[k ^DateTimeFormatter f]] [k (.withZone f ^DateTimeZone utc)]) {:basic-date (ISODateTimeFormat/basicDate) :basic-date-time (ISODateTimeFormat/basicDateTime) :basic-date-time-no-ms (ISODateTimeFormat/basicDateTimeNoMillis) :basic-ordinal-date (ISODateTimeFormat/basicOrdinalDate) :basic-ordinal-date-time (ISODateTimeFormat/basicOrdinalDateTime) :basic-ordinal-date-time-no-ms (ISODateTimeFormat/basicOrdinalDateTimeNoMillis) :basic-time (ISODateTimeFormat/basicTime) :basic-time-no-ms (ISODateTimeFormat/basicTimeNoMillis) :basic-t-time (ISODateTimeFormat/basicTTime) :basic-t-time-no-ms (ISODateTimeFormat/basicTTimeNoMillis) :basic-week-date (ISODateTimeFormat/basicWeekDate) :basic-week-date-time (ISODateTimeFormat/basicWeekDateTime) :basic-week-date-time-no-ms (ISODateTimeFormat/basicWeekDateTimeNoMillis) :date (ISODateTimeFormat/date) :date-element-parser (ISODateTimeFormat/dateElementParser) :date-hour (ISODateTimeFormat/dateHour) :date-hour-minute (ISODateTimeFormat/dateHourMinute) :date-hour-minute-second (ISODateTimeFormat/dateHourMinuteSecond) :date-hour-minute-second-fraction (ISODateTimeFormat/dateHourMinuteSecondFraction) :date-hour-minute-second-ms (ISODateTimeFormat/dateHourMinuteSecondMillis) :date-opt-time (ISODateTimeFormat/dateOptionalTimeParser) :date-parser (ISODateTimeFormat/dateParser) :date-time (ISODateTimeFormat/dateTime) :date-time-no-ms (ISODateTimeFormat/dateTimeNoMillis) :date-time-parser (ISODateTimeFormat/dateTimeParser) :hour (ISODateTimeFormat/hour) :hour-minute (ISODateTimeFormat/hourMinute) :hour-minute-second (ISODateTimeFormat/hourMinuteSecond) :hour-minute-second-fraction (ISODateTimeFormat/hourMinuteSecondFraction) :hour-minute-second-ms (ISODateTimeFormat/hourMinuteSecondMillis) :local-date-opt-time (ISODateTimeFormat/localDateOptionalTimeParser) :local-date (ISODateTimeFormat/localDateParser) :local-time (ISODateTimeFormat/localTimeParser) :ordinal-date (ISODateTimeFormat/ordinalDate) :ordinal-date-time (ISODateTimeFormat/ordinalDateTime) :ordinal-date-time-no-ms (ISODateTimeFormat/ordinalDateTimeNoMillis) :time (ISODateTimeFormat/time) :time-element-parser (ISODateTimeFormat/timeElementParser) :time-no-ms (ISODateTimeFormat/timeNoMillis) :time-parser (ISODateTimeFormat/timeParser) :t-time (ISODateTimeFormat/tTime) :t-time-no-ms (ISODateTimeFormat/tTimeNoMillis) :week-date (ISODateTimeFormat/weekDate) :week-date-time (ISODateTimeFormat/weekDateTime) :week-date-time-no-ms (ISODateTimeFormat/weekDateTimeNoMillis) :weekyear (ISODateTimeFormat/weekyear) :weekyear-week (ISODateTimeFormat/weekyearWeek) :weekyear-week-day (ISODateTimeFormat/weekyearWeekDay) :year (ISODateTimeFormat/year) :year-month (ISODateTimeFormat/yearMonth) :year-month-day (ISODateTimeFormat/yearMonthDay) :rfc822 (with-locale (formatter "EEE, dd MMM yyyy HH:mm:ss Z") java.util.Locale/US) :mysql (formatter "yyyy-MM-dd HH:mm:ss")}))) (def ^{:private true} parsers #{:date-element-parser :date-opt-time :date-parser :date-time-parser :local-date-opt-time :local-date :local-time :time-element-parser :time-parser}) (def ^{:private true} printers (difference (set (keys formatters)) parsers)) (defn parse "Returns a DateTime instance in the UTC time zone obtained by parsing the given string according to the given formatter." (^org.joda.time.DateTime [^DateTimeFormatter fmt ^String s] (.parseDateTime fmt s)) (^org.joda.time.DateTime [^String s] (first (for [f (vals formatters) :let [d (try (parse f s) (catch Exception _ nil))] :when d] d)))) (defn parse-local "Returns a LocalDateTime instance obtained by parsing the given string according to the given formatter." (^org.joda.time.LocalDateTime [^DateTimeFormatter fmt ^String s] (.parseLocalDateTime fmt s)) (^org.joda.time.LocalDateTime [^String s] (first (for [f (vals formatters) :let [d (try (parse-local f s) (catch Exception _ nil))] :when d] d)))) (defn parse-local-date "Returns a LocalDate instance obtained by parsing the given string according to the given formatter." (^org.joda.time.LocalDate [^DateTimeFormatter fmt ^String s] (.parseLocalDate fmt s)) (^org.joda.time.LocalDate [^String s] (first (for [f (vals formatters) :let [d (try (parse-local-date f s) (catch Exception _ nil))] :when d] d)))) (defn parse-local-time "Returns a LocalTime instance obtained by parsing the given string according to the given formatter." (^org.joda.time.LocalTime [^DateTimeFormatter fmt ^ String s] (.parseLocalTime fmt s)) ([^String s] (first (for [f (vals formatters) :let [d (try (parse-local-time f s) (catch Exception _ nil))] :when d] d)))) (defn unparse "Returns a string representing the given DateTime instance in UTC and in the form determined by the given formatter." ^String [^DateTimeFormatter fmt ^DateTime dt] (.print fmt dt)) (defn unparse-local "Returns a string representing the given LocalDateTime instance in the form determined by the given formatter." ^String [^DateTimeFormatter fmt ^LocalDateTime dt] (.print fmt dt)) (defn unparse-local-date "Returns a string representing the given LocalDate instance in the form determined by the given formatter." ^String [^DateTimeFormatter fmt ^LocalDate ld] (.print fmt ld)) (defn unparse-local-time "Returns a string representing the given LocalTime instance in the form determined by the given formatter." ^String [^DateTimeFormatter fmt ^LocalTime lt] (.print fmt lt)) (defn show-formatters "Shows how a given DateTime, or by default the current time, would be formatted with each of the available printing formatters." ([] (show-formatters (now))) ([^DateTime dt] (doseq [p (sort printers)] (let [fmt (formatters p)] (printf "%-40s%s\n" p (unparse fmt dt)))))) (defprotocol Mappable (instant->map [instant] "Returns a map representation of the given instant. It will contain the following keys: :years, :months, :days, :hours, :minutes and :seconds.")) (defn- to-map [years months days hours minutes seconds] {:years years :months months :days days :hours hours :minutes minutes :seconds seconds}) (extend-protocol Mappable DateTime (instant->map [dt] (to-map (.getYear dt) (.getMonthOfYear dt) (.getDayOfMonth dt) (.getHourOfDay dt) (.getMinuteOfHour dt) (.getSecondOfMinute dt)))) (extend-protocol Mappable Period (instant->map [period] (to-map (.getYears period) (.getMonths period) (.getDays period) (.getHours period) (.getMinutes period) (.getSeconds period)))) (extend-protocol Mappable Interval (instant->map [it] (instant->map (.toPeriod it (PeriodType/yearMonthDayTime)))))