Code Monkey home page Code Monkey logo

purescript-datetime's Introduction

PureScript

A small strongly typed programming language with expressive types that compiles to JavaScript, written in and inspired by Haskell.

Hackage Build Status

Language info

Resources

Help!

Community Spaces

The following spaces are governed by the PureScript Community Code of Conduct. The majority of PureScript users use these spaces to discuss and collaborate on PureScript-related topics:

Unaffiliated Spaces

Some PureScript users also collaborate in the below spaces. These do not fall under the code of conduct linked above. They may have no code of conduct or one very different than the one linked above.

purescript-datetime's People

Contributors

anilanar avatar antoine-fl avatar bkyrlach avatar bouzuya avatar chexxor avatar garyb avatar hdgarrood avatar i-am-the-slime avatar javcasas avatar jdegoes avatar jordanmartinez avatar kika avatar kl0tl avatar kritzcreek avatar liamgoodacre avatar marcopullo avatar michaelxavier avatar milesfrain avatar monoidmusician avatar mschristiansen avatar nwolverson avatar paf31 avatar parsonsmatt avatar philopon avatar safareli avatar tfausak avatar thomashoneyman avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

purescript-datetime's Issues

Week number of the Year

Hi, thank you for such a great lib.

I'm trying to figure out a way to get the week of the year.

Is there a way to accomplish this with the library?

Addjust by Month, Year

adjust now can modify a date by the number of Days, adjusting by Months and Years is not actually the same as adding some easily calculable number of days. Should it be included in the package?

Overflow with Int and milliseconds

Using this with PS 0.7. Millisecond values, e.g. from toEpochMilliseconds of today overflows Int and ends up -ve. Particularly an issue as this is used under the hood and not just this conversion.

(See also new Date().getTime() | 0)

Add a saturating `adjust` variant?

While writing an app with lots of DateTime manipulations, I noticed that adjust is very annoying to use, because of the Maybe return type. I don't think this is a very practical design, similarly to how (+) :: Int -> Int -> Maybe Int wouldn't be. There is often not much you can do on failure, other than just skip the operation. Needless to say, that doesn't help with correctness.

I think that saturating semantics would make more sense. Something like this:

-- | Like `adjust`, but saturates on out-of-bounds results.
shift :: forall d. Duration d => d -> DateTime -> DateTime
shift d dt = case adjust d dt of
    Nothing -> if fromDuration d > mempty then top else bottom
    Just d' -> d'

Does this sound like a good idea? I can make it into a PR, but wanted to get some feedback first.

Some questions regarding durations

  1. There is Data.Time.Duration and also Data.Interval.Duration and I wonder what the difference is. Data.Time.Duration is used for functions like adjust and diff, while Data.Interval.Duration seems to be mostly about parsing and formatting of ISO8601 durations? Is one of them deprecated? Is there a conceptual difference that I fail to see?

  2. Is there a way to format durations in a human-readable fashion? E.g. neither "643257 milliseconds" nor "0,051 days" but in a multi-unit format like "2 days, 15h, 45min"? (this is probably related to purescript formatters, but the calculation might belong to this package)

  3. I realized that the documentation in Pursuit is not up-to-date (the latest version is 3.2.0). Could somebody please update it?

Can't build this with current `purs`: 3 compiler errors related to `Data.Generic.Generic`

Using current purs master branch (prints as "v0.11.6 dev build"), this build error when simply rebuilding a project where I did just bower install purescript-datetime (it picked 3.4.0 btw) prior, not even importing or using this lib anywhere:

[00:00 _ ~/c/ps/gonad-demos]$ pulp build -- --dump-corefn
* Building project in ~/c/ps/gonad-demos
Compiling Data.Time.Component
Compiling Data.Date.Component
Compiling Data.Time.Duration
Error 1 of 3:

  at bower_components/purescript-datetime/src/Data/Date/Component.purs line 23, column 8 - line 23, column 44
    
    Cannot derive a type class instance for
                               
      Data.Generic.Generic Year
                               
    since instances of this type class are not derivable.


  See https://github.com/purescript/documentation/blob/master/errors/CannotDerive.md for more information,
  or to contribute content related to this error.

