import times

const zeroTime = fromUnix(0)
const ISO_8601_FORMATS = @[
  "yyyy-MM-dd'T'HH:mm:ssz",
  "yyyy-MM-dd'T'HH:mm:sszzz",
  "yyyy-MM-dd'T'HH:mm:ss'.'fffzzz",
  "yyyy-MM-dd HH:mm:ssz",
  "yyyy-MM-dd HH:mm:sszzz",
  "yyyy-MM-dd HH:mm:ss'.'fffzzz"
]

proc format*(ti: TimeInterval, fmt: string): string =
  let info = (zeroTime + ti).utc
  return info.format(fmt)

# Will be deprecated in Nim 0.18.1 as it will exist in the standard times module.
proc `-`*(a, b: DateTime): Duration {.deprecated} =
  return times.`-`(a,b)

proc `<`*(a, b: DateTime): bool {.deprecated} =
  return times.`<`(a,b)

proc `<=`*(a, b: DateTime): bool {.deprecated} =
  return times.`<=`(a,b)

proc `<`*(a, b: TimeInterval): bool =
  return (zeroTime + a) < (zeroTime + b)

proc `<=`*(a, b: TimeInterval): bool =
  return (zeroTime + a) <= (zeroTime + b)

proc between*(a, s, e: DateTime): bool =
  return times.`<=`(s, a) and times.`<`(a, e)

proc cmp*(a, b: DateTime): int =
  if b == a: return 0
  elif times.`<`(a, b): return -1
  else: return 1

proc startOfDay*(dt: DateTime): DateTime =
  result = dt
  result.hour = 0
  result.minute = 0
  result.second = 0
  result.nanosecond = 0

proc trimNanoSec*(dt: DateTime): DateTime =
  result = dt
  result.nanosecond = 0

proc startOfWeek*(ti: DateTime, startDay = dMon): DateTime =
  var diff = (ti.weekday.ord - startDay.ord)
  if diff < 0: diff += 7
  return (ti - diff.days).startOfDay

proc parseIso8601*(val: string): DateTime =
  var errString = ""
  for df in ISO_8601_FORMATS:
    try: return val.parse(df)
    except: errString &= "\n" & getCurrentExceptionMsg()
  raise newException(Exception, "Could not parse date. Tried:" & errString)

proc formatIso8601*(d: DateTime): string =
  return d.format(ISO_8601_FORMATS[2])

# This is a workaround needed due a bug in Nim's times.parse procedure.
# see: https://github.com/nim-lang/Nim/issues/4922
template fixedParse*(value, format: string): DateTime {.deprecated.} =
  parse(value, format)