# Copyright 2026 SigmaSRC, Inc. All Rights Reserved.

"""Format timestamps in a standard format.

The format*() functions in this module return a string of the form

  YYYY-MM-DDThh:mm:ss.uuuuuuZ

where:

  - YYYY-MM-DD is the date using a 4-digit year
  - T is the upper case letter T
  - hh:mm:ss is the time in a 24-hour clock
  - uuuuuu is the microseconds
  - Z is the upper case letter Z indicating this is a UTC timestamp

The parse() function parses a string of the same form and returns a
datetime object.

Empty or absent parameters return the current time.

References:

- http://www.cl.cam.ac.uk/~mgk25/iso-time.html
- http://www.w3.org/TR/xmlschema-2/#isoformats

"""

import re
import time
import datetime

# Functions returning a string

def formatdatetime(x=None):
    """Format a datetime object.  It must not have a timezone."""
    if not x:
        x = now()
    assert x.tzinfo is None
    s = x.isoformat()
    if x.microsecond == 0:
        return s + ".000000Z"
    else:
        return s + "Z"

def formattimestamp(t=None):
    """Format a timestamp (as returned by time.time())."""
    return formatdatetime(datetimeobj(t))

def formatnow():
    """Format the current time."""
    return formatdatetime()

# Functions returning a datetime object

def datetimeobj(t=None):
    """Convert a timestamp to a datetime instance."""
    if t is None:
        t = time.time()
    return datetime.datetime.fromtimestamp(t, datetime.timezone.utc).replace(tzinfo=None)

def now():
    """Return the current time as a datetime instance."""
    return datetimeobj()

_parse_regex = re.compile(r"\A(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)"
                          r"\.(\d\d\d\d\d\d)Z\Z")

def parse(s=None):
    """Parse a formatted time, return a datetime object.

    An empty input returns the current time.

    When the input is invalid, ValueError is raised.

    """
    if not s:
        return now()
    m = _parse_regex.match(s)
    if not m:
        raise ValueError("indecipherable timestamp %r" % s)
    return datetime.datetime(*map(int, m.groups()))

# Functions dealing with date objects; these represent local time

def formatdate(x=None):
    """Format a date object."""
    if not x:
        x = today()
    return x.isoformat()

def dateobj(t=None):
    """Convert a timestamp to a date instance."""
    if t is None:
        return datetime.date.today()
    else:
        return datetime.date.fromtimestamp(t)

def today():
    """Return today's date."""
    return datetime.date.today()

_parsedate_regex = re.compile(r"\A(\d\d\d\d)-(\d\d)-(\d\d)\Z")

def parsedate(s=None):
    """Parse a formatted date, return a date object.

    An empty input returns the current time.

    When the input is invalid, ValueError is raised.

    """
    if not s:
        return today()
    m = _parsedate_regex.match(s)
    if not m:
        raise  ValueError("indecipherable date %r" % s)
    return datetime.date(*map(int, m.groups()))
