diff --git a/.gitignore b/.gitignore
new file mode 100644
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,17 @@
+dist
+cabal-dev
+*.o
+*.hi
+*.chi
+*.chs.h
+*.dyn_o
+*.dyn_hi
+.hpc
+.hsenv
+.cabal-sandbox/
+cabal.sandbox.config
+*.prof
+*.aux
+*.hp
+.stack-work/
+*~
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,88 @@
+# This file has been generated -- see https://github.com/hvr/multi-ghc-travis
+language: c
+sudo: false
+
+cache:
+  directories:
+    - $HOME/.cabsnap
+    - $HOME/.cabal/packages
+
+before_cache:
+  - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log
+  - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.tar
+
+matrix:
+  include:
+    - env: CABALVER=1.18 GHCVER=7.8.3
+      compiler: ": #GHC 7.8.3"
+      addons: {apt: {packages: [cabal-install-1.18,ghc-7.8.3], sources: [hvr-ghc]}}
+    - env: CABALVER=1.18 GHCVER=7.8.4
+      compiler: ": #GHC 7.8.4"
+      addons: {apt: {packages: [cabal-install-1.18,ghc-7.8.4], sources: [hvr-ghc]}}
+    - env: CABALVER=1.22 GHCVER=7.10.1
+      compiler: ": #GHC 7.10.1"
+      addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.1], sources: [hvr-ghc]}}
+    - env: CABALVER=1.22 GHCVER=7.10.2
+      compiler: ": #GHC 7.10.2"
+      addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.2], sources: [hvr-ghc]}}
+    - env: CABALVER=1.22 GHCVER=7.10.3
+      compiler: ": #GHC 7.10.3"
+      addons: {apt: {packages: [cabal-install-1.22,ghc-7.10.3], sources: [hvr-ghc]}}
+
+before_install:
+ - unset CC
+ - export PATH=/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH
+
+install:
+ - cabal --version
+ - echo "$(ghc --version) [$(ghc --print-project-git-commit-id 2> /dev/null || echo '?')]"
+ - if [ -f $HOME/.cabal/packages/hackage.haskell.org/00-index.tar.gz ];
+   then
+     zcat $HOME/.cabal/packages/hackage.haskell.org/00-index.tar.gz >
+          $HOME/.cabal/packages/hackage.haskell.org/00-index.tar;
+   fi
+ - travis_retry cabal update -v
+ - sed -i 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config
+ - cabal install --only-dependencies --enable-tests --enable-benchmarks --dry -v > installplan.txt
+ - sed -i -e '1,/^Resolving /d' installplan.txt; cat installplan.txt
+
+# check whether current requested install-plan matches cached package-db snapshot
+ - if diff -u installplan.txt $HOME/.cabsnap/installplan.txt;
+   then
+     echo "cabal build-cache HIT";
+     rm -rfv .ghc;
+     cp -a $HOME/.cabsnap/ghc $HOME/.ghc;
+     cp -a $HOME/.cabsnap/lib $HOME/.cabsnap/share $HOME/.cabsnap/bin $HOME/.cabal/;
+   else
+     echo "cabal build-cache MISS";
+     rm -rf $HOME/.cabsnap;
+     mkdir -p $HOME/.ghc $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin;
+     cabal install --only-dependencies --enable-tests --enable-benchmarks;
+   fi
+
+# snapshot package-db on cache miss
+ - if [ ! -d $HOME/.cabsnap ];
+   then
+      echo "snapshotting package-db to build-cache";
+      mkdir $HOME/.cabsnap;
+      cp -a $HOME/.ghc $HOME/.cabsnap/ghc;
+      cp -a $HOME/.cabal/lib $HOME/.cabal/share $HOME/.cabal/bin installplan.txt $HOME/.cabsnap/;
+   fi
+
+# Here starts the actual work to be performed for the package under test;
+# any command which exits with a non-zero exit code causes the build to fail.
+script:
+ - if [ -f configure.ac ]; then autoreconf -i; fi
+ - cabal configure --enable-tests --enable-benchmarks -v2  # -v2 provides useful information for debugging
+ - cabal build   # this builds all libraries and executables (including tests/benchmarks)
+ - cabal test
+ - cabal check
+ - cabal sdist   # tests that a source-distribution can be generated
+
+# Check that the resulting source distribution can be built & installed.
+# If there are no other `.tar.gz` files in `dist`, this can be even simpler:
+# `cabal install --force-reinstalls dist/*-*.tar.gz`
+ - SRC_TGZ=$(cabal info . | awk '{print $2;exit}').tar.gz &&
+   (cd dist && cabal install --force-reinstalls "$SRC_TGZ")
+
+# EOF
diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.markdown
diff --git a/Data/DotNet/TimeSpan.hs b/Data/DotNet/TimeSpan.hs
new file mode 100644
--- /dev/null
+++ b/Data/DotNet/TimeSpan.hs
@@ -0,0 +1,37 @@
+--------------------------------------------------------------------------------
+-- |
+-- Module : Data.DotNet.TimeSpan
+-- Copyright : (C) 2016 Yorick Laupa
+-- License : (see the file LICENSE)
+--
+-- Maintainer : Yorick Laupa <yo.eight@gmail.com>
+-- Stability : provisional
+-- Portability : non-portable
+--
+-- .NET TimeSpan implemented in Haskell.
+--------------------------------------------------------------------------------
+module Data.DotNet.TimeSpan
+    ( TimeSpan
+    , timeSpanTicks
+    , timeSpanHoursMinsSecs
+    , timeSpanDaysHoursMinsSecs
+    , timeSpanDaysHoursMinsSecsMillis
+    , ticks
+    , days
+    , hours
+    , minutes
+    , seconds
+    , millis
+    , fromSeconds
+    , fromMinutes
+    , fromHours
+    , fromDays
+    , totalDays
+    , totalHours
+    , totalMinutes
+    , totalSeconds
+    , totalMillis
+    ) where
+
+--------------------------------------------------------------------------------
+import Data.DotNet.TimeSpan.Internal
diff --git a/Data/DotNet/TimeSpan/Internal.hs b/Data/DotNet/TimeSpan/Internal.hs
new file mode 100644
--- /dev/null
+++ b/Data/DotNet/TimeSpan/Internal.hs
@@ -0,0 +1,312 @@
+--------------------------------------------------------------------------------
+-- |
+-- Module : Data.DotNet.TimeSpan.Internal
+-- Copyright : (C) 2016 Yorick Laupa
+-- License : (see the file LICENSE)
+--
+-- Maintainer : Yorick Laupa <yo.eight@gmail.com>
+-- Stability : provisional
+-- Portability : non-portable
+--
+-- .NET TimeSpan implemented in Haskell.
+--------------------------------------------------------------------------------
+module Data.DotNet.TimeSpan.Internal where
+
+--------------------------------------------------------------------------------
+import Data.Int
+import Data.Monoid
+import Prelude
+
+--------------------------------------------------------------------------------
+-- | .NET TimeSpan: Represents a time interval.
+newtype TimeSpan = TimeSpan Int64 deriving (Eq, Ord)
+
+--------------------------------------------------------------------------------
+instance Show TimeSpan where
+    show = timeSpanString
+
+--------------------------------------------------------------------------------
+millisPerSecond :: Int64
+millisPerSecond = 1000
+
+--------------------------------------------------------------------------------
+millisPerMinute :: Int64
+millisPerMinute = millisPerSecond * 60
+
+--------------------------------------------------------------------------------
+millisPerHour :: Int64
+millisPerHour = millisPerMinute * 60
+
+--------------------------------------------------------------------------------
+millisPerDay :: Int64
+millisPerDay = millisPerHour * 24
+
+--------------------------------------------------------------------------------
+ticksPerMillisecond :: Int64
+ticksPerMillisecond = 10000
+
+--------------------------------------------------------------------------------
+ticksPerSecond :: Int64
+ticksPerSecond = ticksPerMillisecond * 1000
+
+--------------------------------------------------------------------------------
+ticksPerMinute :: Int64
+ticksPerMinute = ticksPerSecond * 60
+
+--------------------------------------------------------------------------------
+ticksPerHour :: Int64
+ticksPerHour = ticksPerMinute * 60
+
+--------------------------------------------------------------------------------
+ticksPerDay :: Int64
+ticksPerDay = ticksPerHour * 24
+
+--------------------------------------------------------------------------------
+daysPerTick :: Double
+daysPerTick = 1 / (realToFrac ticksPerDay)
+
+--------------------------------------------------------------------------------
+hoursPerTick :: Double
+hoursPerTick = 1 / (realToFrac ticksPerHour)
+
+--------------------------------------------------------------------------------
+minutesPerTick :: Double
+minutesPerTick = 1 / (realToFrac ticksPerMinute)
+
+--------------------------------------------------------------------------------
+secondsPerTick :: Double
+secondsPerTick = 1 / (realToFrac ticksPerSecond)
+
+--------------------------------------------------------------------------------
+millisPerTick :: Double
+millisPerTick = 1 / (realToFrac ticksPerMillisecond)
+
+--------------------------------------------------------------------------------
+maxMillis :: Int64
+maxMillis =
+    truncate
+    (((realToFrac (maxBound :: Int64) :: Double)
+      / realToFrac ticksPerMillisecond) :: Double)
+
+--------------------------------------------------------------------------------
+minMillis :: Int64
+minMillis =
+    truncate
+    (((realToFrac (minBound :: Int64) :: Double)
+      / realToFrac ticksPerMillisecond) :: Double)
+
+--------------------------------------------------------------------------------
+-- | Initializes a new instance of the TimeSpan structure to the specified
+--   number of ticks.
+timeSpanTicks :: Int64 -> TimeSpan
+timeSpanTicks = TimeSpan
+
+--------------------------------------------------------------------------------
+-- | Initializes a new instance of the TimeSpan structure to a specified number
+--   of hours, minutes, and seconds.
+timeSpanHoursMinsSecs :: Int64 -> Int64 -> Int64 -> TimeSpan
+timeSpanHoursMinsSecs hh mm ss = TimeSpan $ totalSecs * ticksPerSecond
+  where
+    totalSecs = (hh * 3600) + (mm * 60) + ss
+
+--------------------------------------------------------------------------------
+-- | Initializes a new instance of the TimeSpan structure to a specified number
+--   of days, hours, minutes, and seconds.
+timeSpanDaysHoursMinsSecs :: Int64 -> Int64 -> Int64 -> Int64 -> TimeSpan
+timeSpanDaysHoursMinsSecs dd hh mm ss =
+    timeSpanDaysHoursMinsSecsMillis dd hh mm ss 0
+
+--------------------------------------------------------------------------------
+-- | Initializes a new instance of the TimeSpan structure to a specified number
+--   of days, hours, minutes, seconds, and milliseconds.
+timeSpanDaysHoursMinsSecsMillis :: Int64
+                                -> Int64
+                                -> Int64
+                                -> Int64
+                                -> Int64
+                                -> TimeSpan
+timeSpanDaysHoursMinsSecsMillis dd hh mm ss ms =
+    TimeSpan $ _totalMillis * ticksPerMillisecond
+  where
+    _totalMillis = ((dd * 3600 * 24) +
+                    (hh * 3600)       +
+                    (mm * 60)         +
+                    ss) * 1000 + ms
+
+--------------------------------------------------------------------------------
+-- | Gets the number of ticks that represent the value of the current 'TimeSpan'
+--   structure.
+ticks :: TimeSpan -> Int64
+ticks (TimeSpan i) = i
+
+--------------------------------------------------------------------------------
+-- | Gets the days component of the time interval represented by the current
+--   'TimeSpan' structure.
+days :: TimeSpan -> Int64
+days (TimeSpan i) = truncate $ (realToFrac i :: Double) /
+                               (realToFrac ticksPerDay)
+
+--------------------------------------------------------------------------------
+-- | Gets the hours component of the time interval represented by the current
+--   'TimeSpan' structure.
+hours :: TimeSpan -> Int64
+hours (TimeSpan i) = mod (truncate $
+                          (realToFrac i :: Double) /
+                          (realToFrac ticksPerHour)) 24
+
+--------------------------------------------------------------------------------
+-- | Gets the minutes component of the time interval represented by the current
+--   'TimeSpan' structure.
+minutes :: TimeSpan -> Int64
+minutes (TimeSpan i) = mod (truncate $
+                            (realToFrac i :: Double) /
+                            (realToFrac ticksPerMinute)) 60
+
+--------------------------------------------------------------------------------
+-- | Gets the seconds component of the time interval represented by the current
+--   'TimeSpan' structure.
+seconds :: TimeSpan -> Int64
+seconds (TimeSpan i) = mod (truncate $
+                            (realToFrac i :: Double) /
+                            (realToFrac ticksPerSecond)) 60
+
+--------------------------------------------------------------------------------
+-- | Gets the milliseconds component of the time interval represented by the
+--   current 'TimeSpan' structure.
+millis :: TimeSpan -> Int64
+millis (TimeSpan i) = mod (truncate $
+                           (realToFrac i :: Double) /
+                           (realToFrac ticksPerMillisecond)) 1000
+
+--------------------------------------------------------------------------------
+-- | Returns a 'TimeSpan' that represents a specified number of seconds, where
+--   the specification is accurate to the nearest millisecond.
+fromSeconds :: Double -> TimeSpan
+fromSeconds i = interval i millisPerSecond
+
+--------------------------------------------------------------------------------
+-- | Returns a 'TimeSpan' that represents a specified number of minutes, where
+--   the specification is accurate to the nearest millisecond.
+fromMinutes :: Double -> TimeSpan
+fromMinutes i = interval i millisPerMinute
+
+--------------------------------------------------------------------------------
+-- | Returns a 'TimeSpan' that represents a specified number of hours, where the
+--   specification is accurate to the nearest millisecond.
+fromHours :: Double -> TimeSpan
+fromHours i = interval i millisPerHour
+
+--------------------------------------------------------------------------------
+-- | Returns a 'TimeSpan' that represents a specified number of days, where the
+--   specification is accurate to the nearest millisecond.
+fromDays :: Double -> TimeSpan
+fromDays i = interval i millisPerDay
+
+--------------------------------------------------------------------------------
+-- | Gets the value of the current 'TimeSpan' structure expressed in whole and
+--   fractional days.
+totalDays :: TimeSpan -> Double
+totalDays (TimeSpan i) = (realToFrac i) * daysPerTick
+
+--------------------------------------------------------------------------------
+-- | Gets the value of the current 'TimeSpan' structure expressed in whole and
+--   fractional hours.
+totalHours :: TimeSpan -> Double
+totalHours (TimeSpan i) = (realToFrac i) * hoursPerTick
+
+--------------------------------------------------------------------------------
+-- | Gets the value of the current 'TimeSpan' structure expressed in whole and
+--   fractional minutes.
+totalMinutes :: TimeSpan -> Double
+totalMinutes (TimeSpan i) = (realToFrac i) * minutesPerTick
+
+--------------------------------------------------------------------------------
+-- | Gets the value of the current 'TimeSpan' structure expressed in whole and
+--   fractional seconds.
+totalSeconds :: TimeSpan -> Double
+totalSeconds (TimeSpan i) = (realToFrac i) * secondsPerTick
+
+--------------------------------------------------------------------------------
+-- | Gets the value of the current 'TimeSpan' structure expressed in whole and
+--   fractional milliseconds.
+totalMillis :: TimeSpan -> Double
+totalMillis (TimeSpan i) =
+    let tmp = (realToFrac i) * millisPerTick in
+    if tmp > (realToFrac maxMillis) then realToFrac maxMillis
+    else if tmp < (realToFrac minMillis) then realToFrac minMillis
+         else tmp
+
+--------------------------------------------------------------------------------
+data FormatLiteral = Positive | Negative
+
+--------------------------------------------------------------------------------
+padded :: Int -> a -> [a] -> [a]
+padded n p xs = replicate diff p ++ xs
+  where
+    len_xs = length xs
+    diff   = n - len_xs
+
+--------------------------------------------------------------------------------
+timeSpanString :: TimeSpan -> String
+timeSpanString (TimeSpan _ticks) =
+    start    <>
+    genDay   <>
+    genHours <>
+    genMins  <>
+    genSecs  <>
+    genFract
+
+  where
+    ticksPerHourD   = realToFrac ticksPerHour   :: Double
+    ticksPerDayD    = realToFrac ticksPerDay    :: Double
+    ticksPerMinuteD = realToFrac ticksPerMinute :: Double
+    ticksPerSecondD = realToFrac ticksPerSecond :: Double
+
+    day :: Int64
+    day = truncate $ realToFrac _ticks / ticksPerDayD
+
+    time = _ticks `mod` ticksPerDay
+
+    cday  = if _ticks < 0 then negate day else day
+    ctime = if _ticks < 0 then negate time else time
+
+    _hours :: Int64
+    _hours = mod (truncate (realToFrac ctime / ticksPerHourD)) 24
+
+    mins :: Int64
+    mins = mod (truncate (realToFrac ctime / ticksPerMinuteD)) 60
+
+    secs :: Int64
+    secs = mod (truncate (realToFrac ctime / ticksPerSecondD)) 60
+
+    fraction :: Int64
+    fraction = ctime `mod` ticksPerSecond
+
+    literal = if _ticks < 0 then Negative else Positive
+
+    start =
+        case literal of
+            Positive -> ""
+            Negative -> "-"
+
+    genDay =
+        if cday /= 0
+        then show cday <> "."
+        else mempty
+
+    genHours = padded 2 '0' (show _hours) <> ":"
+    genMins  = padded 2 '0' (show mins)  <> ":"
+    genSecs  = padded 2 '0' (show secs)
+
+    genFract =
+        if fraction /= 0
+        then "." <> padded 7 '0' (show fraction)
+        else mempty
+
+--------------------------------------------------------------------------------
+interval :: Double -> Int64 -> TimeSpan
+interval value scale =
+    let tmp     = value * (realToFrac scale)
+        _millis = tmp + (if value >= 0 then 0.5 else (-0.5))
+        res     = truncate (_millis * (realToFrac ticksPerMillisecond)) in
+    TimeSpan res
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,27 @@
+Copyright (c) 2016, Yorick Laupa
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+* Neither the name of dotnet-timespan nor the names of its
+  contributors may be used to endorse or promote products derived from
+  this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,13 @@
+# dotnet-timespan
+[![Build Status](https://travis-ci.org/YoEight/dotnet-timespan.svg?branch=master)](https://travis-ci.org/YoEight/dotnet-timespan)
+
+.NET TimeSpan in Haskell
+
+Notes
+=====
+
+Contributions and bug reports are welcome!
+
+BSD3 License
+
+-Yorick Laupa
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/dotnet-timespan.cabal b/dotnet-timespan.cabal
new file mode 100644
--- /dev/null
+++ b/dotnet-timespan.cabal
@@ -0,0 +1,79 @@
+-- Initial eventstore.cabal generated by cabal init.  For further
+-- documentation, see http://haskell.org/cabal/users-guide/
+
+-- The name of the package.
+name:                dotnet-timespan
+
+-- The package version.  See the Haskell package versioning policy (PVP)
+-- for standards guiding when and how versions should be incremented.
+-- http://www.haskell.org/haskellwiki/Package_versioning_policy
+-- PVP summary:      +-+------- breaking API changes
+--                   | | +----- non-breaking API additions
+--                   | | | +--- code changes with no API change
+version:             0.0.1.0
+
+tested-with: GHC >= 7.8.3 && < 7.11
+
+-- A short (one-line) description of the package.
+synopsis: .NET TimeSpan
+
+-- A longer description of the package.
+description: .NET TimeSpan
+
+-- The license under which the package is released.
+license:             BSD3
+
+-- The file containing the license text.
+license-file:        LICENSE
+
+-- The package author(s).
+author:              Yorick Laupa
+
+-- An email address to which users can send suggestions, bug reports, and
+-- patches.
+maintainer:          yo.eight@gmail.com
+
+-- A copyright notice.
+-- copyright:
+
+homepage:            http://github.com/YoEight/dotnet-timespan
+bug-reports:         http://github.com/YoEight/dotnet-timespan/issues
+category:            Data
+
+build-type:          Simple
+
+-- Extra files to be distributed with the package, such as examples or a
+-- README.
+extra-source-files:  README.md
+                     CHANGELOG.markdown
+                     .gitignore
+                     .travis.yml
+
+-- Constraint on the version of Cabal needed to build this package.
+cabal-version:       >=1.10
+
+source-repository head
+  type: git
+  location: git://github.com/YoEight/dotnet-timespan.git
+
+library
+  -- Modules exported by the library.
+  exposed-modules: Data.DotNet.TimeSpan
+                   Data.DotNet.TimeSpan.Internal
+  -- Modules included in this library but not exported.
+  -- other-modules:
+
+  -- LANGUAGE extensions used by modules in this package.
+  -- other-extensions:
+
+  -- Other library packages from which modules are imported.
+  build-depends:       base >=4.7 && <5
+
+
+  -- Directories containing source files.
+  -- hs-source-dirs:
+
+  -- Base language which the package is written in.
+  ghc-options: -Wall
+
+  default-language:    Haskell2010
