diff --git a/.gitignore b/.gitignore index 2c77d0839..c75e6a4b4 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,6 @@ Morphir.Elm.CLI.js Morphir.Elm.Generator.js docs.json /.coverage/ -tests-integration/reference-model/Dockerfile \ No newline at end of file +tests-integration/reference-model/Dockerfile + +.scala-build/ \ No newline at end of file diff --git a/elm-tooling.json b/elm-tooling.json new file mode 100644 index 000000000..e4c63d5bf --- /dev/null +++ b/elm-tooling.json @@ -0,0 +1,7 @@ +{ + "tools": { + "elm": "0.19.1", + "elm-format": "0.8.5", + "elm-json": "0.2.13" + } +} diff --git a/package-lock.json b/package-lock.json index 727894a76..52072005e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,7 @@ "": { "name": "morphir-elm", "version": "2.86.0", + "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { "ajv": "^8.10.0", @@ -39,6 +40,7 @@ "elm-doc-preview": "^5.0.5", "elm-format": "^0.8.5", "elm-test": "^0.19.1-revision6", + "elm-tooling": "^1.15.0", "execa": "^5.1.1", "gulp": "^4.0.2", "gulp-mocha": "^8.0.0", @@ -55,8 +57,8 @@ "typescript": "^4.4.3" }, "engines": { - "node": "v16.14.0", - "npm": "8.5.5" + "node": "*", + "npm": "*" } }, "node_modules/@ampproject/remapping": { @@ -4071,9 +4073,9 @@ } }, "node_modules/elm-tooling": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/elm-tooling/-/elm-tooling-1.7.0.tgz", - "integrity": "sha512-EHZ54voWrG3BhUONbH/wFw5U95H6N7R4QFgXHDrPIaDBDdeyNkpFu4QWArSWkhzxyCF7hqT8ya2yy7SferDsgg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/elm-tooling/-/elm-tooling-1.15.0.tgz", + "integrity": "sha512-quRE5LGJyrkPBoJ3MvFQ5RGgf80J0L0d3NkduStvXh4TmZuMXNC3Z/l2ZRoq2UTUaNWeYfO1Zx5wns1AvsTrnw==", "dev": true, "bin": { "elm-tooling": "index.js" @@ -13168,6 +13170,7 @@ } }, "redistributable/TypeSpec/sdk": { + "name": "@morphir/typespec-sdk", "version": "0.2.0", "dev": true, "license": "Apache-2.0", @@ -16348,9 +16351,9 @@ } }, "elm-tooling": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/elm-tooling/-/elm-tooling-1.7.0.tgz", - "integrity": "sha512-EHZ54voWrG3BhUONbH/wFw5U95H6N7R4QFgXHDrPIaDBDdeyNkpFu4QWArSWkhzxyCF7hqT8ya2yy7SferDsgg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/elm-tooling/-/elm-tooling-1.15.0.tgz", + "integrity": "sha512-quRE5LGJyrkPBoJ3MvFQ5RGgf80J0L0d3NkduStvXh4TmZuMXNC3Z/l2ZRoq2UTUaNWeYfO1Zx5wns1AvsTrnw==", "dev": true }, "emittery": { diff --git a/package.json b/package.json index 84d06f166..3ef142f9d 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "ncc-morphir-server": "ncc build cli/morphir-elm-develop.js -o dist/morphir-server", "build": "gulp && npm run ncc-morphir && npm run ncc-morphir-server && npx jest && gulp test", "test-coverage": "elm-coverage src/ --open", - "build-value-editor": "elm make cli/src/Morphir/Web/Editor.elm --output=cli/web/valueEditor.js" + "build-value-editor": "elm make cli/src/Morphir/Web/Editor.elm --output=cli/web/valueEditor.js", + "postinstall": "elm-tooling install" }, "repository": { "type": "git", @@ -75,6 +76,7 @@ "elm-doc-preview": "^5.0.5", "elm-format": "^0.8.5", "elm-test": "^0.19.1-revision6", + "elm-tooling": "^1.15.0", "execa": "^5.1.1", "gulp": "^4.0.2", "gulp-mocha": "^8.0.0", diff --git a/src/Morphir/IR/SDK/LocalDate.elm b/src/Morphir/IR/SDK/LocalDate.elm index 805d4b523..f5ae67b27 100644 --- a/src/Morphir/IR/SDK/LocalDate.elm +++ b/src/Morphir/IR/SDK/LocalDate.elm @@ -22,8 +22,8 @@ import Morphir.IR.Documented exposing (Documented) import Morphir.IR.Literal as Literal import Morphir.IR.Module as Module exposing (ModuleName) import Morphir.IR.Name as Name -import Morphir.IR.Path as Path exposing (Path) -import Morphir.IR.SDK.Basics exposing (intType) +import Morphir.IR.Path as Path +import Morphir.IR.SDK.Basics exposing (boolType, intType) import Morphir.IR.SDK.Common exposing (toFQName, vSpec) import Morphir.IR.SDK.Maybe exposing (maybeType) import Morphir.IR.SDK.String exposing (stringType) @@ -53,6 +53,20 @@ moduleSpec = , DerivedTypeSpecification [] config |> Documented "Type that represents a date concept." ) + , ( Name.fromString "DayOfWeek" + , CustomTypeSpecification [] + (Dict.fromList + [ ( Name.fromString "Monday", [] ) + , ( Name.fromString "Tuesday", [] ) + , ( Name.fromString "Wednesday", [] ) + , ( Name.fromString "Thursday", [] ) + , ( Name.fromString "Friday", [] ) + , ( Name.fromString "Saturday", [] ) + , ( Name.fromString "Sunday", [] ) + ] + ) + |> Documented "Type that represents days of the week." + ) , ( Name.fromString "Month" , CustomTypeSpecification [] (Dict.fromList @@ -75,9 +89,13 @@ moduleSpec = ] , values = Dict.fromList - [ vSpec "toISOString" [ ( "date", localDateType () ) ] (stringType ()) + [ vSpec "fromCalendarDate" [ ( "y", intType () ), ( "m", monthType () ), ( "d", intType () ) ] (localDateType ()) + , vSpec "toISOString" [ ( "date", localDateType () ) ] (stringType ()) , vSpec "fromISO" [ ( "iso", stringType () ) ] (maybeType () (localDateType ())) + , vSpec "fromOrdinalDate" [ ( "y", intType () ), ( "dayOfyear", intType () ) ] (localDateType ()) , vSpec "fromParts" [ ( "year", intType () ), ( "month", intType () ), ( "day", intType () ) ] (maybeType () (localDateType ())) + , vSpec "day" [ ( "localDate", localDateType () ) ] (intType ()) + , vSpec "dayOfWeek" [ ( "localDate", localDateType () ) ] (dayOfWeekType ()) , vSpec "diffInDays" [ ( "date1", localDateType () ), ( "date2", localDateType () ) ] (intType ()) , vSpec "diffInWeeks" [ ( "date1", localDateType () ), ( "date2", localDateType () ) ] (intType ()) , vSpec "diffInMonths" [ ( "date1", localDateType () ), ( "date2", localDateType () ) ] (intType ()) @@ -86,16 +104,32 @@ moduleSpec = , vSpec "addWeeks" [ ( "offset", intType () ), ( "startDate", localDateType () ) ] (localDateType ()) , vSpec "addMonths" [ ( "offset", intType () ), ( "startDate", localDateType () ) ] (localDateType ()) , vSpec "addYears" [ ( "offset", intType () ), ( "startDate", localDateType () ) ] (localDateType ()) + , vSpec "isWeekend" [ ( "localDate", localDateType () ) ] (boolType ()) + , vSpec "isWeekday" [ ( "localDate", localDateType () ) ] (boolType ()) + , vSpec "month" [ ( "localDate", localDateType () ) ] (monthType ()) + , vSpec "monthNumber" [ ( "localDate", localDateType () ) ] (intType ()) + , vSpec "monthToInt" [ ( "m", monthType () ) ] (intType ()) + , vSpec "year" [ ( "localDate", localDateType () ) ] (intType ()) ] , doc = Just "Contains the LocalDate type (representing a date concept), and it's associated functions." } +dayOfWeekType : a -> Type a +dayOfWeekType attributes = + Reference attributes (toFQName moduleName "DayOfWeek") [] + + localDateType : a -> Type a localDateType attributes = Reference attributes (toFQName moduleName "LocalDate") [] +monthType : a -> Type a +monthType attributes = + Reference attributes (toFQName moduleName "Month") [] + + nativeFunctions : List ( String, Native.Function ) nativeFunctions = [ ( "fromISO" diff --git a/src/Morphir/SDK/LocalDate.elm b/src/Morphir/SDK/LocalDate.elm index 3357383a4..32baf1ef4 100644 --- a/src/Morphir/SDK/LocalDate.elm +++ b/src/Morphir/SDK/LocalDate.elm @@ -19,11 +19,11 @@ module Morphir.SDK.LocalDate exposing ( LocalDate , diffInDays, diffInWeeks, diffInMonths, diffInYears , addDays, addWeeks, addMonths, addYears - , fromCalendarDate, fromISO, fromOrdinalDate, fromParts, fromRataDie - , toISOString, toRataDie + , fromCalendarDate, fromISO, fromOrdinalDate, fromParts + , toISOString, monthToInt , DayOfWeek(..), dayOfWeek, isWeekend, isWeekday , Month(..) - , year, month, day + , year, month, monthNumber, day ) {-| This module adds the definition of a date without time zones. Useful in business modeling. @@ -42,19 +42,19 @@ module Morphir.SDK.LocalDate exposing # Constructors -@docs fromCalendarDate, fromISO, fromOrdinalDate, fromParts, fromRataDie +@docs fromCalendarDate, fromISO, fromOrdinalDate, fromParts # Convert -@docs toISOString, toRataDie +@docs toISOString, monthToInt # Query @docs DayOfWeek, dayOfWeek, isWeekend, isWeekday @docs Month -@docs year, month, day +@docs year, month, monthNumber, day -} @@ -150,8 +150,8 @@ year. Out-of-range day values will be clamped. -} fromOrdinalDate : Int -> Int -> LocalDate -fromOrdinalDate y d = - Date.fromOrdinalDate y d +fromOrdinalDate y dayOfYear = + Date.fromOrdinalDate y dayOfYear {-| Construct a LocalDate based on ISO formatted string. Opportunity for error denoted by Maybe return type. @@ -173,14 +173,14 @@ Errors can occur when any of the given values fall outside of their relevant con For example, the date given as 2000 2 30 (2000-Feb-30) would fail because the day of the 30th is impossible. -} fromParts : Int -> Int -> Int -> Maybe LocalDate -fromParts yearNumber monthNumber dayOfMonthNumber = +fromParts yearNumber monthNum dayOfMonthNumber = -- We do all of this processing because our Elm Date library accepts invalid values while most other languages don't. -- So we want to maintain consistency. -- Oddly, Date has fromCalendarParts, but it's not exposed. let maybeMonth = - if monthNumber > 0 && monthNumber < 13 then - Just (Date.numberToMonth monthNumber) + if monthNum > 0 && monthNum < 13 then + Just (Date.numberToMonth monthNum) else Nothing @@ -250,6 +250,90 @@ month localDate = December +{-| Returns the month of the year as an Int, where January is month 1 and December is month 12. +-} +monthNumber : LocalDate -> Int +monthNumber localDate = + case Date.month localDate of + Time.Jan -> + 1 + + Time.Feb -> + 2 + + Time.Mar -> + 3 + + Time.Apr -> + 4 + + Time.May -> + 5 + + Time.Jun -> + 6 + + Time.Jul -> + 7 + + Time.Aug -> + 8 + + Time.Sep -> + 9 + + Time.Oct -> + 10 + + Time.Nov -> + 11 + + Time.Dec -> + 12 + + +{-| Converts a Month to an Int, where January is month 1 and December is month 12. +-} +monthToInt : Month -> Int +monthToInt m = + case m of + January -> + 1 + + February -> + 2 + + March -> + 3 + + April -> + 4 + + May -> + 5 + + June -> + 6 + + July -> + 7 + + August -> + 8 + + September -> + 9 + + October -> + 10 + + November -> + 11 + + December -> + 12 + + monthToMonth : Month -> Time.Month monthToMonth m = case m of @@ -373,19 +457,3 @@ type Month | October | November | December - - -{-| Construct a LocalDate from Integer Rata Die, a system for system for assigning calendar days to -numbers, with 1 representing 0001-01-01. --} -fromRataDie : Int -> LocalDate -fromRataDie rataDieNumber = - Date.fromRataDie rataDieNumber - - -{-| Convert a LocalDate to its number representation in Rata Die. Rata Die is a system for -assigning calendar days to numbers, with 1 representing 0001-01-01. --} -toRataDie : LocalDate -> Int -toRataDie localDate = - Date.toRataDie localDate diff --git a/tests-integration/reference-model/src/Morphir/Reference/Model/SDK/LocalDate.elm b/tests-integration/reference-model/src/Morphir/Reference/Model/SDK/LocalDate.elm index 93f6330f6..a899ee90a 100644 --- a/tests-integration/reference-model/src/Morphir/Reference/Model/SDK/LocalDate.elm +++ b/tests-integration/reference-model/src/Morphir/Reference/Model/SDK/LocalDate.elm @@ -3,6 +3,16 @@ module Morphir.Reference.Model.SDK.LocalDate exposing (..) import Morphir.SDK.LocalDate as Date exposing (..) +day : LocalDate -> Int +day date = + Date.day date + + +dayOfWeek : LocalDate -> DayOfWeek +dayOfWeek date = + Date.dayOfWeek date + + diffInDays : LocalDate -> LocalDate -> Int diffInDays fromDate toDate = Date.diffInDays fromDate toDate @@ -57,6 +67,21 @@ addYears count date = Date.addYears count date +{-| Create a date from a [calendar date][gregorian]: a year, month, and day of +the month. Out-of-range day values will be clamped. + + import Morphir.SDK.LocalDate exposing (fromCalendarDate, Month(..)) + + fromCalendarDate 2018 September 26 + +[gregorian]: https://en.wikipedia.org/wiki/Proleptic_Gregorian_calendar + +-} +fromCalendarDate : Int -> Month -> Int -> LocalDate +fromCalendarDate y m d = + Date.fromCalendarDate y m d + + {-| Construct a LocalDate based on ISO formatted string. Opportunity for error denoted by Maybe return type. -} fromISO : String -> Maybe LocalDate @@ -64,6 +89,20 @@ fromISO iso = Date.fromISO iso +{-| Returns true if the date falls on a weekend (Saturday or Sunday). +-} +isWeekend : LocalDate -> Bool +isWeekend date = + Date.isWeekend date + + +{-| Returns true if the date falls on a weekday (any day other than Saturday or Sunday). +-} +isWeekday : LocalDate -> Bool +isWeekday date = + Date.isWeekday date + + {-| Convert a LocalDate to a string in ISO format. -} toISOString : LocalDate -> String @@ -71,10 +110,45 @@ toISOString localDate = Date.toISOString localDate +{-| Create a date from an [ordinal date][ordinaldate]: a year and day of the +year. Out-of-range day values will be clamped. + + import Morphir.SDK.LocalDate exposing (fromOrdinalDate) + + fromOrdinalDate 2018 269 + +[ordinaldate]: https://en.wikipedia.org/wiki/Ordinal_date + +-} +fromOrdinalDate : Int -> Int -> LocalDate +fromOrdinalDate y dayOfYear = + Date.fromOrdinalDate y dayOfYear + + {-| Construct a LocalDate based on Year, Month, Day. Opportunity for error denoted by Maybe return type. Errors can occur when any of the given values fall outside of their relevant constraints. For example, the date given as 2000 2 30 (2000-Feb-30) would fail because the day of the 30th is impossible. -} fromParts : Int -> Int -> Int -> Maybe LocalDate -fromParts year month day = - Date.fromParts year month day +fromParts y m d = + Date.fromParts y m d + + +month : LocalDate -> Month +month date = + Date.month date + + +monthNumber : LocalDate -> Int +monthNumber date = + Date.monthNumber date + + +monthToInt : Month -> Int +monthToInt m = + Date.monthToInt m + + +year : LocalDate -> Int +year date = + Date.year date diff --git a/tests/Morphir/SDK/LocalDateTests.elm b/tests/Morphir/SDK/LocalDateTests.elm index ec85a15af..579dd1d60 100644 --- a/tests/Morphir/SDK/LocalDateTests.elm +++ b/tests/Morphir/SDK/LocalDateTests.elm @@ -108,21 +108,4 @@ constructorTests = \_ -> LocalDate.fromOrdinalDate 2023 15 |> Expect.equal (Date.fromCalendarDate 2023 Jan 15) - , test "valid fromRataDie" <| - \_ -> - LocalDate.fromRataDie 1 - |> Expect.equal (Date.fromCalendarDate 1 Jan 1) - , test "valid contemporary fromRataDie" <| - \_ -> - LocalDate.fromRataDie 738860 - |> Expect.equal (Date.fromCalendarDate 2023 Dec 6) - , test "valid toRataDie" <| - \_ -> - Date.fromCalendarDate 1 Jan 1 - |> LocalDate.toRataDie - |> Expect.equal 1 - , test "valid contemporary toRataDie" <| - \_ -> - LocalDate.toRataDie (Date.fromCalendarDate 2023 Dec 6) - |> Expect.equal 738860 ]