Skip to content

Commit

Permalink
Add Duration DeltaRecord zeroDelta and diff. Bump version to 7.0.0
Browse files Browse the repository at this point in the history
feat(Duration): add diff DeltaRecord zeroDelta
test(Duration): add for diff
style(Period): change indentifier name
  • Loading branch information
rluiten committed Jun 24, 2016
1 parent 1ad67e0 commit 85d9570
Show file tree
Hide file tree
Showing 5 changed files with 282 additions and 31 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
### Introduction
A collection of modules for working with dates and times.

Only major changes are listed here.
* 2016/04/3 4.0.0
* Changed the name space from `Date.` to `Date.Extra.` for all modules.
* 2016/05/13 5.0.1
Expand Down Expand Up @@ -106,6 +107,7 @@ In the long run this may require writing a date parser and introducing Elm nativ
## People Using this library.

* Currently Robin on a new far from finished project. Only put this here because this section would be empty with out it.
* Feel free to contact me to let me know you are using this library.

## Things to think abut for future development, not really a road map.

Expand Down
2 changes: 1 addition & 1 deletion elm-package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "6.1.0",
"version": "7.0.0",
"summary": "Date Extra library add/subtract/diff/format etc dates.",
"repository": "https://github.com/rluiten/elm-date-extra.git",
"license": "BSD3",
Expand Down
189 changes: 176 additions & 13 deletions src/Date/Extra/Duration.elm
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
module Date.Extra.Duration exposing
( add
, Duration (..)
, DeltaRecord
, zeroDelta
, diff
)

{-| A Duration is a length of time that may vary with with calendar date
Expand All @@ -19,15 +22,26 @@ Note adding or subtracting 24 * Hour units from a date may produce a
different answer to adding or subtracting a Day if day light saving
transitions occur as part of the date change.
**Warning**
Be careful if you add Duration Delta to a Date as Duration contains months
and Years which are not fixed elapsed times like Period Delta, however if
you really need a relative number of months or years then it may meet
your needs.
@docs add
@docs Duration
@docs DeltaRecord
@docs zeroDelta
@docs diff
Copyright (c) 2016 Robin Luiten
-}

import Date exposing (Date, Month)

-- import Date.Extra.Calendar as Calendar
import Date.Extra.Compare as Compare
import Date.Extra.Core as Core
import Date.Extra.Create as Create
import Date.Extra.Format as Format
Expand All @@ -49,9 +63,37 @@ type Duration
| Week
| Month
| Year
-- | Combo {year,month,week,day,hour,min,sec,millisecond}
-- DateDiff could return a Duration with fields set
| Delta DeltaRecord

{-| A multi granularity duration delta.
This does not contain week like Period.DeltaRecord.
It does contain month and year.
-}
type alias DeltaRecord =
{ year : Int
, month : Int
, day : Int
, hour : Int
, minute : Int
, second : Int
, millisecond : Int
}


{-| All zero delta.
Useful as a starting point if you want to set a few fields only.
-}
zeroDelta : DeltaRecord
zeroDelta =
{ year = 0
, month = 0
, day = 0
, hour = 0
, minute = 0
, second = 0
, millisecond = 0
}

