This is an alternative site for discovering Elm packages. You may be looking for the official Elm package site instead.

Date.Extra.Field

Setting a date field on a date.

fieldToDate : Field -> Date -> Maybe Date

Set a field on a date to a specific value. If your value in field is out side of valid range for the date field this function will return Nothing.

  • DayOfWeek cannot be invalid input range
  • Month cannot be invalid Valid ranges
  • Millisecond 0 to 999
  • Second 0 to 59
  • Minute 0 to 59
  • Hour 0 to 23
  • DayOfMonth 1 to max day of month for year
  • Year >= 0
fieldToDateClamp : Field -> Date -> Date

Set a field on a date to a specific value. This version clamps any input Field values to valid ranges as described in the doc for fieldToDate function.

type Field = Millisecond Int | Second Int | Minute Int | Hour Int | DayOfWeek (Day, Day) -- (Target Day, Start of Weekday) | DayOfMonth Int | Month Month | Year Int

Configured Field and Value to set on date. All field values are applied Modulus there maximum value.

  • DayOfWeek
    • The week keeps the same start of week day as passed in and changes day.
  • Month
    • Will not change year only the month of year.
module Date.Extra.Field exposing
  ( fieldToDate
  , fieldToDateClamp
  , Field (..)
  )

{-| Setting a date field on a date.

@docs fieldToDate
@docs fieldToDateClamp
@docs Field

-}

import Date exposing (Date, Day, Month)

import Date.Extra.Core as Core
import Date.Extra.Duration as Duration


{-| Configured Field and Value to set on date.

All field values are applied Modulus there maximum value.


* DayOfWeek
 * The week keeps the same start of week day as passed in and changes day.
* Month
 * Will not change year only the month of year.

-}
type Field
  = Millisecond Int
  | Second Int
  | Minute Int
  | Hour Int
  | DayOfWeek (Day, Day) -- (Target Day, Start of Weekday)
  | DayOfMonth Int
  | Month Month
  | Year Int


{-| Set a field on a date to a specific value.

If your value in field is out side of valid range for
the date field this function will return Nothing.

* DayOfWeek cannot be invalid input range
* Month cannot be invalid

Valid ranges
* Millisecond 0 to 999
* Second 0 to 59
* Minute 0 to 59
* Hour 0 to 23
* DayOfMonth 1 to max day of month for year
* Year >= 0

-}
fieldToDate : Field -> Date -> Maybe Date
fieldToDate field date =
  case field of
    Millisecond millisecond ->
      if millisecond < 0 || millisecond > 999 then
        Nothing
      else
        Just <| Duration.add Duration.Millisecond (millisecond - Date.millisecond date) date

    Second second ->
      if second < 0 || second > 59 then
        Nothing
      else
        Just <| Duration.add Duration.Second (second - Date.second date) date

    Minute minute ->
      if minute < 0 || minute > 59 then
        Nothing
      else
        Just <| Duration.add Duration.Minute (minute - Date.minute date) date

    Hour hour ->
      if hour < 0 || hour > 23 then
        Nothing
      else
        Just <| Duration.add Duration.Hour (hour - Date.hour date) date

    -- Inputs can't be out of range so Nothing is never returned
    DayOfWeek (newDayOfWeek, startOfWeekDay) -> -- Just date
      Just <| dayOfWeekToDate newDayOfWeek startOfWeekDay date

    DayOfMonth day ->
      let
        maxDays = Core.daysInMonthDate date
      in
        if day < 1 || day > maxDays then
          Nothing
        else
          Just <| Duration.add Duration.Day (day - Date.day date) date

    -- Inputs can't be out of range so Nothing is never returned.
    Month month ->
      Just <| monthToDate month date

    Year year ->
      if year < 0 then
        Nothing
      else
        Just <| Duration.add Duration.Year (year - (Date.year date)) date


monthToDate month date =
  let
    targetMonthInt = Core.monthToInt month
    monthInt = Core.monthToInt (Date.month date)
  in
    Duration.add Duration.Month (targetMonthInt - monthInt) date


dayOfWeekToDate newDayOfWeek startOfWeekDay date =
  let
    dayOfWeek = Date.dayOfWeek date
    daysToStartOfWeek = Core.daysBackToStartOfWeek dayOfWeek startOfWeekDay
    targetIsoDay = Core.isoDayOfWeek newDayOfWeek
    isoDay = Core.isoDayOfWeek dayOfWeek
    dayDiff = targetIsoDay - isoDay
    -- _ = Debug.log("dayOfWeekToDate") (daysToStartOfWeek, dayDiff)
    adjustedDiff =
      if (daysToStartOfWeek + dayDiff) < 0 then
        dayDiff + 7
      else
        dayDiff
  in
    Duration.add Duration.Day adjustedDiff date


{-|  Set a field on a date to a specific value.

This version clamps any input Field values to valid ranges as
described in the doc for fieldToDate function.
-}
fieldToDateClamp : Field -> Date -> Date
fieldToDateClamp field date =
  case field of
    Millisecond millisecond ->
      Duration.add Duration.Millisecond (clamp 0 999 millisecond - Date.millisecond date) date

    Second second ->
      Duration.add Duration.Second (clamp 0 59 second - Date.second date) date

    Minute minute ->
      Duration.add Duration.Minute (clamp 0 59 minute - Date.minute date) date

    Hour hour ->
      Duration.add Duration.Hour (clamp 0 23 hour - Date.hour date) date

    DayOfWeek (newDayOfWeek, startOfWeekDay) ->
      dayOfWeekToDate newDayOfWeek startOfWeekDay date

    DayOfMonth day ->
      let
        maxDays = Core.daysInMonthDate date
      in
        Duration.add Duration.Day (clamp 1 maxDays day - Date.day date) date

    Month month ->
      monthToDate month date

    Year year ->
      let
        minYear = if year < 0 then 0 else year
      in
        Duration.add Duration.Year (minYear - (Date.year date)) date