Skip to content

Commit

Permalink
Initial import
Browse files Browse the repository at this point in the history
  • Loading branch information
Richard Feldman committed Apr 1, 2018
0 parents commit 9db469d
Show file tree
Hide file tree
Showing 3 changed files with 207 additions and 0 deletions.
31 changes: 31 additions & 0 deletions .gitignore
@@ -0,0 +1,31 @@
elm.js
# Logs
logs
*.log

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules

# Elm packages and build artifacts
elm-stuff
17 changes: 17 additions & 0 deletions elm.json
@@ -0,0 +1,17 @@
{
"type": "package",
"name": "rtfeldman/iso8601",
"summary": "Convert ISO8601 date strings to and from Posix times",
"license": "BSD-3-Clause",
"version": "1.0.0",
"exposed-modules": [
"Iso8601"
],
"elm-version": "0.19.0 <= v < 0.20.0",
"dependencies": {
"elm-lang/core": "6.0.0 <= v < 7.0.0",
"elm-lang/parser": "1.0.0 <= v < 2.0.0",
"elm-lang/time": "1.0.0 <= v < 2.0.0"
},
"test-dependencies": {}
}
159 changes: 159 additions & 0 deletions src/Iso8601.elm
@@ -0,0 +1,159 @@
module Iso8601 exposing (fromTime, toTime)

import Parser exposing ((|.), (|=), Parser, int, succeed, symbol)
import Time exposing (Month(..), utc)


{-| Convert from an ISO-8601 date string to a Posix time.
ISO-8601 date strings sometimes specify things in UTC. Other times, they specify
a non-UTC time as well as a UTC offset. I view the choice to support UTC offsets
as a design flaw in ISO-8601 strings. This function corrects this flaw by
normalizing dates into UTC regardless of how they were specified.
For example, if an ISO-8601 date string specifies "9am in UTC+2", this function
will return 7am UTC. The UTC offset is automatically factored in, then discarded.
-}
toTime : String -> Result Parser.Error Time.Posix
toTime str =
Parser.run iso8601 str


fromParts : Int -> Int -> Int -> Int -> Int -> Int -> Int -> Time.Posix
fromParts year month day hour minute second ms =
Time.millisToPosix
-- TODO account for leap years
((year * 365 * 24 * 60 * 60 * 1000)
+ -- TODO calculate days-per-month accurately
(month * 12 * 24 * 60 * 60 * 1000)
+ (day * 24 * 60 * 60 * 1000)
+ (hour * 60 * 60 * 1000)
+ (minute * 60 * 1000)
+ (second * 1000)
+ ms
)


{-| From <https://www.timeanddate.com/date/leapyear.html>
In the Gregorian calendar three criteria must be taken into account to identify leap years:
- The year can be evenly divided by 4;
- If the year can be evenly divided by 100, it is NOT a leap year, unless;
- The year is also evenly divisible by 400. Then it is a leap year.
This means that in the Gregorian calendar, the years 2000 and 2400 are leap years, while 1800, 1900, 2100, 2200, 2300 and 2500 are NOT leap years.
-}
isLeapYear : Int -> Bool
isLeapYear year =
(modBy 4 year == 0) && ((modBy 100 year /= 0) || (modBy 400 year == 0))


{-| YYYY-MM-DDTHH:mm:ss.sssZ or ±YYYYYY-MM-DDTHH:mm:ss.sssZ
-}
iso8601 : Parser Time.Posix
iso8601 =
-- TODO account for format variations, including those with UTC offsets
succeed fromParts
|= int
-- YYYY
|. symbol "-"
|= int
-- MM
|. symbol "-"
|= int
-- DD
|. symbol "T"
|= int
-- HH
|. symbol ":"
|= int
-- mm
|. symbol ":"
|= int
-- ss
|. symbol ":"
|= int
-- sss
|. symbol "Z"


{-| Inflate a Posix integer into a more memory-intensive ISO-8601 date string.
It's generally best to avoid doing this unless an external API requires it.
(UTC integers are less error-prone, take up less memory, and are more efficient
for time arithmetic.)
Format: YYYY-MM-DDTHH:mm:ss.sssZ
-}
fromTime : Time.Posix -> String
fromTime time =
---- YYYY
toPaddedString 4 (Time.toYear utc time)
++ "-"
-- MM
++ fromMonth (Time.toMonth utc time)
++ "-"
-- DD
++ toPaddedString 2 (Time.toDay utc time)
++ "T"
-- HH
++ toPaddedString 2 (Time.toHour utc time)
++ ":"
-- mm
++ toPaddedString 2 (Time.toMinute utc time)
++ ":"
-- ss
++ toPaddedString 2 (Time.toSecond utc time)
++ ":"
-- sss
++ toPaddedString 2 (Time.toMillis utc time)
++ "Z"


toPaddedString digits time =
String.padLeft digits '0' (String.fromInt time)


fromMonth : Time.Month -> String
fromMonth month =
case month of
Jan ->
"01"

Feb ->
"02"

Mar ->
"03"

Apr ->
"04"

May ->
"05"

Jun ->
"06"

Jul ->
"07"

Aug ->
"08"

Sep ->
"09"

Oct ->
"10"

Nov ->
"11"

Dec ->
"12"

0 comments on commit 9db469d

Please sign in to comment.