diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 1c6afb9..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "parserOptions": { - "ecmaVersion": 6, - "sourceType": "module" - }, - "extends": "eslint:recommended", - "rules": { - "strict": [2, "global"], - "block-scoped-var": 2, - "consistent-return": 2, - "eqeqeq": [2, "smart"], - "guard-for-in": 2, - "no-caller": 2, - "no-extend-native": 2, - "no-loop-func": 2, - "no-new": 2, - "no-param-reassign": 2, - "no-return-assign": 2, - "no-unused-expressions": 2, - "no-use-before-define": 2, - "radix": [2, "always"], - "indent": [2, 2], - "quotes": [2, "double"], - "semi": [2, "always"] - } -} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c69237a..6d95f29 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,30 +6,19 @@ on: pull_request: branches: [master] +permissions: + contents: read + jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - - uses: purescript-contrib/setup-purescript@main - with: - purescript: "unstable" - - - uses: actions/setup-node@v2 + - uses: actions/checkout@v6 + - uses: actions/setup-node@v6 with: - node-version: "14.x" - - - name: Install dependencies - run: | - npm install -g bower - npm install - bower install --production - - - name: Build source - run: npm run-script build - - - name: Run tests - run: | - bower install - npm run-script test --if-present + node-version: "24" + - run: npm install + - run: npm run build + - run: npm test + - run: npm run lint + - run: npm run format:check diff --git a/.gitignore b/.gitignore index 7224331..e634a28 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,10 @@ /.* !/.gitignore -!/.eslintrc.json +!/.tidyrc.json +!/.oxfmtrc.json !/.github/ -package-lock.json +/package-lock.json +/spago.lock /bower_components/ /node_modules/ /output/ diff --git a/.oxfmtrc.json b/.oxfmtrc.json new file mode 100644 index 0000000..757fd64 --- /dev/null +++ b/.oxfmtrc.json @@ -0,0 +1,3 @@ +{ + "trailingComma": "es5" +} diff --git a/.tidyrc.json b/.tidyrc.json new file mode 100644 index 0000000..9e39cf1 --- /dev/null +++ b/.tidyrc.json @@ -0,0 +1,10 @@ +{ + "importSort": "ide", + "importWrap": "source", + "indent": 2, + "operatorsFile": null, + "ribbon": 1, + "typeArrowPlacement": "first", + "unicode": "never", + "width": null +} diff --git a/CHANGELOG.md b/CHANGELOG.md index cacc9ef..43286bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ Notable changes to this project are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [Unreleased](https://github.com/purescript/purescript-datetime/compare/v6.1.1...HEAD) Breaking changes: @@ -12,11 +12,18 @@ Bugfixes: Other improvements: +## [v6.1.1](https://github.com/purescript/purescript-datetime/releases/tag/v6.1.1) - 2026-10-17 + +Other improvements: + +- Migrate from Bower/Pulp to Spago and update CI (#103 by @thomashoneyman). + ## [v6.1.0](https://github.com/purescript/purescript-datetime/releases/tag/v6.1.0) - 2022-07-13 Breaking changes: New features: + - Added `diff` for `Instant` (#99 by @i-am-the-slime, #100 by @garyb) Bugfixes: @@ -26,6 +33,7 @@ Other improvements: ## [v6.0.0](https://github.com/purescript/purescript-datetime/releases/tag/v6.0.0) - 2022-04-27 Breaking changes: + - Migrate FFI to ES modules (#93 by @JordanMartinez) New features: @@ -33,31 +41,37 @@ New features: Bugfixes: Other improvements: + - Drop deprecated `math` dependency; update imports (#94 by @JordanMartinez) ## [v5.0.2](https://github.com/purescript/purescript-datetime/releases/tag/v5.0.2) - 2021-04-19 Other improvements: + - Fix one more UnusedName warning revealed by v0.14.1 PureScript release (@thomashoneyman) ## [v5.0.1](https://github.com/purescript/purescript-datetime/releases/tag/v5.0.1) - 2021-04-19 Other improvements: + - Fix UnusedName warnings revealed by v0.14.1 PureScript release (#91 by @JordanMartinez) ## [v5.0.0](https://github.com/purescript/purescript-datetime/releases/tag/v5.0.0) - 2021-02-26 Breaking changes: - - Added support for PureScript 0.14 and dropped support for all previous versions (#81) + +- Added support for PureScript 0.14 and dropped support for all previous versions (#81) New features: Bugfixes: + - Fixed `genDate` generator frequency (#83) Other improvements: - - Migrated CI to GitHub Actions and updated installation instructions to use Spago (#82) - - Added a CHANGELOG.md file and pull request template (#84, #85) + +- Migrated CI to GitHub Actions and updated installation instructions to use Spago (#82) +- Added a CHANGELOG.md file and pull request template (#84, #85) ## [v4.1.1](https://github.com/purescript/purescript-datetime/releases/tag/v4.1.1) - 2019-02-09 @@ -178,6 +192,7 @@ Initial release candidate of the library intended for the 0.7 compiler. ## [v0.3.0](https://github.com/purescript/purescript-datetime/releases/tag/v0.3.0) - 2015-02-21 **This release requires PureScript v0.6.8 or later** + - Updated dependencies ## [v0.2.0](https://github.com/purescript/purescript-datetime/releases/tag/v0.2.0) - 2015-01-11 @@ -199,4 +214,3 @@ Added `fromStringStrict` and updated dependencies (@jdegoes) ## [v0.0.1](https://github.com/purescript/purescript-datetime/releases/tag/v0.0.1) - 2014-10-14 Initial version release. - diff --git a/bower.json b/bower.json deleted file mode 100644 index 712032a..0000000 --- a/bower.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "purescript-datetime", - "homepage": "https://github.com/purescript/purescript-datetime", - "license": "BSD-3-Clause", - "repository": { - "type": "git", - "url": "https://github.com/purescript/purescript-datetime.git" - }, - "ignore": [ - "**/.*", - "bower_components", - "node_modules", - "output", - "bower.json", - "package.json" - ], - "dependencies": { - "purescript-bifunctors": "^6.0.0", - "purescript-control": "^6.0.0", - "purescript-either": "^6.0.0", - "purescript-enums": "^6.0.0", - "purescript-foldable-traversable": "^6.0.0", - "purescript-functions": "^6.0.0", - "purescript-gen": "^4.0.0", - "purescript-integers": "^6.0.0", - "purescript-lists": "^7.0.0", - "purescript-maybe": "^6.0.0", - "purescript-newtype": "^5.0.0", - "purescript-numbers": "^9.0.0", - "purescript-ordered-collections": "^3.0.0", - "purescript-partial": "^4.0.0", - "purescript-prelude": "^6.0.0", - "purescript-tuples": "^7.0.0" - }, - "devDependencies": { - "purescript-assert": "^6.0.0", - "purescript-console": "^6.0.0", - "purescript-strings": "^6.0.0" - } -} diff --git a/package.json b/package.json index a1d6811..c0a8ad2 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,17 @@ { "private": true, "scripts": { - "clean": "rimraf output && rimraf .pulp-cache", - "build": "eslint src && pulp build -- --censor-lib --strict", - "test": "pulp test" + "build": "spago build", + "test": "spago test", + "lint": "oxlint --deny-warnings src test", + "format": "purs-tidy format-in-place src test && oxfmt --write .", + "format:check": "purs-tidy check src test && oxfmt --check ." }, "devDependencies": { - "eslint": "^7.15.0", - "pulp": "16.0.0-0", - "purescript-psa": "^0.8.2", - "rimraf": "^3.0.2" + "oxfmt": "^0.73.0", + "oxlint": "^1.88.0", + "purescript": "^0.15.16", + "purs-tidy": "^0.11.1", + "spago": "^1.0.5" } } diff --git a/spago.yaml b/spago.yaml new file mode 100644 index 0000000..9da96d3 --- /dev/null +++ b/spago.yaml @@ -0,0 +1,39 @@ +package: + name: datetime + description: Date and time types and functions. + build: + strict: true + pedanticPackages: true + publish: + version: 6.1.1 + license: BSD-3-Clause + location: + githubOwner: purescript + githubRepo: purescript-datetime + dependencies: + - bifunctors: ">=6.0.0 <7.0.0" + - control: ">=6.0.0 <7.0.0" + - either: ">=6.0.0 <7.0.0" + - enums: ">=6.0.0 <7.0.0" + - foldable-traversable: ">=6.0.0 <7.0.0" + - functions: ">=6.0.0 <7.0.0" + - gen: ">=4.0.0 <5.0.0" + - integers: ">=6.0.0 <7.0.0" + - lists: ">=7.0.0 <8.0.0" + - maybe: ">=6.0.0 <7.0.0" + - newtype: ">=5.0.0 <6.0.0" + - numbers: ">=9.0.0 <10.0.0" + - ordered-collections: ">=3.0.0 <4.0.0" + - partial: ">=4.0.0 <5.0.0" + - prelude: ">=6.0.0 <7.0.0" + - tuples: ">=7.0.0 <8.0.0" + test: + main: Test.Main + strict: true + pedanticPackages: true + dependencies: + - arrays: ">=7.0.0 <8.0.0" + - assert: ">=6.0.0 <7.0.0" + - console: ">=6.0.0 <7.0.0" + - effect: ">=4.0.0 <5.0.0" +workspace: {} diff --git a/src/Data/Date.purs b/src/Data/Date.purs index fbee82b..70da6a3 100644 --- a/src/Data/Date.purs +++ b/src/Data/Date.purs @@ -16,8 +16,8 @@ module Data.Date import Prelude import Data.Date.Component (Day, Month(..), Weekday(..), Year) -import Data.Enum (class Enum, toEnum, fromEnum, succ, pred) -import Data.Function.Uncurried (Fn3, runFn3, Fn4, runFn4, Fn6, runFn6) +import Data.Enum (class Enum, fromEnum, pred, succ, toEnum) +import Data.Function.Uncurried (Fn3, Fn4, Fn6, runFn3, runFn4, runFn6) import Data.Int (fromNumber) import Data.Maybe (Maybe(..), fromJust, fromMaybe, isNothing) import Data.Time.Duration (class Duration, Days(..), Milliseconds, toDuration) @@ -39,9 +39,9 @@ canonicalDate y m d = runFn4 canonicalDateImpl mkDate y (fromEnum m) d -- | Constructs a date from year, month, and day components. The result will be -- | `Nothing` if the provided values result in an invalid date. exactDate :: Year -> Month -> Day -> Maybe Date -exactDate y m d = +exactDate y m d = do let dt = Date y m d - in if canonicalDate y m d == dt then Just dt else Nothing + if canonicalDate y m d == dt then Just dt else Nothing derive instance eqDate :: Eq Date derive instance ordDate :: Ord Date @@ -56,20 +56,22 @@ instance showDate :: Show Date where instance enumDate :: Enum Date where succ (Date y m d) = Date <$> y' <*> pure m' <*> d' where - d' = if isNothing sd then toEnum 1 else sd - m' = if isNothing sd then fromMaybe January sm else m - y' = if isNothing sd && isNothing sm then succ y else Just y - sd = let v = succ d in if v > Just l then Nothing else v - sm = succ m - l = lastDayOfMonth y m + d' = if isNothing sd then toEnum 1 else sd + m' = if isNothing sd then fromMaybe January sm else m + y' = if isNothing sd && isNothing sm then succ y else Just y + sd = do + let v = succ d + if v > Just l then Nothing else v + sm = succ m + l = lastDayOfMonth y m pred (Date y m d) = Date <$> y' <*> pure m' <*> d' where - d' = if isNothing pd then Just l else pd - m' = if isNothing pd then fromMaybe December pm else m - y' = if isNothing pd && isNothing pm then pred y else Just y - pd = pred d - pm = pred m - l = lastDayOfMonth y m' + d' = if isNothing pd then Just l else pd + m' = if isNothing pd then fromMaybe December pm else m + y' = if isNothing pd && isNothing pm then pred y else Just y + pd = pred d + pm = pred m + l = lastDayOfMonth y m' -- | The year component of a date value. year :: Date -> Year @@ -85,9 +87,9 @@ day (Date _ _ d) = d -- | The weekday for a date value. weekday :: Date -> Weekday -weekday = unsafePartial \(Date y m d) -> +weekday = unsafePartial \(Date y m d) -> do let n = runFn3 calcWeekday y (fromEnum m) d - in if n == 0 then fromJust (toEnum 7) else fromJust (toEnum n) + if n == 0 then fromJust (toEnum 7) else fromJust (toEnum n) -- | Adjusts a date with a Duration in days. The number of days must -- | already be an integer and fall within the valid range of values @@ -95,19 +97,30 @@ weekday = unsafePartial \(Date y m d) -> adjust :: Days -> Date -> Maybe Date adjust (Days n) date = fromNumber n >>= flip adj date where - adj 0 dt = Just dt - adj i (Date y m d) = adj i' =<< dt' - where - i' | low = j - | hi = j - fromEnum l - 1 - | otherwise = 0 - dt' | low = pred =<< Date y m <$> toEnum 1 - | hi = succ (Date y m l) - | otherwise = Date y m <$> toEnum j - j = i + fromEnum d + adj i dt = case i of + 0 -> Just dt + _ -> do + let + Date y m d = dt + i' = + if low then + j + else if hi then + j - fromEnum l - 1 + else + 0 + dt' = + if low then + pred =<< Date y m <$> toEnum 1 + else if hi then + succ (Date y m l) + else + Date y m <$> toEnum j + j = i + fromEnum d low = j < 1 - hi = j > fromEnum l - l = lastDayOfMonth y (if low then fromMaybe December (pred m) else m) + hi = j > fromEnum l + l = lastDayOfMonth y (if low then fromMaybe December (pred m) else m) + adj i' =<< dt' -- | Calculates the difference between two dates, returning the result as a -- | duration. @@ -140,7 +153,7 @@ lastDayOfMonth y m = case m of November -> unsafeDay 30 December -> unsafeDay 31 where - unsafeDay = unsafePartial fromJust <<< toEnum + unsafeDay = unsafePartial fromJust <<< toEnum -- TODO: these could (and probably should) be implemented in PS foreign import canonicalDateImpl :: Fn4 (Year -> Int -> Day -> Date) Year Int Day Date diff --git a/src/Data/Date/Component.purs b/src/Data/Date/Component.purs index 2762eaa..734a613 100644 --- a/src/Data/Date/Component.purs +++ b/src/Data/Date/Component.purs @@ -7,7 +7,7 @@ module Data.Date.Component import Prelude -import Data.Enum (class Enum, class BoundedEnum, toEnum, fromEnum, Cardinality(..)) +import Data.Enum (class BoundedEnum, class Enum, Cardinality(..), fromEnum, toEnum) import Data.Maybe (Maybe(..)) -- | A year component for a date. @@ -34,9 +34,11 @@ instance enumYear :: Enum Year where instance boundedEnumYear :: BoundedEnum Year where cardinality = Cardinality 547580 - toEnum n - | n >= (-271820) && n <= 275759 = Just (Year n) - | otherwise = Nothing + toEnum n = + if n >= (-271820) && n <= 275759 then + Just (Year n) + else + Nothing fromEnum (Year n) = n instance showYear :: Show Year where @@ -99,18 +101,19 @@ instance boundedEnumMonth :: BoundedEnum Month where December -> 12 instance showMonth :: Show Month where - show January = "January" - show February = "February" - show March = "March" - show April = "April" - show May = "May" - show June = "June" - show July = "July" - show August = "August" - show September = "September" - show October = "October" - show November = "November" - show December = "December" + show = case _ of + January -> "January" + February -> "February" + March -> "March" + April -> "April" + May -> "May" + June -> "June" + July -> "July" + August -> "August" + September -> "September" + October -> "October" + November -> "November" + December -> "December" -- | A day component for a date. -- | @@ -132,9 +135,11 @@ instance enumDay :: Enum Day where instance boundedEnumDay :: BoundedEnum Day where cardinality = Cardinality 31 - toEnum n - | n >= 1 && n <= 31 = Just (Day n) - | otherwise = Nothing + toEnum n = + if n >= 1 && n <= 31 then + Just (Day n) + else + Nothing fromEnum (Day n) = n instance showDay :: Show Day where @@ -182,10 +187,11 @@ instance boundedEnumWeekday :: BoundedEnum Weekday where Sunday -> 7 instance showWeekday :: Show Weekday where - show Monday = "Monday" - show Tuesday = "Tuesday" - show Wednesday = "Wednesday" - show Thursday = "Thursday" - show Friday = "Friday" - show Saturday = "Saturday" - show Sunday = "Sunday" + show = case _ of + Monday -> "Monday" + Tuesday -> "Tuesday" + Wednesday -> "Wednesday" + Thursday -> "Thursday" + Friday -> "Friday" + Saturday -> "Saturday" + Sunday -> "Sunday" diff --git a/src/Data/Date/Component/Gen.purs b/src/Data/Date/Component/Gen.purs index fb5fb80..691d732 100644 --- a/src/Data/Date/Component/Gen.purs +++ b/src/Data/Date/Component/Gen.purs @@ -1,6 +1,7 @@ module Data.Date.Component.Gen where import Prelude + import Control.Monad.Gen (class MonadGen, chooseInt) import Data.Date.Component (Day, Month, Weekday, Year) import Data.Enum (toEnum) diff --git a/src/Data/Date/Gen.purs b/src/Data/Date/Gen.purs index 1608782..589182c 100644 --- a/src/Data/Date/Gen.purs +++ b/src/Data/Date/Gen.purs @@ -4,6 +4,7 @@ module Data.Date.Gen ) where import Prelude + import Control.Monad.Gen (class MonadGen, chooseInt) import Data.Date (Date, adjust, exactDate, isLeapYear) import Data.Date.Component.Gen (genDay, genMonth, genWeekday, genYear) diff --git a/src/Data/DateTime.js b/src/Data/DateTime.js index 86578b7..01d82a2 100644 --- a/src/Data/DateTime.js +++ b/src/Data/DateTime.js @@ -7,8 +7,24 @@ var createUTC = function (y, mo, d, h, m, s, ms) { }; export function calcDiff(rec1, rec2) { - var msUTC1 = createUTC(rec1.year, rec1.month - 1, rec1.day, rec1.hour, rec1.minute, rec1.second, rec1.millisecond); - var msUTC2 = createUTC(rec2.year, rec2.month - 1, rec2.day, rec2.hour, rec2.minute, rec2.second, rec2.millisecond); + var msUTC1 = createUTC( + rec1.year, + rec1.month - 1, + rec1.day, + rec1.hour, + rec1.minute, + rec1.second, + rec1.millisecond + ); + var msUTC2 = createUTC( + rec2.year, + rec2.month - 1, + rec2.day, + rec2.hour, + rec2.minute, + rec2.second, + rec2.millisecond + ); return msUTC1 - msUTC2; } @@ -16,17 +32,27 @@ export function adjustImpl(just) { return function (nothing) { return function (offset) { return function (rec) { - var msUTC = createUTC(rec.year, rec.month - 1, rec.day, rec.hour, rec.minute, rec.second, rec.millisecond); + var msUTC = createUTC( + rec.year, + rec.month - 1, + rec.day, + rec.hour, + rec.minute, + rec.second, + rec.millisecond + ); var dt = new Date(msUTC + offset); - return isNaN(dt.getTime()) ? nothing : just({ - year: dt.getUTCFullYear(), - month: dt.getUTCMonth() + 1, - day: dt.getUTCDate(), - hour: dt.getUTCHours(), - minute: dt.getUTCMinutes(), - second: dt.getUTCSeconds(), - millisecond: dt.getUTCMilliseconds() - }); + return isNaN(dt.getTime()) + ? nothing + : just({ + year: dt.getUTCFullYear(), + month: dt.getUTCMonth() + 1, + day: dt.getUTCDate(), + hour: dt.getUTCHours(), + minute: dt.getUTCMinutes(), + second: dt.getUTCSeconds(), + millisecond: dt.getUTCMilliseconds(), + }); }; }; }; diff --git a/src/Data/DateTime.purs b/src/Data/DateTime.purs index 2119ded..e6c8ed2 100644 --- a/src/Data/DateTime.purs +++ b/src/Data/DateTime.purs @@ -15,11 +15,11 @@ module Data.DateTime import Prelude import Data.Date (Date, Day, Month(..), Weekday(..), Year, canonicalDate, day, exactDate, month, weekday, year) -import Data.Enum (toEnum, fromEnum) +import Data.Enum (fromEnum, toEnum) import Data.Function.Uncurried (Fn2, runFn2) -import Data.Time (Hour, Millisecond, Minute, Second, Time(..), hour, setHour, millisecond, setMillisecond, minute, setMinute, second, setSecond) -import Data.Time.Duration (class Duration, fromDuration, toDuration, Milliseconds) import Data.Maybe (Maybe(..)) +import Data.Time (Hour, Millisecond, Minute, Second, Time(..), hour, millisecond, minute, second, setHour, setMillisecond, setMinute, setSecond) +import Data.Time.Duration (class Duration, Milliseconds, fromDuration, toDuration) -- | A date/time value in the Gregorian calendar/UTC time zone. data DateTime = DateTime Date Time diff --git a/src/Data/DateTime/Gen.purs b/src/Data/DateTime/Gen.purs index 717d37a..1b5cd20 100644 --- a/src/Data/DateTime/Gen.purs +++ b/src/Data/DateTime/Gen.purs @@ -5,6 +5,7 @@ module Data.DateTime.Gen ) where import Prelude + import Control.Monad.Gen (class MonadGen) import Data.Date.Gen (genDate, genDay, genMonth, genWeekday, genYear) import Data.DateTime (DateTime(..)) diff --git a/src/Data/DateTime/Instant.js b/src/Data/DateTime/Instant.js index c016c34..e3efae5 100644 --- a/src/Data/DateTime/Instant.js +++ b/src/Data/DateTime/Instant.js @@ -13,6 +13,8 @@ export function fromDateTimeImpl(y, mo, d, h, mi, s, ms) { export function toDateTimeImpl(ctor) { return function (instant) { var dt = new Date(instant); - return ctor (dt.getUTCFullYear())(dt.getUTCMonth() + 1)(dt.getUTCDate())(dt.getUTCHours())(dt.getUTCMinutes())(dt.getUTCSeconds())(dt.getUTCMilliseconds()); + return ctor(dt.getUTCFullYear())(dt.getUTCMonth() + 1)(dt.getUTCDate())(dt.getUTCHours())( + dt.getUTCMinutes() + )(dt.getUTCSeconds())(dt.getUTCMilliseconds()); }; } diff --git a/src/Data/DateTime/Instant.purs b/src/Data/DateTime/Instant.purs index ad32d8c..01ab52b 100644 --- a/src/Data/DateTime/Instant.purs +++ b/src/Data/DateTime/Instant.purs @@ -10,7 +10,7 @@ module Data.DateTime.Instant import Prelude -import Data.DateTime (Millisecond, Second, Minute, Hour, Day, Year, DateTime(..), Date, Time(..), canonicalDate, millisecond, second, minute, hour, day, month, year) +import Data.DateTime (Date, DateTime(..), Day, Hour, Millisecond, Minute, Second, Time(..), Year, canonicalDate, day, hour, millisecond, minute, month, second, year) import Data.Enum (fromEnum, toEnum) import Data.Function.Uncurried (Fn7, runFn7) import Data.Maybe (Maybe(..), fromJust) @@ -42,9 +42,11 @@ instance showInstant :: Show Instant where -- | minimum acceptable value equates to the `bottom` `DateTime` and the maximum -- | acceptable value equates to the `top` `DateTime`. instant :: Milliseconds -> Maybe Instant -instant ms@(Milliseconds n) - | n >= -8639977881600000.0 && n <= 8639977881599999.0 = Just (Instant ms) - | otherwise = Nothing +instant ms@(Milliseconds n) = + if n >= -8639977881600000.0 && n <= 8639977881599999.0 then + Just (Instant ms) + else + Nothing -- | Lowers an `Instant` to a `Milliseconds` duration. unInstant :: Instant -> Milliseconds @@ -54,15 +56,25 @@ unInstant (Instant ms) = ms fromDateTime :: DateTime -> Instant fromDateTime (DateTime d t) = runFn7 fromDateTimeImpl - (year d) (fromEnum (month d)) (day d) - (hour t) (minute t) (second t) (millisecond t) + (year d) + (fromEnum (month d)) + (day d) + (hour t) + (minute t) + (second t) + (millisecond t) -- | Creates an `Instant` from a `Date` value, using the assumed time 00:00:00. fromDate :: Date -> Instant fromDate d = runFn7 fromDateTimeImpl - (year d) (fromEnum (month d)) (day d) - bottom bottom bottom bottom + (year d) + (fromEnum (month d)) + (day d) + bottom + bottom + bottom + bottom -- | Creates a `DateTime` value from an `Instant`. toDateTime :: Instant -> DateTime @@ -87,5 +99,5 @@ foreign import toDateTimeImpl :: (Year -> Int -> Day -> Hour -> Minute -> Second -- | hours = Instant.diff end start -- | log ("A long running Aff took " <> show hours) -- | ``` -diff :: forall d. Duration d => Instant → Instant → d +diff :: forall d. Duration d => Instant -> Instant -> d diff dt1 dt2 = toDuration (unInstant dt1 <> negateDuration (unInstant dt2)) diff --git a/src/Data/Interval.purs b/src/Data/Interval.purs index cff8f0b..2638595 100644 --- a/src/Data/Interval.purs +++ b/src/Data/Interval.purs @@ -7,13 +7,13 @@ module Data.Interval import Prelude import Control.Extend (class Extend, extend) -import Data.Bifoldable (class Bifoldable, bifoldl, bifoldr, bifoldrDefault, bifoldMapDefaultL) +import Data.Bifoldable (class Bifoldable, bifoldMapDefaultL, bifoldl, bifoldr, bifoldrDefault) import Data.Bifunctor (class Bifunctor, bimap) -import Data.Bitraversable (class Bitraversable, bitraverse, bisequenceDefault) -import Data.Foldable (class Foldable, foldl, foldr, foldrDefault, foldMapDefaultL) +import Data.Bitraversable (class Bitraversable, bisequenceDefault, bitraverse) +import Data.Foldable (class Foldable, foldMapDefaultL, foldl, foldr, foldrDefault) import Data.Interval.Duration (Duration(..), DurationComponent(..), day, hour, millisecond, minute, month, second, week, year) as Exports import Data.Maybe (Maybe) -import Data.Traversable (class Traversable, traverse, sequenceDefault) +import Data.Traversable (class Traversable, sequenceDefault, traverse) data RecurringInterval d a = RecurringInterval (Maybe Int) (Interval d a) @@ -22,10 +22,10 @@ derive instance ordRecurringInterval :: (Ord d, Ord a) => Ord (RecurringInterval instance showRecurringInterval :: (Show d, Show a) => Show (RecurringInterval d a) where show (RecurringInterval x y) = "(RecurringInterval " <> show x <> " " <> show y <> ")" -interval :: ∀ d a. RecurringInterval d a -> Interval d a +interval :: forall d a. RecurringInterval d a -> Interval d a interval (RecurringInterval _ i) = i -over :: ∀ f d a d' a'. Functor f => (Interval d a -> f (Interval d' a')) -> RecurringInterval d a -> f (RecurringInterval d' a') +over :: forall f d a d' a'. Functor f => (Interval d a -> f (Interval d' a')) -> RecurringInterval d a -> f (RecurringInterval d' a') over f (RecurringInterval n i) = map (RecurringInterval n) (f i) instance functorRecurringInterval :: Functor (RecurringInterval d) where @@ -56,60 +56,67 @@ instance extendRecurringInterval :: Extend (RecurringInterval d) where extend f a@(RecurringInterval n i) = RecurringInterval n (extend (const (f a)) i) data Interval d a - = StartEnd a a - | DurationEnd d a + = StartEnd a a + | DurationEnd d a | StartDuration a d - | DurationOnly d + | DurationOnly d derive instance eqInterval :: (Eq d, Eq a) => Eq (Interval d a) derive instance ordInterval :: (Ord d, Ord a) => Ord (Interval d a) instance showInterval :: (Show d, Show a) => Show (Interval d a) where - show (StartEnd x y) = "(StartEnd " <> show x <> " " <> show y <> ")" - show (DurationEnd d x) = "(DurationEnd " <> show d <> " " <> show x <> ")" - show (StartDuration x d) = "(StartDuration " <> show x <> " " <> show d <> ")" - show (DurationOnly d) = "(DurationOnly " <> show d <> ")" + show = case _ of + StartEnd x y -> "(StartEnd " <> show x <> " " <> show y <> ")" + DurationEnd d x -> "(DurationEnd " <> show d <> " " <> show x <> ")" + StartDuration x d -> "(StartDuration " <> show x <> " " <> show d <> ")" + DurationOnly d -> "(DurationOnly " <> show d <> ")" instance functorInterval :: Functor (Interval d) where map = bimap identity instance bifunctorInterval :: Bifunctor Interval where - bimap _ f (StartEnd x y) = StartEnd (f x) (f y) - bimap g f (DurationEnd d x) = DurationEnd (g d) (f x) - bimap g f (StartDuration x d) = StartDuration (f x) (g d) - bimap g _ (DurationOnly d) = DurationOnly (g d) + bimap g f = case _ of + StartEnd x y -> StartEnd (f x) (f y) + DurationEnd d x -> DurationEnd (g d) (f x) + StartDuration x d -> StartDuration (f x) (g d) + DurationOnly d -> DurationOnly (g d) instance foldableInterval :: Foldable (Interval d) where - foldl f z (StartEnd x y) = (z `f` x) `f` y - foldl f z (DurationEnd _ x) = z `f` x - foldl f z (StartDuration x _) = z `f` x - foldl _ z _ = z + foldl f z = case _ of + StartEnd x y -> (z `f` x) `f` y + DurationEnd _ x -> z `f` x + StartDuration x _ -> z `f` x + _ -> z foldr x = foldrDefault x foldMap = foldMapDefaultL instance bifoldableInterval :: Bifoldable Interval where - bifoldl _ f z (StartEnd x y) = (z `f` x) `f` y - bifoldl g f z (DurationEnd d x) = (z `g` d) `f` x - bifoldl g f z (StartDuration x d) = (z `g` d) `f` x - bifoldl g _ z (DurationOnly d) = z `g` d + bifoldl g f z = case _ of + StartEnd x y -> (z `f` x) `f` y + DurationEnd d x -> (z `g` d) `f` x + StartDuration x d -> (z `g` d) `f` x + DurationOnly d -> z `g` d bifoldr x = bifoldrDefault x bifoldMap = bifoldMapDefaultL instance traversableInterval :: Traversable (Interval d) where - traverse f (StartEnd x y) = StartEnd <$> f x <*> f y - traverse f (DurationEnd d x) = f x <#> DurationEnd d - traverse f (StartDuration x d) = f x <#> (_ `StartDuration` d) - traverse _ (DurationOnly d) = pure (DurationOnly d) + traverse f = case _ of + StartEnd x y -> StartEnd <$> f x <*> f y + DurationEnd d x -> f x <#> DurationEnd d + StartDuration x d -> f x <#> (_ `StartDuration` d) + DurationOnly d -> pure (DurationOnly d) sequence = sequenceDefault instance bitraversableInterval :: Bitraversable Interval where - bitraverse _ r (StartEnd x y) = StartEnd <$> r x <*> r y - bitraverse l r (DurationEnd d x) = DurationEnd <$> l d <*> r x - bitraverse l r (StartDuration x d) = StartDuration <$> r x <*> l d - bitraverse l _ (DurationOnly d) = DurationOnly <$> l d + bitraverse l r = case _ of + StartEnd x y -> StartEnd <$> r x <*> r y + DurationEnd d x -> DurationEnd <$> l d <*> r x + StartDuration x d -> StartDuration <$> r x <*> l d + DurationOnly d -> DurationOnly <$> l d bisequence = bisequenceDefault instance extendInterval :: Extend (Interval d) where - extend f a@(StartEnd _ _) = StartEnd (f a) (f a) - extend f a@(DurationEnd d _) = DurationEnd d (f a) - extend f a@(StartDuration _ d) = StartDuration (f a) d - extend _ (DurationOnly d) = DurationOnly d + extend f a = case a of + StartEnd _ _ -> StartEnd (f a) (f a) + DurationEnd d _ -> DurationEnd d (f a) + StartDuration _ d -> StartDuration (f a) d + DurationOnly d -> DurationOnly d diff --git a/src/Data/Interval/Duration.purs b/src/Data/Interval/Duration.purs index dac75c4..2f73452 100644 --- a/src/Data/Interval/Duration.purs +++ b/src/Data/Interval/Duration.purs @@ -32,18 +32,19 @@ instance monoidDuration :: Monoid Duration where mempty = Duration Map.empty data DurationComponent = Second | Minute | Hour | Day | Week | Month | Year + derive instance eqDurationComponent :: Eq DurationComponent derive instance ordDurationComponent :: Ord DurationComponent instance showDurationComponent :: Show DurationComponent where - show Minute = "Minute" - show Second = "Second" - show Hour = "Hour" - show Day = "Day" - show Week = "Week" - show Month = "Month" - show Year = "Year" - + show = case _ of + Minute -> "Minute" + Second -> "Second" + Hour -> "Hour" + Day -> "Day" + Week -> "Week" + Month -> "Month" + Year -> "Year" week :: Number -> Duration week = durationFromComponent Week diff --git a/src/Data/Interval/Duration/Iso.purs b/src/Data/Interval/Duration/Iso.purs index 19e42b1..3e2e0f9 100644 --- a/src/Data/Interval/Duration/Iso.purs +++ b/src/Data/Interval/Duration/Iso.purs @@ -13,14 +13,14 @@ import Control.Plus (empty) import Data.Either (Either(..)) import Data.Foldable (fold, foldMap) import Data.Interval.Duration (Duration(..), DurationComponent(..)) -import Data.List (List(..), reverse, span, null) +import Data.List (List(..), null, reverse, span) import Data.List.NonEmpty (fromList) import Data.List.Types (NonEmptyList) import Data.Map as Map -import Data.Number as Number import Data.Maybe (Maybe(..), isJust) import Data.Monoid.Additive (Additive(..)) import Data.Newtype (unwrap) +import Data.Number as Number import Data.Tuple (Tuple(..), snd) newtype IsoDuration = IsoDuration Duration @@ -41,17 +41,18 @@ data Error derive instance eqError :: Eq Error derive instance ordError :: Ord Error instance showError :: Show Error where - show (IsEmpty) = "(IsEmpty)" - show (InvalidWeekComponentUsage) = "(InvalidWeekComponentUsage)" - show (ContainsNegativeValue c) = "(ContainsNegativeValue " <> show c <> ")" - show (InvalidFractionalUse c) = "(InvalidFractionalUse " <> show c <> ")" + show = case _ of + IsEmpty -> "(IsEmpty)" + InvalidWeekComponentUsage -> "(InvalidWeekComponentUsage)" + ContainsNegativeValue c -> "(ContainsNegativeValue " <> show c <> ")" + InvalidFractionalUse c -> "(InvalidFractionalUse " <> show c <> ")" prettyError :: Error -> String -prettyError (IsEmpty) = "Duration is empty (has no components)" -prettyError (InvalidWeekComponentUsage) = "Week component of Duration is used with other components" -prettyError (ContainsNegativeValue c) = "Component `" <> show c <> "` contains negative value" -prettyError (InvalidFractionalUse c) = "Invalid usage of Fractional value at component `" <> show c <> "`" - +prettyError = case _ of + IsEmpty -> "Duration is empty (has no components)" + InvalidWeekComponentUsage -> "Week component of Duration is used with other components" + ContainsNegativeValue c -> "Component `" <> show c <> "` contains negative value" + InvalidFractionalUse c -> "Invalid usage of Fractional value at component `" <> show c <> "`" unIsoDuration :: IsoDuration -> Duration unIsoDuration (IsoDuration a) = a @@ -62,7 +63,7 @@ mkIsoDuration d = case fromList (checkValidIsoDuration d) of Nothing -> Right (IsoDuration d) checkValidIsoDuration :: Duration -> List Error -checkValidIsoDuration (Duration asMap) = check {asList, asMap} +checkValidIsoDuration (Duration asMap) = check { asList, asMap } where asList = reverse (Map.toUnfoldable asMap) check = fold @@ -72,20 +73,24 @@ checkValidIsoDuration (Duration asMap) = check {asList, asMap} , checkNegativeValues ] - type CheckEnv = { asList :: List (Tuple DurationComponent Number) - , asMap :: Map.Map DurationComponent Number} + , asMap :: Map.Map DurationComponent Number + } checkWeekUsage :: CheckEnv -> List Error -checkWeekUsage {asMap} = if isJust (Map.lookup Week asMap) && Map.size asMap > 1 - then pure InvalidWeekComponentUsage else empty +checkWeekUsage { asMap } = do + let hasWeekWithOtherComponents = isJust (Map.lookup Week asMap) && Map.size asMap > 1 + if hasWeekWithOtherComponents then + pure InvalidWeekComponentUsage + else + empty checkEmptiness :: CheckEnv -> List Error -checkEmptiness {asList} = if null asList then pure IsEmpty else empty +checkEmptiness { asList } = if null asList then pure IsEmpty else empty checkFractionalUse :: CheckEnv -> List Error -checkFractionalUse {asList} = case _.rest (span (snd >>> not isFractional) asList) of +checkFractionalUse { asList } = case _.rest (span (snd >>> not isFractional) asList) of Cons (Tuple c _) rest | checkRest rest -> pure (InvalidFractionalUse c) _ -> empty where @@ -93,5 +98,5 @@ checkFractionalUse {asList} = case _.rest (span (snd >>> not isFractional) asLis checkRest rest = unwrap (foldMap (snd >>> Number.abs >>> Additive) rest) > 0.0 checkNegativeValues :: CheckEnv -> List Error -checkNegativeValues {asList} = flip foldMap asList \(Tuple c num) -> +checkNegativeValues { asList } = flip foldMap asList \(Tuple c num) -> if num >= 0.0 then empty else pure (ContainsNegativeValue c) diff --git a/src/Data/Time.purs b/src/Data/Time.purs index 7224b32..0ae2233 100644 --- a/src/Data/Time.purs +++ b/src/Data/Time.purs @@ -1,9 +1,13 @@ module Data.Time ( Time(..) - , hour, setHour - , minute, setMinute - , second, setSecond - , millisecond, setMillisecond + , hour + , setHour + , minute + , setMinute + , second + , setSecond + , millisecond + , setMillisecond , adjust , diff , module Data.Time.Component @@ -14,8 +18,8 @@ import Prelude import Data.Enum (fromEnum, toEnum) import Data.Int as Int import Data.Maybe (fromJust) -import Data.Number as Number import Data.Newtype (unwrap) +import Data.Number as Number import Data.Time.Component (Hour, Millisecond, Minute, Second) import Data.Time.Duration (class Duration, Days(..), Milliseconds(..), fromDuration, negateDuration, toDuration) import Data.Tuple (Tuple(..)) @@ -72,7 +76,7 @@ setMillisecond ms (Time h m s _) = Time h m s ms -- | duration is negative, a negative number of days may also be returned as -- | the remainder. adjust :: forall d. Duration d => d -> Time -> Tuple Days Time -adjust d t = +adjust d t = do let d' = fromDuration d tLength = timeToMillis t @@ -81,10 +85,9 @@ adjust d t = msAdjust = d' <> negateDuration (fromDuration wholeDays) msAdjusted = tLength <> msAdjust wrap = if msAdjusted > maxTime then 1.0 else if msAdjusted < minTime then -1.0 else 0.0 - in - Tuple - (wholeDays <> Days wrap) - (millisToTime (msAdjusted <> Milliseconds (dayLength * -wrap))) + Tuple + (wholeDays <> Days wrap) + (millisToTime (msAdjusted <> Milliseconds (dayLength * -wrap))) maxTime :: Milliseconds maxTime = timeToMillis top @@ -95,12 +98,12 @@ minTime = timeToMillis bottom timeToMillis :: Time -> Milliseconds timeToMillis t = Milliseconds $ 3600000.0 * Int.toNumber (fromEnum (hour t)) - + 60000.0 * Int.toNumber (fromEnum (minute t)) - + 1000.0 * Int.toNumber (fromEnum (second t)) - + Int.toNumber (fromEnum (millisecond t)) + + 60000.0 * Int.toNumber (fromEnum (minute t)) + + 1000.0 * Int.toNumber (fromEnum (second t)) + + Int.toNumber (fromEnum (millisecond t)) millisToTime :: Milliseconds -> Time -millisToTime (Milliseconds ms') = +millisToTime (Milliseconds ms') = do let hourLength = 3600000.0 minuteLength = 60000.0 @@ -109,13 +112,12 @@ millisToTime (Milliseconds ms') = minutes = Number.floor ((ms' - hours * hourLength) / minuteLength) seconds = Number.floor ((ms' - (hours * hourLength + minutes * minuteLength)) / secondLength) milliseconds = ms' - (hours * hourLength + minutes * minuteLength + seconds * secondLength) - in - unsafePartial fromJust $ - Time - <$> toEnum (Int.floor hours) - <*> toEnum (Int.floor minutes) - <*> toEnum (Int.floor seconds) - <*> toEnum (Int.floor milliseconds) + unsafePartial fromJust $ + Time + <$> toEnum (Int.floor hours) + <*> toEnum (Int.floor minutes) + <*> toEnum (Int.floor seconds) + <*> toEnum (Int.floor milliseconds) -- | Calculates the difference between two times, returning the result as a -- | duration. diff --git a/src/Data/Time/Component.purs b/src/Data/Time/Component.purs index bbc6977..5286581 100644 --- a/src/Data/Time/Component.purs +++ b/src/Data/Time/Component.purs @@ -7,7 +7,7 @@ module Data.Time.Component import Prelude -import Data.Enum (class Enum, class BoundedEnum, toEnum, fromEnum, Cardinality(..)) +import Data.Enum (class BoundedEnum, class Enum, Cardinality(..), fromEnum, toEnum) import Data.Maybe (Maybe(..)) -- | An hour component for a time value. @@ -31,9 +31,11 @@ instance enumHour :: Enum Hour where instance boundedEnumHour :: BoundedEnum Hour where cardinality = Cardinality 24 - toEnum n - | n >= 0 && n <= 23 = Just (Hour n) - | otherwise = Nothing + toEnum n = + if n >= 0 && n <= 23 then + Just (Hour n) + else + Nothing fromEnum (Hour n) = n instance showHour :: Show Hour where @@ -60,9 +62,11 @@ instance enumMinute :: Enum Minute where instance boundedEnumMinute :: BoundedEnum Minute where cardinality = Cardinality 60 - toEnum n - | n >= 0 && n <= 59 = Just (Minute n) - | otherwise = Nothing + toEnum n = + if n >= 0 && n <= 59 then + Just (Minute n) + else + Nothing fromEnum (Minute n) = n instance showMinute :: Show Minute where @@ -89,9 +93,11 @@ instance enumSecond :: Enum Second where instance boundedEnumSecond :: BoundedEnum Second where cardinality = Cardinality 60 - toEnum n - | n >= 0 && n <= 59 = Just (Second n) - | otherwise = Nothing + toEnum n = + if n >= 0 && n <= 59 then + Just (Second n) + else + Nothing fromEnum (Second n) = n instance showSecond :: Show Second where @@ -119,9 +125,11 @@ instance enumMillisecond :: Enum Millisecond where instance boundedEnumMillisecond :: BoundedEnum Millisecond where cardinality = Cardinality 1000 - toEnum n - | n >= 0 && n <= 999 = Just (Millisecond n) - | otherwise = Nothing + toEnum n = + if n >= 0 && n <= 999 then + Just (Millisecond n) + else + Nothing fromEnum (Millisecond n) = n instance showMillisecond :: Show Millisecond where diff --git a/src/Data/Time/Gen.purs b/src/Data/Time/Gen.purs index 1aa5bb3..373b00d 100644 --- a/src/Data/Time/Gen.purs +++ b/src/Data/Time/Gen.purs @@ -4,6 +4,7 @@ module Data.Time.Gen ) where import Prelude + import Control.Monad.Gen (class MonadGen) import Data.Time (Time(..)) import Data.Time.Component.Gen (genHour, genMillisecond, genMinute, genSecond) diff --git a/test/Test/Main.purs b/test/Test/Main.purs index 0388fcb..b85cd0b 100644 --- a/test/Test/Main.purs +++ b/test/Test/Main.purs @@ -2,14 +2,12 @@ module Test.Main where import Prelude -import Effect (Effect) -import Effect.Console (log) import Data.Array as Array import Data.Date as Date import Data.DateTime as DateTime import Data.DateTime.Instant as Instant import Data.Either (Either(..), isRight) -import Data.Enum (class BoundedEnum, Cardinality, toEnum, enumFromTo, cardinality, succ, fromEnum, pred) +import Data.Enum (class BoundedEnum, Cardinality, cardinality, enumFromTo, fromEnum, pred, succ, toEnum) import Data.Interval as Interval import Data.Interval.Duration.Iso as IsoDuration import Data.Maybe (Maybe(..), fromJust) @@ -18,6 +16,8 @@ import Data.Number (floor) import Data.Time as Time import Data.Time.Duration as Duration import Data.Tuple (Tuple(..), snd) +import Effect (Effect) +import Effect.Console (log) import Partial.Unsafe (unsafePartial) import Test.Assert (assert) import Type.Proxy (Proxy(..)) @@ -41,10 +41,11 @@ main = do assert $ IsoDuration.mkIsoDuration (mempty) == Left (pure IsoDuration.IsEmpty) - let epochDate = unsafePartial fromJust $ Date.canonicalDate - <$> toEnum 1 - <*> pure bottom - <*> pure bottom + let + epochDate = unsafePartial fromJust $ Date.canonicalDate + <$> toEnum 1 + <*> pure bottom + <*> pure bottom let epochDateTime = DateTime.DateTime epochDate bottom let epochMillis = -62135596800000.0 -- time -------------------------------------------------------------------- @@ -139,7 +140,7 @@ main = do assert $ Date.adjust (Duration.Days 999.0) d1 == Just d4 assert $ Date.adjust (Duration.Days 10000.0) d5 == Just d1 assert $ Date.adjust (Duration.Days (-31.0)) d2 == Just d1 - assert $ Date.adjust (Duration.Days (- 999.0)) d4 == Just d1 + assert $ Date.adjust (Duration.Days (-999.0)) d4 == Just d1 assert $ Date.adjust (Duration.Days (-10000.0)) d1 == Just d5 -- datetime ---------------------------------------------------------------- @@ -152,9 +153,8 @@ main = do log "Check that adjust behaves as expected" assert $ DateTime.adjust (Duration.fromDuration (Duration.Days 31.0) <> Duration.fromDuration (Duration.Minutes 40.0)) dt1 == Just dt4 - assert $ (Date.year <<< DateTime.date <$> - (DateTime.adjust (Duration.Days 735963.0) epochDateTime)) - == toEnum 2016 + let adjustedYear = map (Date.year <<< DateTime.date) $ DateTime.adjust (Duration.Days 735963.0) epochDateTime + assert $ adjustedYear == toEnum 2016 log "Check that diff behaves as expected" assert $ DateTime.diff dt2 dt1 == Duration.Minutes 40.0