justinmimbs / timezone-data

An Elm package containing time zone data from the IANA Time Zone Database
https://package.elm-lang.org/packages/justinmimbs/timezone-data/latest/
BSD 3-Clause "New" or "Revised" License
29 stars 5 forks source link

[Feature Request] Add enoder and decoder for Zone type #2

Closed MM-MikeSauce closed 5 years ago

MM-MikeSauce commented 5 years ago

Right now, it's easy enough to get the Zone from a string using the zones Dict. However, what isn't as easy is encoding the Zone back into a string.

It would be nice to have an encoder that did this. And since there would be an encoder, it would make sense to also have a decoder.

MM-MikeSauce commented 5 years ago

Here is the decoder I am currently using in my project:

decodeZone : Decoder Zone
decodeZone =
    Decode.string
        |> Decode.andThen
            (\string ->
                Dict.get string TimeZone.zones
                    |> Maybe.map (\zone -> zone ())
                    |> Maybe.map Decode.succeed
                    |> Maybe.withDefault (Decode.fail (string ++ " is not a valid time zone."))
            )

Not sure how to write an encoder though.

justinmimbs commented 5 years ago

Hi Michael,

Since you're decoding a Zone from just its name, then I imagine you want to encode just its name as well.

You'll just want your decoder to return the name too:

decodeZone : Decoder ( String, Zone )
decodeZone =
    Decode.string
        |> Decode.andThen
            (\string ->
                case Dict.get string TimeZone.zones of
                    Just zone ->
                        Decode.succeed ( string, zone () )

                    Nothing ->
                        Decode.fail (string ++ " is not a valid time zone.")
            )

And then store the name with the Zone, so that later you can encode just the name using Json.Encode.string.

Does this help?

Justin

MM-MikeSauce commented 5 years ago

Hmm, that would work just fine. However, I don't particularly like the idea of changing my model for the Zone to a Tuple. I like how clean my code is with only passing the Zone around.

It's a nice idea if I cannot get an encoder written, so I may fall back on that solution. I appreciate the idea.

Just to maybe spawn some thoughts, I'm sharing what I have so far as a potential encoder:

encodeZone : Zone -> Encode.Value
encodeZone timeZone =
    let
        zoneString =
            Dict.filter (\zoneName zone -> zone () == timeZone) TimeZone.zones
                |> Dict.toList
                |> List.head
                |> Maybe.map (\( zoneName, zone ) -> zoneName)
                |> Maybe.withDefault ""
    in
    Encode.string zoneString

I'm not going to lie, this isn't a valid solution in my opinion. Ideally, it should never reach the Maybe.withDefault state here. And this is also assuming that filtering the Dict would produce exactly one result.

I'm working on refining this idea further.

justinmimbs commented 5 years ago

I agree; I would not use that version of encodeZone.

You don't have to store the name and Zone together in a tuple. You just have to store them both somewhere in your model. You can still pass just the Zone around, and when it comes time to encode, you only need the name, not the Zone. 😃

MM-MikeSauce commented 5 years ago

Fair point, I guess I'm worried they could get out of sync. Don't want to introduce the possibility for an impossible state!

justinmimbs commented 5 years ago

Right on; I can appreciate that!

I'm going to close this for now, as while the purpose of this library is to enable things like the decoder above, I don't know if it should provide it. Thanks for the feature suggestion and for sharing how you're using the library. 👍