<filename>datetime0_idt.py
#
# wpylib.datetime0_idt
# Created: 20141015
# <NAME>
#
# Simple date/time stamp using 64-bit integer.
# This module is an internal module; the exported name will
# be put in wpylib.datetime module.
#

"""
wplib.datetime0_idt module

Simple date/time stamp using 64-bit integer.
The primary motivation of creating this class is to store date/time
information in the most compact way, while keeping the ease of
human readability.

The internal format is very simple:

YYYYMMDDhhmmssfff

where:
 - YYYY = year (0 < YYYY <= 9999)
 - MM   = month (1 <= MM <= 12)
 - DD   = day
 - hh   = hour (0 <= hh < 24)
 - mm   = minutes
 - ss   = seconds
 - fff  = milliseconds

The time is always expressed in terms of UTC to eliminate ambiguity of
time zone, daylight savings time, etc.
CAVEAT: The resolution of the time is only on the order of millisecond.
Linux OS, for example, allows resolution down to nanoseconds!

This is a 17-decimal-digit integer, which fits in a 64-bit range (9.22e+18).

"""

from wpylib.params import struct


class idatetime(object):
  """Simple 8-byte integer representation of a date/time stamp.
  """
  def __init__(self, idt=None):
    if idt != None:
      self.idt = idt

  def validate(self):
    raise NotImplementedError

  def split_values(self):
    """Splits the compact datetime into components.
    Warning: the values are NOT validated.
    """
    idt = self.idt
    s = str(idt)
    R = struct()
    #R.YYYY, R.MM, R.DD, R.hh, R.mm, R.ss, R.fff # names are too cryptic
    # Use same member names as in datetime (in sofar it is possible):
    R.year, R.month, R.day, R.hour, R.minute, R.second, R.millisecond = map(int, [
      s[:-13],    # year
      s[-13:-11], # month
      s[-11:-9],  # day
      s[-9:-7],   # hour
      s[-7:-5],   # min
      s[-5:-3],   # sec
      s[-3:]      # millisec
    ])
    R.microsecond = R.millisecond * 1000
    return R

  def join_values(self, R=None):
    """Joins the values contained in data structure 'R' to create a
    composite integer date.
    This is the converse of function 'split_values'.
    """
    if R is None:
      R = self.R
    if hasattr(R, 'microsecond'):
      millisecond = R.microsecond // 1000
    elif hasattr(R, 'millisecond'):
      millisecond = R.millisecond

    return R.year   * 10000000000000 \
         + R.month  * 100000000000 \
         + R.day    * 1000000000 \
         + R.hour   * 10000000 \
         + R.minute * 100000 \
         + R.second * 1000 \
         + millisecond

  def to_datetime(self):
    """Converts the object value to standard python datetime object.
    """
    from datetime import datetime
    R = self.split_values()
    dt = datetime(R.year, R.month, R.day, R.hour, R.minute, R.second, R.microsecond)
    '''
    idt = self.idt
    s = str(idt)
    YYYY, MM, DD, hh, mm, ss, ms = map(int, [
      s[:-13],    # year
      s[-13:-11], # month
      s[-11:-9],  # day
      s[-9:-7],   # hour
      s[-7:-5],   # min
      s[-5:-3],   # sec
      s[-3:]      # millisec
    ])
    dt = datetime(YYYY, MM, DD, hh, mm, ss, fff*1000)
    '''
    return dt

  def str_iso8601(self):
    """Outputs the object according to ISO-8601 combined date/time format.
    No time zone is shown."""
    R = self.split_values()
    fmt = "%(year)04d-%(month)02d-%(day)02dT%(hour)02d:%(minute)02d:%(second)02d.%(millisecond)03d"
    return fmt % R