{- Return true if this Duration unit compensates for crossing daylight saving
boundaries.
Expand All @@ -67,6 +109,8 @@ requireDaylightCompensateInAdd duration =
Week -> True
Month -> True
Year -> True
-- If day,month,year is non zero in Delta then compensate.
Delta delta -> delta.day /= 0 || delta.month /= 0 || delta.year /= 0


{-| Add duration * count to date. -}
Expand All @@ -83,16 +127,30 @@ add duration addend date =


doAdd : Duration -> Int -> Date -> Date
doAdd duration =
doAdd duration addend date =
case duration of
Millisecond -> Period.add Period.Millisecond
Second -> Period.add Period.Second
Minute -> Period.add Period.Minute
Hour -> Period.add Period.Hour
Day -> Period.add Period.Day
Week -> Period.add Period.Week
Month -> addMonth
Year -> addYear
Millisecond -> Period.add Period.Millisecond addend date
Second -> Period.add Period.Second addend date
Minute -> Period.add Period.Minute addend date
Hour -> Period.add Period.Hour addend date
Day -> Period.add Period.Day addend date
Week -> Period.add Period.Week addend date
Month -> addMonth addend date
Year -> addYear addend date
Delta delta ->
doAdd Year delta.year date
|> doAdd Month delta.month
|> Period.add
( Period.Delta
{ week = 0
, day = delta.day
, hour = delta.hour
, minute = delta.minute
, second = delta.second
, millisecond = delta.millisecond
}
)
addend


daylightOffsetCompensate : Date -> Date -> Date
Expand Down Expand Up @@ -165,10 +223,115 @@ addMonth monthCount date =
Period.add Period.Day daysDifferent date


-- todo use civil days ?
{- Return a date with year count added to date.
TODO this is inefficient, as adding larger numbers of years loops a lot.
-}
addYear : Int -> Date -> Date
addYear yearCount date =
addMonth (12 * yearCount) date


{-| Return a Period representing date difference. date1 - date2.
If you add the result of this function to date2 with addend of 1
will return date1.
**Differences to Period.diff**
* Duration DeltaRecord excludes week field
* Duration DeltaRecord includes month field
* Duration DeltaRecord includes year field
* Day is number of days difference between months.
When adding a Duration DeltaRecord to a date.
The larger granularity fields are added before lower granularity fields
so Years are added before Months before Days etc.
* Very different behaviour to Period diff
* If date1 > date2 then all fields in DeltaRecord will be positive or zero.
* If date1 < date2 then all fields in DeltaRecord will be negative or zero.
* Because it deals with non fixed length periods of time
Example 1.
days in 2016/05 (May) = 31
days in 2016/04 (Apr) = 30
days in 2016/03 (Mar) = 31
days in 2015/03 (Mar) = 31
diff of "2016/05/15" "2015/03/20"
result naive field diff.
year 1, month 2, day -5
days "2015/03/20" to "2015/04/01" (31 - 20) = 11 days (12). still in march with 11.
days "2015/04/01" to "2016/04/15" (15 - 1) = 14 days
months "2016/04/15" to "2016/05/15" 1 months
result field diff
year 1, month 1, day 26
This logic applies all the way down to milliseconds.
-}
diff : Date -> Date -> DeltaRecord
diff date1 date2 =
if Compare.is Compare.After date1 date2 then
positiveDiff date1 date2 1
else
positiveDiff date2 date1 -1


{-| Return diff between dates. date1 - date.
Precondition for this function is date1 must be after date2.
Input mult is used to multiply output fields as needed for caller,
this is used to conditionally negate them in initial use case.
-}
positiveDiff : Date -> Date -> Int -> DeltaRecord
positiveDiff date1 date2 mult =
let
year1 = Date.year date1
year2 = Date.year date2
month1Mon = Date.month date1
month2Mon = Date.month date2
month1 = Core.monthToInt month1Mon
month2 = Core.monthToInt month2Mon
day1 = Date.day date1
day2 = Date.day date2
hour1 = Date.hour date1
hour2 = Date.hour date2
minute1 = Date.minute date1
minute2 = Date.minute date2
second1 = Date.second date1
second2 = Date.second date2
msec1 = Date.millisecond date1
msec2 = Date.millisecond date2
-- _ = Debug.log "diff>>" ((year2, year1), (month1, month2), (day1, day2))

-- Accumlated diff
accDiff acc v1 v2 maxV2 =
if v1 < v2 then
(acc - 1, maxV2 + v1 - v2)
else
(acc, v1 - v2)

daysInDate2Month = Core.daysInMonth year2 month2Mon
-- _ = Debug.log "daysInDate2Month" (year2, month2Mon, daysInDate2Month)
(yearDiff, monthDiffA) = accDiff (year1 - year2) month1 month2 12
-- _ = Debug.log "diff year:" ((year1 - year2), yearDiff)
(monthDiff, dayDiffA) = accDiff monthDiffA day1 day2 daysInDate2Month
-- _ = Debug.log "diff month:" (monthDiffA, monthDiff, (daysInDate2Month))
(dayDiff, hourDiffA) = accDiff dayDiffA hour1 hour2 24
-- _ = Debug.log "diff month:" (dayDiffA, dayDiff)
(hourDiff, minuteDiffA) = accDiff hourDiffA minute1 minute2 60
(minuteDiff, secondDiffA) = accDiff minuteDiffA second1 second2 60
(secondDiff, msecDiff) = accDiff secondDiffA msec1 msec2 1000
in
{ year = yearDiff * mult
, month = monthDiff * mult
, day = dayDiff * mult
, hour = hourDiff * mult
, minute = minuteDiff * mult
, second = secondDiff * mult
, millisecond = msecDiff * mult
}
10 changes: 5 additions & 5 deletions src/Date/Extra/Period.elm
Original file line number Diff line number Diff line change
Expand Up @@ -126,18 +126,18 @@ diff date1 date2 =
- (minuteDiff * Core.ticksAMinute)
- (secondDiff * Core.ticksASecond)
- (millisecondDiff * Core.ticksAMillisecond)
onlylDaysDiff = ticksDayDiff // Core.ticksADay
onlyDaysDiff = ticksDayDiff // Core.ticksADay
(weekDiff, dayDiff) =
if onlylDaysDiff < 0 then
if onlyDaysDiff < 0 then
let
absDayDiff = abs onlylDaysDiff
absDayDiff = abs onlyDaysDiff
in
( negate (absDayDiff // 7)
, negate (absDayDiff % 7)
)
else
( onlylDaysDiff // 7
, onlylDaysDiff % 7
( onlyDaysDiff // 7
, onlyDaysDiff % 7
)
in
{ week = weekDiff
Expand Down
Loading

0 comments on commit 85d9570

Please sign in to comment.