Error 2 of 3:

  at bower_components/purescript-datetime/src/Data/Time/Component.purs line 24, column 8 - line 24, column 44

    Cannot derive a type class instance for
                                   
      Data.Generic.Generic Hour
                               
    since instances of this type class are not derivable.


  See https://github.com/purescript/documentation/blob/master/errors/CannotDerive.md for more information,
  or to contribute content related to this error.

Error 3 of 3:

  at bower_components/purescript-datetime/src/Data/Time/Duration.purs line 12, column 8 - line 12, column 60

    Cannot derive a type class instance for
                                       
      Data.Generic.Generic Milliseconds
                                       
    since instances of this type class are not derivable.


  See https://github.com/purescript/documentation/blob/master/errors/CannotDerive.md for more information,
  or to contribute content related to this error.


* ERROR: Subcommand terminated with exit code 1

Any ideas? Is the version Bower picks already "legacy"?

add binding for toISOString

It would be great to be able to render a Date as an ISO 8601 string! I'll probably submit a patch shortly, as I've inlined this in another project.

IsForeign instance for dates

What's the current thinking on how parsing dates should be done? I noticed some of the fromString functions have disappeared in the rewrite, and I'd like to be able to have an IsForeign instance for dates.

ISOString class proposal

All the types using JS Date internally could use default* functions proxying to Date.parse and Date.prototype.toISOString respectively:

class ISOString a where
  fromISOString :: a -> String
  toISOString :: String -> a

Incorrect duration between months intervals

Hi there,

In the following code, nDays evaluates to (Days 31.0), I would expect it to evaluate to (Days 28.0)


dStart = unsafePartial $ canonicalDate (fromJust $ toEnum 2012) February (fromJust $ toEnum 1)
dStop = unsafePartial $ canonicalDate (fromJust $ toEnum 2012) March (fromJust $ toEnum 1)

nDays :: Days
nDays = toDuration $ diff dStop dStart

In the following code, nDays evaluates to (Days 29.0), I would expect it to evaluate to (Days 31.0)


dStart = unsafePartial $ canonicalDate (fromJust $ toEnum 2012) January (fromJust $ toEnum 1)
dStop = unsafePartial $ canonicalDate (fromJust $ toEnum 2012) February (fromJust $ toEnum 1)

nDays :: Days
nDays = toDuration $ diff dStop dStart

Non FFI version of purescript-datetime

After a brief chat with @garyb here, it was mentioned that ideally purescript-datetime could actually be 100% pure Purescript.

Would the idea be to reproduce these specs here?

As a starting point I assume I'd need to replace:

var createDate = function (y, m, d) {
  var date = new Date(Date.UTC(y, m, d));
  if (y >= 0 && y < 100) {
    date.setUTCFullYear(y);
  }
  return date;
};

So there would be no leaning on the use of JS's implementation?
This is assuming I'm not barking up the wrong tree, if that is the case what might one need to look at?

edit: I'm also looking at Haskell's Date.Time for guidance.

Add a convenient way to get the `Millisecond`s between two `Instant`s

This is a use case I have often when timing things.
I would like to avoid running unInstant in application code.

Maybe like this:

-- | Get the amount of milliseconds between the first and second Instant
-- | for example:
-- | ```
-- | do
-- |   start <- Instant.now
-- |   aLongRunningEffect
-- |   end <- Instant.now
-- |   let duration = Instant.delta end start
-- |   log ("A long running effect took " <> show duration)
-- | ```
delta  Instant  Instant  Milliseconds
delta i1 i2 = (unInstant i1) <> negateDuration (unInstant i2)

add Traversable instance for LocalValue

I want to be able to write, for instance:

now <- nowDateTime
let now' = (traverse (adjust $ Minutes 3.0) now)

