2012-11-14 21:25:02 +04:00
|
|
|
{-# LANGUAGE NoMonoLocalBinds #-}
|
2008-11-11 15:34:05 +03:00
|
|
|
{-|
|
|
|
|
|
2009-12-13 01:19:57 +03:00
|
|
|
Date parsing and utilities for hledger.
|
|
|
|
|
2008-11-27 07:31:01 +03:00
|
|
|
For date and time values, we use the standard Day and UTCTime types.
|
|
|
|
|
2008-11-27 03:35:00 +03:00
|
|
|
A 'SmartDate' is a date which may be partially-specified or relative.
|
2009-12-13 01:19:57 +03:00
|
|
|
Eg 2008\/12\/31, but also 2008\/12, 12\/31, tomorrow, last week, next year.
|
2008-11-27 10:50:02 +03:00
|
|
|
We represent these as a triple of strings like (\"2008\",\"12\",\"\"),
|
|
|
|
(\"\",\"\",\"tomorrow\"), (\"\",\"last\",\"week\").
|
2008-11-11 15:34:05 +03:00
|
|
|
|
2008-11-27 07:31:01 +03:00
|
|
|
A 'DateSpan' is the span of time between two specific calendar dates, or
|
2008-12-04 02:20:38 +03:00
|
|
|
an open-ended span where one or both dates are unspecified. (A date span
|
|
|
|
with both ends unspecified matches all dates.)
|
|
|
|
|
2010-08-03 21:05:02 +04:00
|
|
|
An 'Interval' is ledger's \"reporting interval\" - weekly, monthly,
|
2008-12-04 02:20:38 +03:00
|
|
|
quarterly, etc.
|
2008-11-27 07:31:01 +03:00
|
|
|
|
2008-11-11 15:34:05 +03:00
|
|
|
-}
|
|
|
|
|
2011-04-22 17:55:42 +04:00
|
|
|
-- XXX fromGregorian silently clips bad dates, use fromGregorianValid instead ?
|
|
|
|
|
2012-04-16 20:44:41 +04:00
|
|
|
module Hledger.Data.Dates (
|
|
|
|
-- * Misc date handling utilities
|
|
|
|
getCurrentDay,
|
|
|
|
getCurrentMonth,
|
|
|
|
getCurrentYear,
|
|
|
|
nulldate,
|
|
|
|
spanContainsDate,
|
2012-12-06 04:28:23 +04:00
|
|
|
parsedateM,
|
2012-04-16 20:44:41 +04:00
|
|
|
parsedate,
|
|
|
|
showDate,
|
2013-09-27 02:06:48 +04:00
|
|
|
showDateSpan,
|
2012-04-16 20:44:41 +04:00
|
|
|
elapsedSeconds,
|
|
|
|
prevday,
|
|
|
|
parsePeriodExpr,
|
|
|
|
nulldatespan,
|
|
|
|
tests_Hledger_Data_Dates,
|
|
|
|
failIfInvalidYear,
|
|
|
|
datesepchar,
|
|
|
|
datesepchars,
|
2013-12-07 02:06:12 +04:00
|
|
|
spanStart,
|
|
|
|
spanEnd,
|
|
|
|
spansSpan,
|
2012-04-16 20:44:41 +04:00
|
|
|
spanIntersect,
|
2012-05-19 06:56:26 +04:00
|
|
|
spansIntersect,
|
|
|
|
spanUnion,
|
|
|
|
spansUnion,
|
2012-04-16 20:44:41 +04:00
|
|
|
orDatesFrom,
|
|
|
|
smartdate,
|
|
|
|
splitSpan,
|
|
|
|
fixSmartDate,
|
|
|
|
fixSmartDateStr,
|
2012-04-16 21:09:27 +04:00
|
|
|
fixSmartDateStrEither,
|
2012-04-16 20:44:41 +04:00
|
|
|
fixSmartDateStrEither',
|
|
|
|
daysInSpan,
|
|
|
|
maybePeriod,
|
2012-05-19 06:56:26 +04:00
|
|
|
mkdatespan,
|
2012-04-16 20:44:41 +04:00
|
|
|
)
|
2008-11-22 15:18:19 +03:00
|
|
|
where
|
2008-11-11 15:34:05 +03:00
|
|
|
|
2011-05-28 08:11:44 +04:00
|
|
|
import Control.Monad
|
|
|
|
import Data.List
|
|
|
|
import Data.Maybe
|
2008-11-11 15:34:05 +03:00
|
|
|
import Data.Time.Format
|
2011-05-28 08:11:44 +04:00
|
|
|
import Data.Time.Calendar
|
2008-11-27 04:49:13 +03:00
|
|
|
import Data.Time.Calendar.OrdinalDate
|
2011-05-28 08:11:44 +04:00
|
|
|
import Data.Time.Clock
|
|
|
|
import Data.Time.LocalTime
|
2013-12-07 02:06:12 +04:00
|
|
|
import Safe (headMay, lastMay, readMay)
|
2010-03-09 04:51:21 +03:00
|
|
|
import System.Locale (defaultTimeLocale)
|
2011-05-28 08:11:44 +04:00
|
|
|
import Test.HUnit
|
2008-11-27 03:35:00 +03:00
|
|
|
import Text.ParserCombinators.Parsec
|
2011-05-28 08:11:44 +04:00
|
|
|
import Text.Printf
|
|
|
|
|
2010-05-20 03:08:53 +04:00
|
|
|
import Hledger.Data.Types
|
2011-05-28 08:11:44 +04:00
|
|
|
import Hledger.Utils
|
2008-11-11 15:34:05 +03:00
|
|
|
|
2008-11-27 07:01:07 +03:00
|
|
|
showDate :: Day -> String
|
2014-03-22 14:31:13 +04:00
|
|
|
showDate = formatTime defaultTimeLocale "%0C%y/%m/%d"
|
2008-11-11 15:34:05 +03:00
|
|
|
|
2013-09-27 02:06:48 +04:00
|
|
|
showDateSpan (DateSpan from to) =
|
|
|
|
concat
|
2014-03-22 14:31:59 +04:00
|
|
|
[maybe "" showDate from
|
2013-09-27 02:06:48 +04:00
|
|
|
,"-"
|
2014-03-22 14:31:59 +04:00
|
|
|
,maybe "" (showDate . prevday) to
|
2013-09-27 02:06:48 +04:00
|
|
|
]
|
|
|
|
|
2011-03-11 21:45:57 +03:00
|
|
|
-- | Get the current local date.
|
2009-01-24 22:48:37 +03:00
|
|
|
getCurrentDay :: IO Day
|
|
|
|
getCurrentDay = do
|
2008-11-26 00:30:21 +03:00
|
|
|
t <- getZonedTime
|
2008-11-27 07:01:07 +03:00
|
|
|
return $ localDay (zonedTimeToLocalTime t)
|
2008-11-27 03:35:00 +03:00
|
|
|
|
2011-08-07 19:31:36 +04:00
|
|
|
-- | Get the current local month number.
|
|
|
|
getCurrentMonth :: IO Int
|
|
|
|
getCurrentMonth = do
|
|
|
|
(_,m,_) <- toGregorian `fmap` getCurrentDay
|
|
|
|
return m
|
|
|
|
|
2011-03-11 21:45:57 +03:00
|
|
|
-- | Get the current local year.
|
|
|
|
getCurrentYear :: IO Integer
|
|
|
|
getCurrentYear = do
|
|
|
|
(y,_,_) <- toGregorian `fmap` getCurrentDay
|
|
|
|
return y
|
|
|
|
|
2008-11-27 07:01:07 +03:00
|
|
|
elapsedSeconds :: Fractional a => UTCTime -> UTCTime -> a
|
2009-09-22 19:56:59 +04:00
|
|
|
elapsedSeconds t1 = realToFrac . diffUTCTime t1
|
2008-11-27 03:35:00 +03:00
|
|
|
|
2013-12-07 02:06:12 +04:00
|
|
|
spanStart :: DateSpan -> Maybe Day
|
|
|
|
spanStart (DateSpan d _) = d
|
|
|
|
|
|
|
|
spanEnd :: DateSpan -> Maybe Day
|
|
|
|
spanEnd (DateSpan _ d) = d
|
|
|
|
|
|
|
|
-- | Get overall span enclosing multiple sequentially ordered spans.
|
|
|
|
spansSpan :: [DateSpan] -> DateSpan
|
|
|
|
spansSpan spans = DateSpan (maybe Nothing spanStart $ headMay spans) (maybe Nothing spanEnd $ lastMay spans)
|
|
|
|
|
2008-12-04 02:20:38 +03:00
|
|
|
-- | Split a DateSpan into one or more consecutive spans at the specified interval.
|
|
|
|
splitSpan :: Interval -> DateSpan -> [DateSpan]
|
2009-06-05 13:44:20 +04:00
|
|
|
splitSpan _ (DateSpan Nothing Nothing) = [DateSpan Nothing Nothing]
|
2011-01-14 07:32:08 +03:00
|
|
|
splitSpan NoInterval s = [s]
|
2011-01-14 08:01:00 +03:00
|
|
|
splitSpan (Days n) s = splitspan startofday (applyN n nextday) s
|
|
|
|
splitSpan (Weeks n) s = splitspan startofweek (applyN n nextweek) s
|
|
|
|
splitSpan (Months n) s = splitspan startofmonth (applyN n nextmonth) s
|
|
|
|
splitSpan (Quarters n) s = splitspan startofquarter (applyN n nextquarter) s
|
|
|
|
splitSpan (Years n) s = splitspan startofyear (applyN n nextyear) s
|
|
|
|
splitSpan (DayOfMonth n) s = splitspan (nthdayofmonthcontaining n) (applyN (n-1) nextday . nextmonth) s
|
|
|
|
splitSpan (DayOfWeek n) s = splitspan (nthdayofweekcontaining n) (applyN (n-1) nextday . nextweek) s
|
2011-01-14 07:32:08 +03:00
|
|
|
-- splitSpan (WeekOfYear n) s = splitspan startofweek (applyN n nextweek) s
|
|
|
|
-- splitSpan (MonthOfYear n) s = splitspan startofmonth (applyN n nextmonth) s
|
|
|
|
-- splitSpan (QuarterOfYear n) s = splitspan startofquarter (applyN n nextquarter) s
|
|
|
|
|
|
|
|
-- Split the given span using the provided helper functions:
|
|
|
|
-- start is applied to the span's start date to get the first sub-span's start date
|
|
|
|
-- next is applied to a sub-span's start date to get the next sub-span's start date
|
2011-01-14 08:01:00 +03:00
|
|
|
splitspan :: (Day -> Day) -> (Day -> Day) -> DateSpan -> [DateSpan]
|
|
|
|
splitspan _ _ (DateSpan Nothing Nothing) = []
|
|
|
|
splitspan start next (DateSpan Nothing (Just e)) = splitspan start next (DateSpan (Just $ start e) (Just $ next $ start e))
|
|
|
|
splitspan start next (DateSpan (Just s) Nothing) = splitspan start next (DateSpan (Just $ start s) (Just $ next $ start s))
|
|
|
|
splitspan start next span@(DateSpan (Just s) (Just e))
|
2011-01-14 07:32:08 +03:00
|
|
|
| s == e = [span]
|
2011-01-14 08:01:00 +03:00
|
|
|
| otherwise = splitspan' start next span
|
2009-06-05 13:44:20 +04:00
|
|
|
where
|
2011-01-14 08:01:00 +03:00
|
|
|
splitspan' start next (DateSpan (Just s) (Just e))
|
2011-01-14 07:32:08 +03:00
|
|
|
| s >= e = []
|
2011-01-14 08:01:00 +03:00
|
|
|
| otherwise = DateSpan (Just subs) (Just sube) : splitspan' start next (DateSpan (Just sube) (Just e))
|
2011-01-14 07:32:08 +03:00
|
|
|
where subs = start s
|
2011-01-14 08:01:00 +03:00
|
|
|
sube = next subs
|
|
|
|
splitspan' _ _ _ = error' "won't happen, avoids warnings"
|
2009-05-29 14:02:14 +04:00
|
|
|
|
|
|
|
-- | Count the days in a DateSpan, or if it is open-ended return Nothing.
|
|
|
|
daysInSpan :: DateSpan -> Maybe Integer
|
|
|
|
daysInSpan (DateSpan (Just d1) (Just d2)) = Just $ diffDays d2 d1
|
|
|
|
daysInSpan _ = Nothing
|
2010-07-11 22:56:36 +04:00
|
|
|
|
|
|
|
-- | Does the span include the given date ?
|
|
|
|
spanContainsDate :: DateSpan -> Day -> Bool
|
|
|
|
spanContainsDate (DateSpan Nothing Nothing) _ = True
|
|
|
|
spanContainsDate (DateSpan Nothing (Just e)) d = d < e
|
|
|
|
spanContainsDate (DateSpan (Just b) Nothing) d = d >= b
|
|
|
|
spanContainsDate (DateSpan (Just b) (Just e)) d = d >= b && d < e
|
2008-12-04 02:20:38 +03:00
|
|
|
|
2010-07-11 22:56:36 +04:00
|
|
|
-- | Combine two datespans, filling any unspecified dates in the first
|
2013-12-07 02:06:12 +04:00
|
|
|
-- with dates from the second. Not a clip operation, just uses the
|
|
|
|
-- second's start/end dates as defaults when the first does not
|
|
|
|
-- specify them.
|
2010-07-11 22:56:36 +04:00
|
|
|
orDatesFrom (DateSpan a1 b1) (DateSpan a2 b2) = DateSpan a b
|
|
|
|
where a = if isJust a1 then a1 else a2
|
|
|
|
b = if isJust b1 then b1 else b2
|
|
|
|
|
2012-05-19 06:56:26 +04:00
|
|
|
-- | Calculate the intersection of a number of datespans.
|
|
|
|
spansIntersect [] = nulldatespan
|
|
|
|
spansIntersect [d] = d
|
|
|
|
spansIntersect (d:ds) = d `spanIntersect` (spansIntersect ds)
|
|
|
|
|
2011-09-23 04:09:39 +04:00
|
|
|
-- | Calculate the intersection of two datespans.
|
|
|
|
spanIntersect (DateSpan b1 e1) (DateSpan b2 e2) = DateSpan b e
|
|
|
|
where
|
|
|
|
b = latest b1 b2
|
|
|
|
e = earliest e1 e2
|
|
|
|
|
2012-05-19 06:56:26 +04:00
|
|
|
-- | Calculate the union of a number of datespans.
|
|
|
|
spansUnion [] = nulldatespan
|
|
|
|
spansUnion [d] = d
|
|
|
|
spansUnion (d:ds) = d `spanUnion` (spansUnion ds)
|
|
|
|
|
|
|
|
-- | Calculate the union of two datespans.
|
|
|
|
spanUnion (DateSpan b1 e1) (DateSpan b2 e2) = DateSpan b e
|
|
|
|
where
|
|
|
|
b = earliest b1 b2
|
|
|
|
e = latest e1 e2
|
|
|
|
|
2011-09-23 04:09:39 +04:00
|
|
|
latest d Nothing = d
|
|
|
|
latest Nothing d = d
|
|
|
|
latest (Just d1) (Just d2) = Just $ max d1 d2
|
|
|
|
|
|
|
|
earliest d Nothing = d
|
|
|
|
earliest Nothing d = d
|
|
|
|
earliest (Just d1) (Just d2) = Just $ min d1 d2
|
|
|
|
|
2008-12-04 02:20:38 +03:00
|
|
|
-- | Parse a period expression to an Interval and overall DateSpan using
|
2010-08-01 04:15:21 +04:00
|
|
|
-- the provided reference date, or return a parse error.
|
|
|
|
parsePeriodExpr :: Day -> String -> Either ParseError (Interval, DateSpan)
|
|
|
|
parsePeriodExpr refdate = parsewith (periodexpr refdate)
|
|
|
|
|
2011-08-16 02:50:09 +04:00
|
|
|
maybePeriod :: Day -> String -> Maybe (Interval,DateSpan)
|
|
|
|
maybePeriod refdate = either (const Nothing) Just . parsePeriodExpr refdate
|
|
|
|
|
2010-08-01 04:15:21 +04:00
|
|
|
-- | Show a DateSpan as a human-readable pseudo-period-expression string.
|
2012-05-07 00:42:34 +04:00
|
|
|
-- dateSpanAsText :: DateSpan -> String
|
|
|
|
-- dateSpanAsText (DateSpan Nothing Nothing) = "all"
|
|
|
|
-- dateSpanAsText (DateSpan Nothing (Just e)) = printf "to %s" (show e)
|
|
|
|
-- dateSpanAsText (DateSpan (Just b) Nothing) = printf "from %s" (show b)
|
|
|
|
-- dateSpanAsText (DateSpan (Just b) (Just e)) = printf "%s to %s" (show b) (show e)
|
2008-11-27 22:42:03 +03:00
|
|
|
|
2008-12-04 02:20:38 +03:00
|
|
|
-- | Convert a single smart date string to a date span using the provided
|
2010-03-09 20:38:12 +03:00
|
|
|
-- reference date, or raise an error.
|
2012-05-07 00:42:34 +04:00
|
|
|
-- spanFromSmartDateString :: Day -> String -> DateSpan
|
|
|
|
-- spanFromSmartDateString refdate s = spanFromSmartDate refdate sdate
|
|
|
|
-- where
|
|
|
|
-- sdate = fromparse $ parsewith smartdateonly s
|
2008-11-27 22:42:03 +03:00
|
|
|
|
|
|
|
spanFromSmartDate :: Day -> SmartDate -> DateSpan
|
|
|
|
spanFromSmartDate refdate sdate = DateSpan (Just b) (Just e)
|
|
|
|
where
|
2009-06-05 13:44:20 +04:00
|
|
|
(ry,rm,_) = toGregorian refdate
|
2008-11-27 09:29:29 +03:00
|
|
|
(b,e) = span sdate
|
|
|
|
span :: SmartDate -> (Day,Day)
|
|
|
|
span ("","","today") = (refdate, nextday refdate)
|
|
|
|
span ("","this","day") = (refdate, nextday refdate)
|
|
|
|
span ("","","yesterday") = (prevday refdate, refdate)
|
|
|
|
span ("","last","day") = (prevday refdate, refdate)
|
|
|
|
span ("","","tomorrow") = (nextday refdate, addDays 2 refdate)
|
|
|
|
span ("","next","day") = (nextday refdate, addDays 2 refdate)
|
|
|
|
span ("","last","week") = (prevweek refdate, thisweek refdate)
|
|
|
|
span ("","this","week") = (thisweek refdate, nextweek refdate)
|
|
|
|
span ("","next","week") = (nextweek refdate, startofweek $ addDays 14 refdate)
|
|
|
|
span ("","last","month") = (prevmonth refdate, thismonth refdate)
|
|
|
|
span ("","this","month") = (thismonth refdate, nextmonth refdate)
|
|
|
|
span ("","next","month") = (nextmonth refdate, startofmonth $ addGregorianMonthsClip 2 refdate)
|
|
|
|
span ("","last","quarter") = (prevquarter refdate, thisquarter refdate)
|
|
|
|
span ("","this","quarter") = (thisquarter refdate, nextquarter refdate)
|
|
|
|
span ("","next","quarter") = (nextquarter refdate, startofquarter $ addGregorianMonthsClip 6 refdate)
|
|
|
|
span ("","last","year") = (prevyear refdate, thisyear refdate)
|
|
|
|
span ("","this","year") = (thisyear refdate, nextyear refdate)
|
|
|
|
span ("","next","year") = (nextyear refdate, startofyear $ addGregorianYearsClip 2 refdate)
|
|
|
|
span ("","",d) = (day, nextday day) where day = fromGregorian ry rm (read d)
|
|
|
|
span ("",m,"") = (startofmonth day, nextmonth day) where day = fromGregorian ry (read m) 1
|
|
|
|
span ("",m,d) = (day, nextday day) where day = fromGregorian ry (read m) (read d)
|
|
|
|
span (y,"","") = (startofyear day, nextyear day) where day = fromGregorian (read y) 1 1
|
|
|
|
span (y,m,"") = (startofmonth day, nextmonth day) where day = fromGregorian (read y) (read m) 1
|
|
|
|
span (y,m,d) = (day, nextday day) where day = fromGregorian (read y) (read m) (read d)
|
|
|
|
|
2012-05-07 00:42:34 +04:00
|
|
|
-- showDay :: Day -> String
|
|
|
|
-- showDay day = printf "%04d/%02d/%02d" y m d where (y,m,d) = toGregorian day
|
2010-03-09 21:33:26 +03:00
|
|
|
|
2009-12-13 01:19:57 +03:00
|
|
|
-- | Convert a smart date string to an explicit yyyy\/mm\/dd string using
|
2010-03-09 20:38:12 +03:00
|
|
|
-- the provided reference date, or raise an error.
|
2008-11-27 07:01:07 +03:00
|
|
|
fixSmartDateStr :: Day -> String -> String
|
2011-08-15 02:39:48 +04:00
|
|
|
fixSmartDateStr d s = either
|
|
|
|
(\e->error' $ printf "could not parse date %s %s" (show s) (show e))
|
|
|
|
id
|
|
|
|
$ fixSmartDateStrEither d s
|
2010-03-09 21:33:26 +03:00
|
|
|
|
|
|
|
-- | A safe version of fixSmartDateStr.
|
|
|
|
fixSmartDateStrEither :: Day -> String -> Either ParseError String
|
2011-08-15 02:39:48 +04:00
|
|
|
fixSmartDateStrEither d = either Left (Right . showDate) . fixSmartDateStrEither' d
|
|
|
|
|
|
|
|
fixSmartDateStrEither' :: Day -> String -> Either ParseError Day
|
|
|
|
fixSmartDateStrEither' d s = case parsewith smartdateonly (lowercase s) of
|
|
|
|
Right sd -> Right $ fixSmartDate d sd
|
|
|
|
Left e -> Left e
|
2008-11-27 03:35:00 +03:00
|
|
|
|
2008-11-27 09:48:46 +03:00
|
|
|
-- | Convert a SmartDate to an absolute date using the provided reference date.
|
2008-11-27 07:01:07 +03:00
|
|
|
fixSmartDate :: Day -> SmartDate -> Day
|
|
|
|
fixSmartDate refdate sdate = fix sdate
|
2008-11-27 03:35:00 +03:00
|
|
|
where
|
2008-11-27 07:01:07 +03:00
|
|
|
fix :: SmartDate -> Day
|
|
|
|
fix ("","","today") = fromGregorian ry rm rd
|
|
|
|
fix ("","this","day") = fromGregorian ry rm rd
|
|
|
|
fix ("","","yesterday") = prevday refdate
|
|
|
|
fix ("","last","day") = prevday refdate
|
|
|
|
fix ("","","tomorrow") = nextday refdate
|
|
|
|
fix ("","next","day") = nextday refdate
|
|
|
|
fix ("","last","week") = prevweek refdate
|
|
|
|
fix ("","this","week") = thisweek refdate
|
|
|
|
fix ("","next","week") = nextweek refdate
|
|
|
|
fix ("","last","month") = prevmonth refdate
|
|
|
|
fix ("","this","month") = thismonth refdate
|
|
|
|
fix ("","next","month") = nextmonth refdate
|
|
|
|
fix ("","last","quarter") = prevquarter refdate
|
|
|
|
fix ("","this","quarter") = thisquarter refdate
|
|
|
|
fix ("","next","quarter") = nextquarter refdate
|
|
|
|
fix ("","last","year") = prevyear refdate
|
|
|
|
fix ("","this","year") = thisyear refdate
|
|
|
|
fix ("","next","year") = nextyear refdate
|
|
|
|
fix ("","",d) = fromGregorian ry rm (read d)
|
2008-11-27 09:29:29 +03:00
|
|
|
fix ("",m,"") = fromGregorian ry (read m) 1
|
2008-11-27 07:01:07 +03:00
|
|
|
fix ("",m,d) = fromGregorian ry (read m) (read d)
|
2008-11-27 09:29:29 +03:00
|
|
|
fix (y,"","") = fromGregorian (read y) 1 1
|
|
|
|
fix (y,m,"") = fromGregorian (read y) (read m) 1
|
2008-11-27 07:01:07 +03:00
|
|
|
fix (y,m,d) = fromGregorian (read y) (read m) (read d)
|
|
|
|
(ry,rm,rd) = toGregorian refdate
|
2008-11-27 03:35:00 +03:00
|
|
|
|
2008-11-27 05:49:22 +03:00
|
|
|
prevday :: Day -> Day
|
|
|
|
prevday = addDays (-1)
|
|
|
|
nextday = addDays 1
|
2008-12-04 02:20:38 +03:00
|
|
|
startofday = id
|
2008-11-27 04:49:13 +03:00
|
|
|
|
2008-11-27 05:49:22 +03:00
|
|
|
thisweek = startofweek
|
|
|
|
prevweek = startofweek . addDays (-7)
|
|
|
|
nextweek = startofweek . addDays 7
|
|
|
|
startofweek day = fromMondayStartWeek y w 1
|
2008-11-27 04:49:13 +03:00
|
|
|
where
|
2008-11-27 05:49:22 +03:00
|
|
|
(y,_,_) = toGregorian day
|
2008-11-27 04:49:13 +03:00
|
|
|
(w,_) = mondayStartWeek day
|
|
|
|
|
2008-11-27 05:49:22 +03:00
|
|
|
thismonth = startofmonth
|
|
|
|
prevmonth = startofmonth . addGregorianMonthsClip (-1)
|
|
|
|
nextmonth = startofmonth . addGregorianMonthsClip 1
|
|
|
|
startofmonth day = fromGregorian y m 1 where (y,m,_) = toGregorian day
|
|
|
|
|
|
|
|
thisquarter = startofquarter
|
|
|
|
prevquarter = startofquarter . addGregorianMonthsClip (-3)
|
|
|
|
nextquarter = startofquarter . addGregorianMonthsClip 3
|
|
|
|
startofquarter day = fromGregorian y (firstmonthofquarter m) 1
|
|
|
|
where
|
|
|
|
(y,m,_) = toGregorian day
|
|
|
|
firstmonthofquarter m = ((m-1) `div` 3) * 3 + 1
|
|
|
|
|
|
|
|
thisyear = startofyear
|
|
|
|
prevyear = startofyear . addGregorianYearsClip (-1)
|
|
|
|
nextyear = startofyear . addGregorianYearsClip 1
|
|
|
|
startofyear day = fromGregorian y 1 1 where (y,_,_) = toGregorian day
|
2008-11-27 03:35:00 +03:00
|
|
|
|
2011-01-14 07:32:08 +03:00
|
|
|
nthdayofmonthcontaining n d | d1 >= d = d1
|
|
|
|
| otherwise = d2
|
|
|
|
where d1 = addDays (fromIntegral n-1) s
|
|
|
|
d2 = addDays (fromIntegral n-1) $ nextmonth s
|
|
|
|
s = startofmonth d
|
|
|
|
|
|
|
|
nthdayofweekcontaining n d | d1 >= d = d1
|
|
|
|
| otherwise = d2
|
|
|
|
where d1 = addDays (fromIntegral n-1) s
|
|
|
|
d2 = addDays (fromIntegral n-1) $ nextweek s
|
|
|
|
s = startofweek d
|
|
|
|
|
2008-11-27 03:35:00 +03:00
|
|
|
----------------------------------------------------------------------
|
|
|
|
-- parsing
|
|
|
|
|
2012-05-07 00:42:34 +04:00
|
|
|
-- -- | Parse a couple of date-time string formats to a time type.
|
|
|
|
-- parsedatetimeM :: String -> Maybe LocalTime
|
|
|
|
-- parsedatetimeM s = firstJust [
|
|
|
|
-- parseTime defaultTimeLocale "%Y/%m/%d %H:%M:%S" s,
|
|
|
|
-- parseTime defaultTimeLocale "%Y-%m-%d %H:%M:%S" s
|
|
|
|
-- ]
|
2009-01-11 09:58:35 +03:00
|
|
|
|
2010-03-09 20:38:12 +03:00
|
|
|
-- | Parse a couple of date string formats to a time type.
|
2009-01-11 09:58:35 +03:00
|
|
|
parsedateM :: String -> Maybe Day
|
|
|
|
parsedateM s = firstJust [
|
|
|
|
parseTime defaultTimeLocale "%Y/%m/%d" s,
|
|
|
|
parseTime defaultTimeLocale "%Y-%m-%d" s
|
|
|
|
]
|
2008-11-11 15:34:05 +03:00
|
|
|
|
2012-05-07 00:42:34 +04:00
|
|
|
-- -- | Parse a date-time string to a time type, or raise an error.
|
|
|
|
-- parsedatetime :: String -> LocalTime
|
|
|
|
-- parsedatetime s = fromMaybe (error' $ "could not parse timestamp \"" ++ s ++ "\"")
|
|
|
|
-- (parsedatetimeM s)
|
2010-03-09 20:38:12 +03:00
|
|
|
|
2008-11-11 15:34:05 +03:00
|
|
|
-- | Parse a date string to a time type, or raise an error.
|
2008-11-27 07:01:07 +03:00
|
|
|
parsedate :: String -> Day
|
2010-09-05 22:18:50 +04:00
|
|
|
parsedate s = fromMaybe (error' $ "could not parse date \"" ++ s ++ "\"")
|
2009-01-11 09:58:35 +03:00
|
|
|
(parsedateM s)
|
2008-11-11 15:34:05 +03:00
|
|
|
|
|
|
|
-- | Parse a time string to a time type using the provided pattern, or
|
|
|
|
-- return the default.
|
|
|
|
parsetimewith :: ParseTime t => String -> String -> t -> t
|
|
|
|
parsetimewith pat s def = fromMaybe def $ parseTime defaultTimeLocale pat s
|
|
|
|
|
2008-11-27 03:35:00 +03:00
|
|
|
{-|
|
|
|
|
Parse a date in any of the formats allowed in ledger's period expressions,
|
|
|
|
and maybe some others:
|
|
|
|
|
|
|
|
> 2004
|
|
|
|
> 2004/10
|
|
|
|
> 2004/10/1
|
|
|
|
> 10/1
|
|
|
|
> 21
|
|
|
|
> october, oct
|
|
|
|
> yesterday, today, tomorrow
|
2010-11-19 20:25:39 +03:00
|
|
|
> this/next/last week/day/month/quarter/year
|
2008-11-27 03:35:00 +03:00
|
|
|
|
|
|
|
Returns a SmartDate, to be converted to a full date later (see fixSmartDate).
|
|
|
|
Assumes any text in the parse stream has been lowercased.
|
|
|
|
-}
|
2008-12-08 04:11:07 +03:00
|
|
|
smartdate :: GenParser Char st SmartDate
|
2008-11-27 03:35:00 +03:00
|
|
|
smartdate = do
|
2010-09-04 03:22:58 +04:00
|
|
|
-- XXX maybe obscures date errors ? see ledgerdate
|
2010-04-15 01:37:03 +04:00
|
|
|
(y,m,d) <- choice' [yyyymmdd, ymd, ym, md, y, d, month, mon, today, yesterday, tomorrow, lastthisnextthing]
|
2009-09-22 15:55:11 +04:00
|
|
|
return (y,m,d)
|
2008-11-27 03:35:00 +03:00
|
|
|
|
2010-03-10 02:11:12 +03:00
|
|
|
-- | Like smartdate, but there must be nothing other than whitespace after the date.
|
|
|
|
smartdateonly :: GenParser Char st SmartDate
|
|
|
|
smartdateonly = do
|
|
|
|
d <- smartdate
|
|
|
|
many spacenonewline
|
|
|
|
eof
|
|
|
|
return d
|
|
|
|
|
2010-09-04 03:22:58 +04:00
|
|
|
datesepchars = "/-."
|
|
|
|
datesepchar = oneOf datesepchars
|
2008-11-27 03:35:00 +03:00
|
|
|
|
2010-04-15 01:49:34 +04:00
|
|
|
validYear, validMonth, validDay :: String -> Bool
|
|
|
|
validYear s = length s >= 4 && isJust (readMay s :: Maybe Int)
|
|
|
|
validMonth s = maybe False (\n -> n>=1 && n<=12) $ readMay s
|
|
|
|
validDay s = maybe False (\n -> n>=1 && n<=31) $ readMay s
|
|
|
|
|
2010-09-05 20:05:38 +04:00
|
|
|
failIfInvalidYear, failIfInvalidMonth, failIfInvalidDay :: (Monad m) => String -> m ()
|
2010-04-15 01:49:34 +04:00
|
|
|
failIfInvalidYear s = unless (validYear s) $ fail $ "bad year number: " ++ s
|
|
|
|
failIfInvalidMonth s = unless (validMonth s) $ fail $ "bad month number: " ++ s
|
|
|
|
failIfInvalidDay s = unless (validDay s) $ fail $ "bad day number: " ++ s
|
|
|
|
|
2009-01-17 23:07:24 +03:00
|
|
|
yyyymmdd :: GenParser Char st SmartDate
|
|
|
|
yyyymmdd = do
|
|
|
|
y <- count 4 digit
|
|
|
|
m <- count 2 digit
|
2010-04-15 01:49:34 +04:00
|
|
|
failIfInvalidMonth m
|
2009-01-17 23:07:24 +03:00
|
|
|
d <- count 2 digit
|
2010-04-15 01:49:34 +04:00
|
|
|
failIfInvalidDay d
|
2009-01-17 23:07:24 +03:00
|
|
|
return (y,m,d)
|
|
|
|
|
2008-12-08 04:11:07 +03:00
|
|
|
ymd :: GenParser Char st SmartDate
|
2008-11-27 03:35:00 +03:00
|
|
|
ymd = do
|
|
|
|
y <- many1 digit
|
2010-04-15 01:49:34 +04:00
|
|
|
failIfInvalidYear y
|
2008-11-27 03:35:00 +03:00
|
|
|
datesepchar
|
2010-04-15 01:49:34 +04:00
|
|
|
m <- many1 digit
|
|
|
|
failIfInvalidMonth m
|
2008-11-27 03:35:00 +03:00
|
|
|
datesepchar
|
2010-04-15 01:49:34 +04:00
|
|
|
d <- many1 digit
|
|
|
|
failIfInvalidDay d
|
2009-11-26 00:21:49 +03:00
|
|
|
return $ (y,m,d)
|
2008-11-27 03:35:00 +03:00
|
|
|
|
2008-12-08 04:11:07 +03:00
|
|
|
ym :: GenParser Char st SmartDate
|
2008-11-27 03:35:00 +03:00
|
|
|
ym = do
|
|
|
|
y <- many1 digit
|
2010-04-15 01:49:34 +04:00
|
|
|
failIfInvalidYear y
|
2008-11-27 03:35:00 +03:00
|
|
|
datesepchar
|
2010-04-15 01:49:34 +04:00
|
|
|
m <- many1 digit
|
|
|
|
failIfInvalidMonth m
|
2008-11-27 09:29:29 +03:00
|
|
|
return (y,m,"")
|
2008-11-27 03:35:00 +03:00
|
|
|
|
2008-12-08 04:11:07 +03:00
|
|
|
y :: GenParser Char st SmartDate
|
2008-11-27 03:35:00 +03:00
|
|
|
y = do
|
|
|
|
y <- many1 digit
|
2010-04-15 01:49:34 +04:00
|
|
|
failIfInvalidYear y
|
2008-11-27 09:29:29 +03:00
|
|
|
return (y,"","")
|
2008-11-27 03:35:00 +03:00
|
|
|
|
2008-12-08 04:11:07 +03:00
|
|
|
d :: GenParser Char st SmartDate
|
2008-11-27 03:35:00 +03:00
|
|
|
d = do
|
|
|
|
d <- many1 digit
|
2010-04-15 01:49:34 +04:00
|
|
|
failIfInvalidDay d
|
2008-11-27 03:35:00 +03:00
|
|
|
return ("","",d)
|
|
|
|
|
2008-12-08 04:11:07 +03:00
|
|
|
md :: GenParser Char st SmartDate
|
2008-11-27 03:35:00 +03:00
|
|
|
md = do
|
2010-04-15 01:49:34 +04:00
|
|
|
m <- many1 digit
|
|
|
|
failIfInvalidMonth m
|
2008-11-27 03:35:00 +03:00
|
|
|
datesepchar
|
2010-04-15 01:49:34 +04:00
|
|
|
d <- many1 digit
|
|
|
|
failIfInvalidDay d
|
2008-11-27 03:35:00 +03:00
|
|
|
return ("",m,d)
|
|
|
|
|
2009-01-17 23:21:44 +03:00
|
|
|
months = ["january","february","march","april","may","june",
|
|
|
|
"july","august","september","october","november","december"]
|
|
|
|
monthabbrevs = ["jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"]
|
2012-05-07 00:42:34 +04:00
|
|
|
-- weekdays = ["monday","tuesday","wednesday","thursday","friday","saturday","sunday"]
|
|
|
|
-- weekdayabbrevs = ["mon","tue","wed","thu","fri","sat","sun"]
|
2008-11-27 09:29:29 +03:00
|
|
|
|
2009-09-22 20:51:27 +04:00
|
|
|
monthIndex s = maybe 0 (+1) $ lowercase s `elemIndex` months
|
|
|
|
monIndex s = maybe 0 (+1) $ lowercase s `elemIndex` monthabbrevs
|
2008-11-27 03:35:00 +03:00
|
|
|
|
2008-12-08 04:11:07 +03:00
|
|
|
month :: GenParser Char st SmartDate
|
2008-11-27 03:35:00 +03:00
|
|
|
month = do
|
2008-11-27 09:29:29 +03:00
|
|
|
m <- choice $ map (try . string) months
|
|
|
|
let i = monthIndex m
|
2009-09-22 15:55:11 +04:00
|
|
|
return ("",show i,"")
|
2008-11-27 03:35:00 +03:00
|
|
|
|
2008-12-08 04:11:07 +03:00
|
|
|
mon :: GenParser Char st SmartDate
|
2008-11-27 03:35:00 +03:00
|
|
|
mon = do
|
2009-01-17 23:21:44 +03:00
|
|
|
m <- choice $ map (try . string) monthabbrevs
|
2008-11-27 09:29:29 +03:00
|
|
|
let i = monIndex m
|
|
|
|
return ("",show i,"")
|
2008-11-27 03:35:00 +03:00
|
|
|
|
2009-01-24 22:48:37 +03:00
|
|
|
today,yesterday,tomorrow :: GenParser Char st SmartDate
|
|
|
|
today = string "today" >> return ("","","today")
|
2008-11-27 03:35:00 +03:00
|
|
|
yesterday = string "yesterday" >> return ("","","yesterday")
|
|
|
|
tomorrow = string "tomorrow" >> return ("","","tomorrow")
|
|
|
|
|
2008-12-08 04:11:07 +03:00
|
|
|
lastthisnextthing :: GenParser Char st SmartDate
|
2008-11-27 03:35:00 +03:00
|
|
|
lastthisnextthing = do
|
|
|
|
r <- choice [
|
|
|
|
string "last"
|
|
|
|
,string "this"
|
|
|
|
,string "next"
|
|
|
|
]
|
2009-01-17 23:21:44 +03:00
|
|
|
many spacenonewline -- make the space optional for easier scripting
|
2009-09-22 15:55:11 +04:00
|
|
|
p <- choice [
|
2008-11-27 03:35:00 +03:00
|
|
|
string "day"
|
|
|
|
,string "week"
|
|
|
|
,string "month"
|
|
|
|
,string "quarter"
|
|
|
|
,string "year"
|
|
|
|
]
|
2009-01-17 23:21:44 +03:00
|
|
|
-- XXX support these in fixSmartDate
|
|
|
|
-- ++ (map string $ months ++ monthabbrevs ++ weekdays ++ weekdayabbrevs)
|
|
|
|
|
2008-11-27 03:35:00 +03:00
|
|
|
return ("",r,p)
|
2008-11-22 15:18:19 +03:00
|
|
|
|
2008-12-08 04:11:07 +03:00
|
|
|
periodexpr :: Day -> GenParser Char st (Interval, DateSpan)
|
2008-12-04 02:20:38 +03:00
|
|
|
periodexpr rdate = choice $ map try [
|
|
|
|
intervalanddateperiodexpr rdate,
|
|
|
|
intervalperiodexpr,
|
|
|
|
dateperiodexpr rdate,
|
2009-09-22 15:55:11 +04:00
|
|
|
(return (NoInterval,DateSpan Nothing Nothing))
|
2008-12-04 02:20:38 +03:00
|
|
|
]
|
2008-11-27 22:42:03 +03:00
|
|
|
|
2008-12-08 04:11:07 +03:00
|
|
|
intervalanddateperiodexpr :: Day -> GenParser Char st (Interval, DateSpan)
|
2008-12-04 02:20:38 +03:00
|
|
|
intervalanddateperiodexpr rdate = do
|
2008-11-27 22:42:03 +03:00
|
|
|
many spacenonewline
|
2011-01-14 07:32:08 +03:00
|
|
|
i <- reportinginterval
|
2008-11-27 22:42:03 +03:00
|
|
|
many spacenonewline
|
2008-12-04 02:20:38 +03:00
|
|
|
s <- periodexprdatespan rdate
|
|
|
|
return (i,s)
|
|
|
|
|
2008-12-08 04:11:07 +03:00
|
|
|
intervalperiodexpr :: GenParser Char st (Interval, DateSpan)
|
2008-12-04 02:20:38 +03:00
|
|
|
intervalperiodexpr = do
|
|
|
|
many spacenonewline
|
2011-01-14 07:32:08 +03:00
|
|
|
i <- reportinginterval
|
2008-12-04 02:20:38 +03:00
|
|
|
return (i, DateSpan Nothing Nothing)
|
|
|
|
|
2008-12-08 04:11:07 +03:00
|
|
|
dateperiodexpr :: Day -> GenParser Char st (Interval, DateSpan)
|
2008-12-04 02:20:38 +03:00
|
|
|
dateperiodexpr rdate = do
|
2008-11-27 22:42:03 +03:00
|
|
|
many spacenonewline
|
2008-12-04 02:20:38 +03:00
|
|
|
s <- periodexprdatespan rdate
|
|
|
|
return (NoInterval, s)
|
|
|
|
|
2011-01-14 07:32:08 +03:00
|
|
|
-- Parse a reporting interval.
|
|
|
|
reportinginterval :: GenParser Char st Interval
|
|
|
|
reportinginterval = choice' [
|
2011-01-14 05:35:00 +03:00
|
|
|
tryinterval "day" "daily" Days,
|
|
|
|
tryinterval "week" "weekly" Weeks,
|
|
|
|
tryinterval "month" "monthly" Months,
|
|
|
|
tryinterval "quarter" "quarterly" Quarters,
|
|
|
|
tryinterval "year" "yearly" Years,
|
|
|
|
do string "biweekly"
|
|
|
|
return $ Weeks 2,
|
|
|
|
do string "bimonthly"
|
2011-01-14 07:32:08 +03:00
|
|
|
return $ Months 2,
|
|
|
|
do string "every"
|
|
|
|
many spacenonewline
|
|
|
|
n <- fmap read $ many1 digit
|
|
|
|
thsuffix
|
|
|
|
many spacenonewline
|
|
|
|
string "day"
|
|
|
|
many spacenonewline
|
|
|
|
string "of"
|
|
|
|
many spacenonewline
|
|
|
|
string "week"
|
|
|
|
return $ DayOfWeek n,
|
|
|
|
do string "every"
|
|
|
|
many spacenonewline
|
|
|
|
n <- fmap read $ many1 digit
|
|
|
|
thsuffix
|
|
|
|
many spacenonewline
|
|
|
|
string "day"
|
|
|
|
optional $ do
|
|
|
|
many spacenonewline
|
|
|
|
string "of"
|
|
|
|
many spacenonewline
|
|
|
|
string "month"
|
|
|
|
return $ DayOfMonth n
|
|
|
|
]
|
2008-12-04 02:20:38 +03:00
|
|
|
where
|
2011-01-14 07:32:08 +03:00
|
|
|
|
|
|
|
thsuffix = choice' $ map string ["st","nd","rd","th"]
|
|
|
|
|
|
|
|
-- Parse any of several variants of a basic interval, eg "daily", "every day", "every N days".
|
2011-01-14 05:35:00 +03:00
|
|
|
tryinterval :: String -> String -> (Int -> Interval) -> GenParser Char st Interval
|
|
|
|
tryinterval singular compact intcons =
|
|
|
|
choice' [
|
|
|
|
do string compact
|
|
|
|
return $ intcons 1,
|
|
|
|
do string "every"
|
|
|
|
many spacenonewline
|
|
|
|
string singular
|
|
|
|
return $ intcons 1,
|
|
|
|
do string "every"
|
|
|
|
many spacenonewline
|
|
|
|
n <- fmap read $ many1 digit
|
|
|
|
many spacenonewline
|
|
|
|
string plural
|
|
|
|
return $ intcons n
|
|
|
|
]
|
|
|
|
where plural = singular ++ "s"
|
2008-12-04 02:20:38 +03:00
|
|
|
|
2008-12-08 04:11:07 +03:00
|
|
|
periodexprdatespan :: Day -> GenParser Char st DateSpan
|
2008-12-04 02:20:38 +03:00
|
|
|
periodexprdatespan rdate = choice $ map try [
|
|
|
|
doubledatespan rdate,
|
|
|
|
fromdatespan rdate,
|
|
|
|
todatespan rdate,
|
|
|
|
justdatespan rdate
|
|
|
|
]
|
|
|
|
|
2008-12-08 04:11:07 +03:00
|
|
|
doubledatespan :: Day -> GenParser Char st DateSpan
|
2008-12-04 02:20:38 +03:00
|
|
|
doubledatespan rdate = do
|
|
|
|
optional (string "from" >> many spacenonewline)
|
|
|
|
b <- smartdate
|
|
|
|
many spacenonewline
|
2013-09-27 02:06:48 +04:00
|
|
|
optional (choice [string "to", string "-"] >> many spacenonewline)
|
2008-11-27 22:42:03 +03:00
|
|
|
e <- smartdate
|
2008-12-04 02:20:38 +03:00
|
|
|
return $ DateSpan (Just $ fixSmartDate rdate b) (Just $ fixSmartDate rdate e)
|
2008-11-27 22:42:03 +03:00
|
|
|
|
2008-12-08 04:11:07 +03:00
|
|
|
fromdatespan :: Day -> GenParser Char st DateSpan
|
2008-12-04 02:20:38 +03:00
|
|
|
fromdatespan rdate = do
|
2013-09-27 02:06:48 +04:00
|
|
|
b <- choice [
|
|
|
|
do
|
|
|
|
string "from" >> many spacenonewline
|
|
|
|
smartdate
|
|
|
|
,
|
|
|
|
do
|
|
|
|
d <- smartdate
|
|
|
|
string "-"
|
|
|
|
return d
|
|
|
|
]
|
2008-12-04 02:20:38 +03:00
|
|
|
return $ DateSpan (Just $ fixSmartDate rdate b) Nothing
|
|
|
|
|
2008-12-08 04:11:07 +03:00
|
|
|
todatespan :: Day -> GenParser Char st DateSpan
|
2008-12-04 02:20:38 +03:00
|
|
|
todatespan rdate = do
|
2013-09-27 02:06:48 +04:00
|
|
|
choice [string "to", string "-"] >> many spacenonewline
|
2008-12-04 02:20:38 +03:00
|
|
|
e <- smartdate
|
|
|
|
return $ DateSpan Nothing (Just $ fixSmartDate rdate e)
|
2008-11-27 22:42:03 +03:00
|
|
|
|
2008-12-08 04:11:07 +03:00
|
|
|
justdatespan :: Day -> GenParser Char st DateSpan
|
2008-12-04 02:20:38 +03:00
|
|
|
justdatespan rdate = do
|
|
|
|
optional (string "in" >> many spacenonewline)
|
|
|
|
d <- smartdate
|
|
|
|
return $ spanFromSmartDate rdate d
|
2008-12-04 22:29:29 +03:00
|
|
|
|
2011-07-17 19:54:58 +04:00
|
|
|
-- | Make a datespan from two valid date strings parseable by parsedate
|
2011-07-18 03:14:51 +04:00
|
|
|
-- (or raise an error). Eg: mkdatespan \"2011/1/1\" \"2011/12/31\".
|
2010-03-09 04:43:25 +03:00
|
|
|
mkdatespan :: String -> String -> DateSpan
|
|
|
|
mkdatespan b = DateSpan (Just $ parsedate b) . Just . parsedate
|
|
|
|
|
2011-06-14 23:10:16 +04:00
|
|
|
nulldatespan :: DateSpan
|
2008-12-04 22:29:29 +03:00
|
|
|
nulldatespan = DateSpan Nothing Nothing
|
|
|
|
|
2011-06-14 23:10:16 +04:00
|
|
|
nulldate :: Day
|
|
|
|
nulldate = parsedate "0000/00/00"
|
2010-03-09 04:43:25 +03:00
|
|
|
|
2010-12-27 23:26:22 +03:00
|
|
|
tests_Hledger_Data_Dates = TestList
|
|
|
|
[
|
2009-12-19 08:57:54 +03:00
|
|
|
|
2011-01-14 07:32:08 +03:00
|
|
|
"parsedate" ~: do
|
|
|
|
let date1 = parsedate "2008/11/26"
|
|
|
|
parsedate "2008/02/03" `is` parsetimewith "%Y/%m/%d" "2008/02/03" date1
|
|
|
|
parsedate "2008-02-03" `is` parsetimewith "%Y/%m/%d" "2008/02/03" date1
|
|
|
|
|
|
|
|
,"period expressions" ~: do
|
|
|
|
let todaysdate = parsedate "2008/11/26"
|
|
|
|
let str `gives` result = show (parsewith (periodexpr todaysdate) str) `is` ("Right " ++ result)
|
|
|
|
"from aug to oct" `gives` "(NoInterval,DateSpan (Just 2008-08-01) (Just 2008-10-01))"
|
|
|
|
"aug to oct" `gives` "(NoInterval,DateSpan (Just 2008-08-01) (Just 2008-10-01))"
|
|
|
|
"every 3 days in aug" `gives` "(Days 3,DateSpan (Just 2008-08-01) (Just 2008-09-01))"
|
|
|
|
"daily from aug" `gives` "(Days 1,DateSpan (Just 2008-08-01) Nothing)"
|
|
|
|
"every week to 2009" `gives` "(Weeks 1,DateSpan Nothing (Just 2009-01-01))"
|
|
|
|
|
|
|
|
,"splitSpan" ~: do
|
2010-03-09 04:43:25 +03:00
|
|
|
let gives (interval, span) = (splitSpan interval span `is`)
|
|
|
|
(NoInterval,mkdatespan "2008/01/01" "2009/01/01") `gives`
|
|
|
|
[mkdatespan "2008/01/01" "2009/01/01"]
|
2011-01-14 05:35:00 +03:00
|
|
|
(Quarters 1,mkdatespan "2008/01/01" "2009/01/01") `gives`
|
2010-03-09 04:43:25 +03:00
|
|
|
[mkdatespan "2008/01/01" "2008/04/01"
|
|
|
|
,mkdatespan "2008/04/01" "2008/07/01"
|
|
|
|
,mkdatespan "2008/07/01" "2008/10/01"
|
|
|
|
,mkdatespan "2008/10/01" "2009/01/01"
|
|
|
|
]
|
2011-01-14 05:35:00 +03:00
|
|
|
(Quarters 1,nulldatespan) `gives`
|
2010-03-09 04:43:25 +03:00
|
|
|
[nulldatespan]
|
2011-01-14 05:35:00 +03:00
|
|
|
(Days 1,mkdatespan "2008/01/01" "2008/01/01") `gives`
|
2010-03-09 04:43:25 +03:00
|
|
|
[mkdatespan "2008/01/01" "2008/01/01"]
|
2011-01-14 05:35:00 +03:00
|
|
|
(Quarters 1,mkdatespan "2008/01/01" "2008/01/01") `gives`
|
2010-03-09 04:43:25 +03:00
|
|
|
[mkdatespan "2008/01/01" "2008/01/01"]
|
2011-01-14 05:35:00 +03:00
|
|
|
(Months 1,mkdatespan "2008/01/01" "2008/04/01") `gives`
|
2011-01-14 04:22:53 +03:00
|
|
|
[mkdatespan "2008/01/01" "2008/02/01"
|
|
|
|
,mkdatespan "2008/02/01" "2008/03/01"
|
|
|
|
,mkdatespan "2008/03/01" "2008/04/01"
|
|
|
|
]
|
2011-01-14 05:35:00 +03:00
|
|
|
(Months 2,mkdatespan "2008/01/01" "2008/04/01") `gives`
|
2011-01-14 04:22:53 +03:00
|
|
|
[mkdatespan "2008/01/01" "2008/03/01"
|
|
|
|
,mkdatespan "2008/03/01" "2008/05/01"
|
|
|
|
]
|
2011-01-14 05:35:00 +03:00
|
|
|
(Weeks 1,mkdatespan "2008/01/01" "2008/01/15") `gives`
|
2011-01-14 04:22:53 +03:00
|
|
|
[mkdatespan "2007/12/31" "2008/01/07"
|
|
|
|
,mkdatespan "2008/01/07" "2008/01/14"
|
|
|
|
,mkdatespan "2008/01/14" "2008/01/21"
|
|
|
|
]
|
2011-01-14 05:35:00 +03:00
|
|
|
(Weeks 2,mkdatespan "2008/01/01" "2008/01/15") `gives`
|
2011-01-14 04:22:53 +03:00
|
|
|
[mkdatespan "2007/12/31" "2008/01/14"
|
|
|
|
,mkdatespan "2008/01/14" "2008/01/28"
|
|
|
|
]
|
2011-01-14 07:32:08 +03:00
|
|
|
(DayOfMonth 2,mkdatespan "2008/01/01" "2008/04/01") `gives`
|
2011-01-14 08:01:00 +03:00
|
|
|
[mkdatespan "2008/01/02" "2008/02/02"
|
|
|
|
,mkdatespan "2008/02/02" "2008/03/02"
|
|
|
|
,mkdatespan "2008/03/02" "2008/04/02"
|
2011-01-14 07:32:08 +03:00
|
|
|
]
|
|
|
|
(DayOfWeek 2,mkdatespan "2011/01/01" "2011/01/15") `gives`
|
2011-01-14 08:01:00 +03:00
|
|
|
[mkdatespan "2011/01/04" "2011/01/11"
|
|
|
|
,mkdatespan "2011/01/11" "2011/01/18"
|
2011-01-14 07:32:08 +03:00
|
|
|
]
|
2010-12-27 23:26:22 +03:00
|
|
|
|
|
|
|
,"fixSmartDateStr" ~: do
|
|
|
|
let gives = is . fixSmartDateStr (parsedate "2008/11/26")
|
2014-03-22 14:31:13 +04:00
|
|
|
"0000-01-01" `gives` "0000/01/01"
|
2010-12-27 23:26:22 +03:00
|
|
|
"1999-12-02" `gives` "1999/12/02"
|
|
|
|
"1999.12.02" `gives` "1999/12/02"
|
|
|
|
"1999/3/2" `gives` "1999/03/02"
|
|
|
|
"19990302" `gives` "1999/03/02"
|
|
|
|
"2008/2" `gives` "2008/02/01"
|
|
|
|
"0020/2" `gives` "0020/02/01"
|
|
|
|
"1000" `gives` "1000/01/01"
|
|
|
|
"4/2" `gives` "2008/04/02"
|
|
|
|
"2" `gives` "2008/11/02"
|
|
|
|
"January" `gives` "2008/01/01"
|
|
|
|
"feb" `gives` "2008/02/01"
|
|
|
|
"today" `gives` "2008/11/26"
|
|
|
|
"yesterday" `gives` "2008/11/25"
|
|
|
|
"tomorrow" `gives` "2008/11/27"
|
|
|
|
"this day" `gives` "2008/11/26"
|
|
|
|
"last day" `gives` "2008/11/25"
|
|
|
|
"next day" `gives` "2008/11/27"
|
|
|
|
"this week" `gives` "2008/11/24" -- last monday
|
|
|
|
"last week" `gives` "2008/11/17" -- previous monday
|
|
|
|
"next week" `gives` "2008/12/01" -- next monday
|
|
|
|
"this month" `gives` "2008/11/01"
|
|
|
|
"last month" `gives` "2008/10/01"
|
|
|
|
"next month" `gives` "2008/12/01"
|
|
|
|
"this quarter" `gives` "2008/10/01"
|
|
|
|
"last quarter" `gives` "2008/07/01"
|
|
|
|
"next quarter" `gives` "2009/01/01"
|
|
|
|
"this year" `gives` "2008/01/01"
|
|
|
|
"last year" `gives` "2007/01/01"
|
|
|
|
"next year" `gives` "2009/01/01"
|
|
|
|
-- "last wed" `gives` "2008/11/19"
|
|
|
|
-- "next friday" `gives` "2008/11/28"
|
|
|
|
-- "next january" `gives` "2009/01/01"
|
|
|
|
|
|
|
|
]
|