where now' is a Maybe LocalDateTime.

Using the functor instance, e.g., map (adjust $ Minutes 3.0) now gives me a LocalValue (Maybe DateTime) which isn't so useful.

Date type without Time component

Hey,

Often I'm in need of a Date type without a Time component. So far I've been making my own, I call it CalendarDate, that's just:

data CalendarDate = CalendarDate Year Month DayOfMonth

I'm wondering, do you think this would be a good idea to incorporate in the core library?

How to format a DateTime object into a string

How can a DateTime object from this library be printed to a string using a specific format? Does this need to be done manually or with another library? For example in MomentJS there is the format function.

Purpose of Locale?

I can't find the explanation of Locale. Am I missing it? Based on my interpretation from reading code, it seems to be fundamentally flawed. Is there plans to move this towards the ThreeTen model of "Locale"?

Current explanation of Locale:

-- | The name of a date/time locale. For example: "GMT", "MDT", "CET", etc.
newtype LocaleName = LocaleName String
data Locale = Locale (Maybe LocaleName) Minutes
data LocalValue a = LocalValue Locale a
type LocalDateTime = LocalValue DateTime

My problem with this is that timezone names (e.g. "GMT", "MDT", "CET") are a poor way of assigning timzone-offsets to a DateTime value. If the DateTime in England is in Summer, then "BST" offset applies, but if it's in Winter, then "GMT" offset applies. So, it makes more sense to join a DateTime with a representative-city than with a timezone. Actually rendering the DateTime with the offset considered, then, requires a map from representative-city to time-offset for that DateTime's specific value. (This map can be quite large, as seen in MomentJS's timezone file.)

The ThreeTen library follows this concept of using representative-city to specify a DateTime's offset, but it calls it "ZoneId". The ZonedDateTime explains a bit further.

proper Ord instances for Date / Time / DateTime

The Ord instance is derived for Time, Date, and DateTime, which doesn't represent their intuitive ordering. Is this just something that never got done, or is there a reason for this? It would be far more useful to have the ordering cascade from year -> month -> day -> hour -> minute -> second -> milliseconds.

I could write this if it's desirable. I'm going to have to write a true lessThan for my own purposes anyway.

addDays

Would there be interest in a PR for an addDays :: Int -> Date -> Date function or same functionality under different name?

Dates before the year 1901 are not handled correctly by `canonicalDate` and `exactDate`

The Year type faithfully represents all the years from -271820 to 275759, but passing, e.g., toEnum 1 into canonicalDate produces a date in the year 1901, not the year 1 as one would expect.

Example code:

show <<< year $ canonicalDate (fromMaybe bottom $ toEnum 1) January bottom

...produces "(Year 1901)", instead of "(Year 1)".

The culprit seems to be on the JavaScript side of things; while the Date type can represent dates like '0001-01-01', creating them the way canonicalDate does, which is new Date(Date.UTC(y, m - 1, d)), "helpfully" "fixes" the year number to after 1900.

Issues with getting dates out of strings

I am aware of two problems with fromString:

  1. Date.parse can depend on the current system timezone; for ISO formats, it's UTC, but for other formats, it uses the system timezone. However the type of fromString is String -> Maybe Date. I think it should be forall eff. String -> Eff (locale :: Locale | eff) (Maybe Date).
  2. Date.parse is probably not what you want to use in most cases, and its behaviour can be quite surprising (see http://ircbrowse.net/browse/purescript?id=301057&timestamp=1460055027#t1460055027 and the following page). In fact the MDN page that the docs link to explicitly discourages its use. Therefore I don't think we should be suggesting it as the default choice (as we currently do by calling it fromString and the other one fromStringStrict). Can we switch these around so that fromStringStrict becomes fromString, and fromString gets some other name?

DateTime library redesign

Here's my thinking so far.

-- Quantities
-- Using `Number` to allow fractional values, but also larger ranges
newtype Hours = Hours Number
newtype Minutes = Minutes Number
newtype Seconds = Seconds Number
newtype Milliseconds = Milliseconds Number

-- Date parts
newtype Year = Year Int
data Month = January | February | March ...
newtype Day = Day Int
data Weekday = Monday | Tuesday | ...

-- Time parts
newtype Hour = Hour Int
newtype Minute = Minute Int
newtype Second = Second Int
newtype Millisecond = Millisecond Int
data DayPart = AM | PM
data Hour12 = Hour12 Hour DayPart

-- UTC dates. Most manipulation operations will only operate on these.
data Date = Date Year Month Day
data Time = Time Hour Minute Second Millisecond
data DateTime = DateTime (Date a) (Time a)

-- Locale-offset values. Constructed from UTC values with an offset, probably
-- only usable for rendering... there may be a limited set of operations that
-- work also.
--
-- `LocalValue` is a Functor so the inner value can be manipulated without
-- explicit unwrapping/rewrapping
--
-- `LocaleName` is for things like "GMT", "MDT", "CET", etc.
newtype LocaleName = LocaleName String
data Locale = Locale (Maybe LocaleName) Minutes
data LocalValue a = LocalValue Locale a
type LocalDate = LocaleValue Date
type LocalTime = LocaleValue Time
type LocalDateTime = LocaleValue DateTime
  • Dates will only be constructable via smart constructors, converted from quantities, or by parsing.
  • When creating a date from a quantity, it will be treated as relative to the unix epoch.
  • We'll only provide a handful of common formats for parsing from in the library, a format string language could be provided in another library if we ever wanted it. I tend to prefer real parsers anyway. 😉
  • Any format we can parse we can also render from a date.
  • Alternatively, maybe we don't do any parsing/pretty printing of dates in this library, and handle it all in a separate place. That has a certain appeal too as it will reduce the number of required dependencies to use dates.

There will be some kind of TimeSpan quantity constructed from DateTimes as well as the numeric quantities, but I haven't fully thought out what additional operations a TimeSpan might have relative to the other quantities. It will probably be possible to construct a TimeSpan from other combinations of values too, two Times only, two Dates only, another quantity, etc. just that it will use DateTime as its internal representation.

We'll have more options for manipulating dates with this formulation too, as we will be able to add or subtract quantities to dates without having to deconstruct and reconstruct them manually.

The main hurdle at the moment that needs figuring out now is an algorithm for converting DateTimes to and from the unix epoch. I didn't have a great amount of success trying to find suitable algorithms for it on the web when I last looked.

Anyway, there are the things I've been kicking around while thinking about where to take this, but I'm definitely open to changing things. The one thing I don't think we can forgo is the unix epoch conversions though, as that's basically our interface to JS dates. It's a generally a handy representation too when you need to squash it into a single portable value of some kind.

/cc @hdgarrood @FrigoEU

Example usage missing in docs

Even a single example of how to construct the main data type in a module would be really helpful.
For example in Data.Time, you need a lot of understanding and experience to figure out that you're supposed to do something like:

import Data.Enum (toEnum)
myTime2 :: Maybe Time
myTime2 = Time <$> toEnum 11 <*> toEnum 30 <*> toEnum 59 <*> toEnum 213 

I didn't have that experience... (Helpful people on #purescript-beginners Slack suggested the above.)
Would you consider adding that kind of example to the docs?

Non-conforming effect names

The PureScript convention for effects names appears to be SCREAMING_SNAKE_CASE. The Locale and Now effects should be renamed.

Perhaps it’s worth considering whether Now should be renamed to something different than NOW. If LOCALE is the effect of reading the current system locale/timezone, then maybe we should go with CLOCK?

Enum instances for seconds/minutes/milliseconds

Right now there are no instances of Enum for the time intervals? Is this by design, or just something that has not yet been implemented? If has not been implemented yet I would be happy to put something together and submit a pull request.

Edit: Actually it looks like there are a few missing instances.

The reason I'm looking for instances of Enum is the fromEnum method of the typeclass, as I'm trying to generate a string representation.

Switch to module over ring for duration arithmetic

Currently a Semiring Milliseconds instance exists. However, durations are not a semiring: the closure property of the multiplication operation is not honoured because multiplying two durations gives not a duration but a duration squared.

This is incorrect, and causes awkward code where you need to add/remove nonsensical wrappers just so you can multiply or divide. One example is multiplying a duration by a scalar:

twiceAsLong :: Milliseconds -> Milliseconds
twiceAsLong a = a * Milliseconds 2.0 -- nasty wrap

Another example is where you divide two durations when implementing a multiplication between durations and frequencies where the frequency type is in terms of the reciprocal of the frequency, which is a duration:

newtype Freq = Freq Milliseconds

-- | The reciprocal of a frequency is a duration. (Note how 1/ms = kHz.)
reciprocal :: Freq -> Milliseconds
reciprocal (Freq r) = r

-- | Multiplying a duration (ms) by a frequency (1/ms) gives a number.
multiply :: Milliseconds -> Freq -> Number
multiply d f = un Milliseconds (d / reciprocal f) -- nasty unwrap

With the right abstraction, no wrapping and unwrapping would be necessary, because the types would just work out. We should remove the Semiring Milliseconds instance (and the instances for other duration types) and switch to modules over rings instead, which model this correctly. To add division we should make a library for vector spaces over fields.

Modules over rings and vector spaces over fields are generally useful for types that represent quantities, such as durations, distances, and file sizes.

Type change?

So I implemented the Enums for WeekDay and Month last night in the Moment bindings. It would be nice to ditch that code and pull in this lib instead. However there is one type mismatch there.

Milliseconds in this lib is *, and in mine its * -> * wrapping Number instead of aliasing it. I feel there is value there because the meaning of the number can be type checked. IE (Days 5) does not match (Milliseconds 5).

Would you consider adopting this pattern for Hours Minutes Seconds Milliseconds and Epoch?

If so I can just move my code over after I finish the instances for the Duration type

Add constructors for Date / Time components using type-level integers

With the advent of 0.15.0 and type-level integers, we can now define Date and Time components without using unsafePartial:

hour
  :: forall i
   . Reflectable i Int
  => PI.Compare i (-1) PO.GT
  => PI.Compare i 24 PO.LT
  => Proxy i
  -> Hour
hour p = Hour $ reflectType p

minute
  :: forall i
   . Reflectable i Int
  => PI.Compare i (-1) PO.GT
  => PI.Compare i 60 PO.LT
  => Proxy i
  -> Minute
minute p = Minute $ reflectType p

second
  :: forall i
   . Reflectable i Int
  => PI.Compare i (-1) PO.GT
  => PI.Compare i 60 PO.LT
  => Proxy i
  -> Second
second p = Second $ reflectType p

millisecond
  :: forall i
   . Reflectable i Int
  => PI.Compare i (-1) PO.GT
  => PI.Compare i 1_000 PO.LT
  => Proxy i
  -> Millisecond
millisecond p = Millisecond $ reflectType p

year
  :: forall i
   . Reflectable i Int
  => PI.Compare i (-271821) PO.GT
  => PI.Compare i 275760 PO.LT
  => Proxy i
  -> Year
year p = Year $ reflectType p

month
  :: forall i
   . Reflectable i Int
  => PI.Compare i 0 PO.GT
  => PI.Compare i 13 PO.LT
  => Proxy i
  -> Month
month p = unsafePartial $ fromJust $ toEnum $ reflectType p

day
  :: forall i
   . Reflectable i Int
  => PI.Compare i 0 PO.GT
  => PI.Compare i 32 PO.LT
  => Proxy i
  -> Day
day p = Day $ reflectType p

I would like to add a similar constructor for DateTime based on exactDate, but that requires verifying at the type-level that the day is within the bounds for the month-year combination. If we wanted to lift the isLeapYear function to the type-level, it seems we'll need a Prim.Int (class Mod) type class.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.