Reading Date and Time Strings Package

by Harry Bishop

Gettting data in a form that can be used for data analysis can be a time consuming project by itself. Data sources can include text based documents such as letters, emails and text notes.  In recovering the actual data content from this type of information, an important data element is a date or time that may be part of a text string. This notebook is a package of functions related to reading text strings that represent date or time or both types of data and converting them to a datatype conducive to using the information content.  The importing of text informaton containing dates and/or times is based on the formats of standard interfaces.  In addition, a fully customizable output format is available for display of the datatype that can also be exported as a string.

The datatype date▫time is constructed and parts are selectable.  Manipulation consist of differencing, comparisons, and additions of a number of days. Numbers can be converted to the datatype where the number value is taken in days.  Strings can be checked for matching allowed formats and for conversion to the datatype.  In addition, a string consisting of dates and or times such as in a sentence can be separated to provide the dates and times matching the built in formats.  Examples are provided in the last section.

Reference

Title

Reading Dates and Times

Author

Harold Bishop
President and Senior Operations Researcher
Process Modeling Solutions, Inc.
200 Park Ave Suite 108
Falls Church, VA  22046
hrbishop@pmsi-va.com

Summary

This notebook is a package of functions for reading dates and/or times from text strings into a defined datatype date▫time (date EmptyVerySmallSquare time).

Copyright

Rights reserved by, Harold R. Bishop, Process Modeling Solutions, Inc.

Notebook Version

1.0

Mathematica Version

5.2

History

Data recovery efforts involve retrieving date and time data from text strings.  So importing this data from strings and then displaying and exporting it back to strings is a necessary part of data analysis.  Manipulation of the data is useful in algorithms requiring display of the information as well as computations.  This package provides a datatype for the information and functions for importing, displaying and exporting in a desired format.  There is such a variety of ways to represent date and time that a set of routines for reading these formats is a useful tool if available in one package.

Keywords

Date, time, format, textstring, display format,, date▫time datatype, overloading, datatype manipulation, constructors, selectors, predicates.

Limitations

Allowed Symbols for date and time value replacement are
yyyy, yy, MMM, month, mm, dd, hh, mn, ss, am, pm, AM, PM
(note mm = month, MMM = three letter abbreviated month name, month = full month name,, mn = minutes)

For Importing: Only the data formats mentioned are tested. 
hhmnssPM, hhmnPM, hhmmss(24hr or Military time), hhmn (24hr or Military time),
ddMMMyyyy; ddMMMyy; ddMMM; monthyyyy; monthyy; month dd, yyyy; mm/dd/yyyy; mm/dd/yy/; mm/dd;
(note allowed date delimiters are {slash, space, hyphen, nospace}; time delimiters are {colon, space})

For Display and Export formats:
No spaces and ANY other symbols are permitted between date and time symbols
Example: "month dd, yyyy T hh:mn:ss PM" represents March 6, 2006 T 1:45:23 PM (note the T is optional and used to show the XML standard)

Discussion

References:
None.

Requirements

Uses the built in functions ToDate and FromDate.

Interface

This part declares the publicly visible functions, options, and values.

Set up the package context, including public imports

BeginPackage["PMSI`ReadDateTime`"]

$ContextPath

Disable annoying spell warnings

Off[General::spell, General::spell1];

Usage messages for the exported functions and the context itself

ReadDateTime::usage = "ReadDateTime.m is a package of functions related to reading date and time information from text strings, constructing a datatype date▫time, datatype manipulation and display."

epoch::usage = "epoch is the six tuple for Mathematica's reference \
date."

findDateTimeStrings::usage = "findDateTimeStrings[s_String] finds \
those parts of s that match date formats and time formats and returns dates \
followed by times. Allowed formats are: ddMMMyyyy; ddMMMyy; ddMMM; monthyyyy; \
monthyy; month dd, yyyy; mm/dd/yyyy; mm/dd/yy/; mm/dd;
(note allowed date delimiters are {slash, hyphen, period and nospace between \
digits and letters}). Allowed formats are: hhmnssPM, hhmnPM, \
hhmmss(24hr or Military time), hhmn (24hr or Military time),
(note allowed time delimiter is only colon).  Allows string of '4:16:23 PM 3 \
Mar 2006' or 'March 6, 2006 5:12' and other combinations of date and time.  \
'March 03, 0444' will be allowed as the monthddyyyy is recognized before \
monthyy."

deconstructDate::usage = "deconstructDate[date_String] compares date_String with date formats and if a match returns a six tuple consisting of {yyyy,mm,dd,0,0,0}. Allowed formats are: ddMMMyyyy; ddMMMyy; ddMMM; monthyyyy; monthyy; month dd, yyyy; mm/dd/yyyy; mm/dd/yy/; mm/dd;
(note allowed date delimiters are {slash, space, hyphen, and nospace between digits and letters})."

deconstructTime::usage = "deconstructTime[time_String] compares time_String with time formats and if a match returns a six tuple consisting of {1900,1,1,hh,mn,ss}. Allowed formats are: hhmnssPM, hhmnPM, hhmmss(24hr or Military time), hhmn (24hr or Military time),
(note allowed time delimiters are {colon and space})."

deconstructDateTime::usage = "deconstructDateTime[{date_String,time_String}] calls deconstructDate and deconstructTime to form a six tuple consisting of {yyyy,mm,dd,hh,mn,ss}."

date▫time::usage = "datatype used for date and \
time representation and manipulations."

make…date▫time::usage = "make…date▫time[{yyyy,mm,dd,hh,mn,ss}]= make(Ellipsis)date(EmptyVerySmallSquare)time= constructor for date▫time datatype date▫time[year,month,day,hour,minute,second to the nearest $TimeUnit,value in days]."

yearDT::usage = "Selector for date▫time datatype that produces Integer year."

monthDT::usage = "Selector for date▫time datatype that produces Integer month."

dayDT::usage = "Selector for date▫time datatype that produces Integer day."

hourDT::usage = "Selector for date▫time datatype that produces Integer hour."

minuteDT::usage = "Selector for date▫time datatype that produces Integer minute."

secondDT::usage = "Selector for date▫time datatype that produces NonNegative seconds."

valueDT::usage = "Selector for date▫time datatype that produces NonNegative days since Mathematica's reference date."

valueDTtoDT::usage = "valueDTtoDT[value_] converts a value in days to \
a six tuple date using ToDate.  value is truncated to the granularity of \
$TimeUnit."

valid▫dateQ::usage = "valid▫dateQ[date_String] returns True if the string can be made into a usable date▫time datatype and False otherwise."

valid▫timeQ::usage = "valid▫timeQ[time_String] returns True if the string can be made into a usable date▫time datatype and False otherwise. Seconds are retained to a granularity of $TimeUnit."

zero▫date::usage = "date▫time datatype constant for Mathematica's reference date."

displayDTG::usage = "displayDTG[dt_date▫time,format_String] produces a text string with the symbols yyyy, yy, MMM, month, mm, dd, hh, mn, ss, am, pm, AM, PM  replaced by their data and time values.  Can also be written as dt|f. 
No spaces and ANY other symbols are permitted between date and time symbols"

VerticalSeparator::usage = "VerticalSeparator is used in dt|f as a \
shorthand for displayDTG[dt_date▫time,format_String] \
which produces a text string with the symbols yyyy, yy, MMM, \
month, mm, dd, hh, mn, ss, am, pm, AM, PM  replaced by their data and time \
values."

ddMMMyyyy::usage = "ddMMMyyyy[dt_date▫time] \
uses Format to produce the standard format dd·MMM·yyyy.\
"

ddMMMyy::usage = "ddMMMyy[dt_date▫time] uses \
Format to produce the standard format dd·MMM·yy."

ddMMM::usage = "ddMMM[dt_date▫time] uses Format \
to produce the standard format dd·MMM."

monthyyyy::usage = "monthyyyy[dt_date▫time] \
uses Format to produce the standard format month·yyyy."

monthyy::usage = "monthyy[dt_date▫time] uses \
Format to produce the standard format month·yy."

monthddyyyy::usage = "monthddyyyy[dt_date▫time] \
uses Format to produce the standard format month dd, yyyy."

mmddyyyy::usage = "mmddyyyy[dt_date▫time] uses \
Format to produce the standard format mm/dd/yyyy."

mmddyy::usage = "mmddyy[dt_date▫time] uses \
Format to produce the standard format mm/dd/yy."

mmdd::usage = "mmdd[dt_date▫time] uses Format \
to produce the standard format mm/dd."

hhmnss::usage = "hhmnss[dt_date▫time] uses \
Format to produce the standard format hh:mn:ss."

hhmn::usage = "hhmn[dt_date▫time] uses Format \
to produce the standard format hh:mn."

\(hhmnssEXP::usage = \*""hhmnssEXP[dt_date▫time] uses Format to produce the standard format \!\(hh\^\(\(\\ \)\(h\)\)\) \!\(mn\^\(\(\\ \)\(m\)\)\) \!\(ss\^\(\(\\ \)\(s\)\)\).""\)

\(hhmnEXP::usage = \*""hhmnEXP[dt_date▫time] uses Format to produce the standard format \!\(hh\^\(\(\\ \)\(h\)\)\) \!\(mn\^\(\(\\ \)\(m\)\)\).""\)

hhmnssPM::usage = "hhmnssPM[dt_date▫time] uses \
Format to produce the standard format hh:mn:ss PM."

hhmnPM::usage = "hhmnPM[dt_date▫time] uses \
Format to produce the standard format hh:mn PM."

Error messages for the exported objects

Implementation

This part contains the actual definitions and any auxiliary functions that should not be visible outside.

Begin the private context (implementation part)

Begin["`Private`"]
{$Context,$ContextPath}

Read in any hidden imports

Unprotect exported variables and any system functions for which definitions will be made

Unprotect[date▫time,dayDT,ddMMM,ddMMMyy,ddMMMyyyy,deconstructDate,deconstructDateTime,deconstructTime,
displayDTG,epoch,hhmn,findDateTimeStrings,hhmnEXP,hhmnPM,hhmnss,hhmnssEXP,hhmnssPM,hourDT,make…date▫time,minuteDT,
mmdd,mmddyy,mmddyyyy,monthddyyyy,monthDT,monthyy,monthyyyy,ReadDateTime,secondDT,valueDT,valueDTtoDT,
valid▫dateQ,valid▫timeQ,yearDT,zero▫date]

Definition of auxiliary functions and local (static) variables

\(\(delimiters = \({nospace, hyphen, period, comma, slash, colon, 
            space} = {"", "-", ".", ",", "/", ":", " "}\);\)\)

centuryAbbrMark = 50;
epoch = 
      ToDate[0];    
macepoch = {1904, 1, 3, 0, 0, 
        0. };    
epochToMac = FromDate[macepoch];
timeResolution = N[$TimeUnit];

digits = Characters["0123456789 "];
periodDigits = 
        Characters["0123456789 ."];
onlyDigits = 
        Characters["0123456789"];
periodonlyDigits = 
        Characters["0123456789."];
ampm = {"am", "pm", "AM", "PM"};
amHHstring = 
        Map[ToString, Range[0, 11]];
pmHHstring = 
        RotateRight[
          Map[ToString, Range[1, 12]]];
\(\(monthAbbr = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};\)\)
\(\(monthFullName = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};\)\)
monthName = 
        ToUpperCase[
          Join[monthFullName, monthAbbr]];
timedelimiters = {colon, space};
datedelimiters = {slash, space, hyphen, 
          nospace};
ymddelimiters = {slash, hyphen, period};
splitdatedelimiters = {comma <> space, slash, hyphen, comma, 
          space};

delimiterQ[char_String] := MemberQ[delimiters, char];
delimiterQ[_] := False;

timeAccuracy[timeValue_?NumericQ] := 
        With[{n = IntegerPart[timeValue], 
            dn = FractionalPart[timeValue]}, 
n + 
            timeResolution*Round[dn/timeResolution]];

Time Formats

timecolonInsert = (x_ /; 
              StringMatchQ[
                x, ___ ~~ onlyDigits ~~ ampm]) :> 
          StringInsert[x, ":", -3];

separateTimes[t_String] := 
        StringSplit[t /. timecolonInsert, timedelimiters];

hhmnssPMformat = 
        StringExpression[onlyDigits .. , colon, onlyDigits .. , colon, 
          periodDigits .. , ampm];
hhmnPMformat = 
        StringExpression[onlyDigits .. , colon, onlyDigits .. , 
          ampm];
hhmnssMILformat = 
        StringExpression[onlyDigits .. , colon, onlyDigits .. , colon, 
          periodonlyDigits .. ];
hhmnMILformat = 
        StringExpression[onlyDigits .. , colon, 
          onlyDigits .. ];

hhmnssPMQ[tstring_String] := 
        StringMatchQ[tstring, hhmnssPMformat];
hhmnssPMQ[_] := False;
hhmnPMQ[tstring_String] := 
        StringMatchQ[tstring, hhmnPMformat];
hhmnPMQ[_] := False;
hhmnssMILQ[tstring_String] := 
        StringMatchQ[tstring, hhmnssMILformat];
hhmnssMILQ[_] := False;
hhmnMILQ[tstring_String] := 
        StringMatchQ[tstring, hhmnMILformat];
hhmnMILQ[_] := False;

Date Formats

monthNr[m_String] := 
        With[{mNr = 
              Mod[First[Flatten[Position[monthName, ToUpperCase[m]]]], 12]}, 
          If[mNr == 0, 12, mNr]];
yearAbbr[year_Integer] := 
        With[{y = Mod[year, 100]}, 
          If[y < 10, "0" <> ToString[y], 
            ToString[y]]];
twodigitString[n_Integer] := 
        If[n < 10, "0" <> ToString[n], 
          ToString[n]];
expandYY[yy_String] := 
        With[{ny = ToExpression[yy]}, 
          If[ny > centuryAbbrMark, 1900 + ny, 2000 + ny]];

\(\(monthforwardspaceInsert = \((x_ /; 
              StringMatchQ[
                x, ___ ~~ \(onlyDigits ~~ \(monthName ~~ ___\)\)])\) :> 
          StringInsert[x, " ", 
            First[Flatten[
                StringPosition[x, monthName, 
                  1]]]];\)\)
\(\(monthafterspaceInsert = \((x_ /; 
              StringMatchQ[
                x, ___ ~~ \(monthName ~~ \(onlyDigits ~~ ___\)\)])\) :> 
          StringInsert[x, " ", 
            Last[Flatten[StringPosition[x, monthName, 1]]] + 
              1];\)\)

separateDateParts[d_String] := 
        StringSplit[d /. monthforwardspaceInsert /. 
            monthafterspaceInsert, splitdatedelimiters];

yyyyformat = 
        StringExpression[onlyDigits, onlyDigits, onlyDigits, 
          onlyDigits];
yyformat = 
        StringExpression[onlyDigits, 
          onlyDigits];
mmformat = ddformat = 
          StringExpression[onlyDigits .. ];

ddMMMyyyyformat = 
        ddformat ~~ datedelimiters ~~ monthName ~~ datedelimiters ~~ 
                yyyyformat;
ddMMMyyformat = 
        ddformat ~~ datedelimiters ~~ monthName ~~ datedelimiters ~~ 
                yyformat;
ddMMMformat = 
        ddformat ~~ datedelimiters ~~ 
            monthName;
monthyyyyformat = 
        monthName ~~ datedelimiters ~~ 
            yyyyformat;
monthyyformat = 
        monthName ~~ datedelimiters ~~ 
            yyformat;
\(\(monthddyyyyformat = 
        monthName ~~ \(datedelimiters ~~ \(ddformat ~~ \("," ~~ \(datedelimiters ~~ yyyyformat\)\)\)\);\)\)

mmddyyyyformat = 
        mmformat ~~ ymddelimiters ~~ ddformat ~~ ymddelimiters ~~ 
                yyyyformat;
mmddyyformat = 
        mmformat ~~ ymddelimiters ~~ ddformat ~~ ymddelimiters ~~ 
                yyformat;
mmddformat = mmformat ~~ ymddelimiters ~~ ddformat;

ddMMMyyyyQ[tstring_String] := 
        StringMatchQ[ToUpperCase[tstring], 
          ddMMMyyyyformat];
ddMMMyyyyQ[_] := False;
ddMMMyyQ[tstring_String] := 
        StringMatchQ[ToUpperCase[tstring], 
          ddMMMyyformat];
ddMMMyyQ[_] := False;
ddMMMQ[tstring_String] := 
        StringMatchQ[ToUpperCase[tstring], 
          ddMMMformat];
ddMMMQ[_] := False;
monthyyyyQ[tstring_String] := 
        StringMatchQ[ToUpperCase[tstring], 
          monthyyyyformat];
monthyyyyQ[_] := False;
monthyyQ[tstring_String] := 
        StringMatchQ[ToUpperCase[tstring], 
          monthyyformat];
monthyyQ[_] := False;
monthddyyyyQ[tstring_String] := 
        StringMatchQ[ToUpperCase[tstring], 
          monthddyyyyformat];
monthddyyyyQ[_] := False;
mmddyyyyQ[tstring_String] := 
        StringMatchQ[tstring, mmddyyyyformat];
mmddyyyyQ[_] := False;
mmddyyQ[tstring_String] := 
        StringMatchQ[tstring, mmddyyformat];
mmddyyQ[_] := False;
mmddQ[tstring_String] := 
        StringMatchQ[tstring, mmddformat];
mmddQ[_] := False;

Format Conversion Rules

\(\(deconstructTimeFormat = {
\((timestring_?
                hhmnssPMQ)\) :> \((separateTimes[
                  timestring] /. {hh_, mn_, ss_, 
                    am_} :> {0, 0, 0, 
                      Mod[ToExpression[hh], 12] + 
                        12\ Boole[\((am == "pm")\) || \((am == "PM")\)], ToExpression[mn], ToExpression[ss]} + 
                    epoch)\), 
\((timestring_?
                hhmnPMQ)\) :> \((separateTimes[
                  timestring] /. {hh_, mn_, 
                    am_} :> {0, 0, 0, 
                      Mod[ToExpression[hh], 12] + 
                        12\ Boole[\((am == "pm")\) || \((am == "PM")\)], ToExpression[mn], 0. } + 
                    epoch)\), 
\((timestring_?
                hhmnssMILQ)\) :> \((separateTimes[
                  timestring] /. {hh_, mn_, 
                    ss_} :> {0, 0, 0, ToExpression[hh], 
                      ToExpression[mn], ToExpression[ss]} + 
                    epoch)\), 
\((timestring_?
                hhmnMILQ)\) :> \((separateTimes[
                  timestring] /. {hh_, 
                    mn_} :> {0, 0, 0, ToExpression[hh], 
                      ToExpression[mn], 0. } + 
                    epoch)\)
};\)\)

deconstructDateFormat = {
(datestring_?
                ddMMMyyyyQ) :> (separateDateParts[
                  datestring] /. {dd_, mmText_, 
                    yyyy_} :> {ToExpression[yyyy], 
                    monthNr[mmText], ToExpression[dd], 0, 0, 
                    0}), 
(datestring_?
                ddMMMyyQ) :> (separateDateParts[
                  datestring] /. {dd_, mmText_, 
                    yy_} :> {expandYY[yy], monthNr[mmText], 
                    ToExpression[dd], 0, 0, 
                    0}), 
(datestring_?
                ddMMMQ) :> (separateDateParts[
                  datestring] /. {dd_, 
                    mmText_} :> {epoch[[1]], monthNr[mmText], ToExpression[dd], 0, 0, 
                    0}), 
(datestring_?
                monthyyyyQ) :> (separateDateParts[
                  datestring] /. {mmText_, 
                    yyyy_} :> {ToExpression[yyyy], 
                    monthNr[mmText], 1, 0, 0, 
                    0}), 
(datestring_?
                monthyyQ) :> (separateDateParts[
                  datestring] /. {mmText_, yy_} :> {expandYY[yy], 
                    monthNr[mmText], 1, 0, 0, 
                    0}), 
(datestring_?
                monthddyyyyQ) :> (separateDateParts[
                  datestring] /. {mmText_, dd_, 
                    yyyy_} :> {ToExpression[yyyy], 
                    monthNr[mmText], ToExpression[dd], 0, 0, 
                    0}), 
(datestring_?
                mmddyyyyQ) :> (separateDateParts[
                  datestring] /. {mm_, dd_, 
                    yyyy_} :> {ToExpression[yyyy], 
                    ToExpression[mm], ToExpression[dd], 0, 0, 
                    0}), 
(datestring_?
                mmddyyQ) :> (separateDateParts[
                  datestring] /. {mm_, dd_, 
                    yy_} :> {expandYY[yy], ToExpression[mm], 
                    ToExpression[dd], 0, 0, 
                    0}), 
(datestring_?
                mmddQ) :> (separateDateParts[
                  datestring] /. {mm_, 
                    dd_} :> {epoch[[1]], ToExpression[mm], ToExpression[dd], 0, 0, 
                    0})
};

Definition of the exported functions

String to six tuple conversions

deconstructDate[date_String] := 
        ToUpperCase[StringReplace[date, "  " -> " "]] /. 
          deconstructDateFormat;
deconstructTime[time_String] := 
        time /. deconstructTimeFormat;
deconstructDateTime[{date_String, time_String}] := 
        deconstructDate[date] + deconstructTime[time] - epoch;

findDateTimeStrings[s_String] := 
        Module[{fullStr = ToUpperCase[s], times, dateStr, 
            dates}, 
times = 
            StringCases[fullStr, 
              hhmnssPMformat | hhmnPMformat | hhmnssMILformat | 
                hhmnMILformat]; 
dateStr = 
            StringReplace[fullStr, 
              Map[# -> "" &, times]]; 
dates = 
            StringCases[dateStr, 
              ddMMMyyyyformat | ddMMMyyformat | ddMMMformat | 
                monthddyyyyformat | monthyyyyformat | monthyyformat | 
                mmddyyyyformat | mmddyyformat | 
                mmddformat]; 
Flatten[{dates, 
              times}]];

Constructors and Selectors

make…date▫time[{
year_Integer?NonNegative, 
month_Integer /; 
              0 ≤ month ≤ 
                12, 
day_Integer /; 
              0 ≤ day ≤ 
                31, 
hour_Integer /; 
              0 ≤ hour ≤ 
                23, 
minute_Integer /; 
              0 ≤ minute ≤ 
                59, 
second_?NonNegative /; 
              0 ≤ 
                second ≤ (60.  - 
                    timeResolution)
}] := 
        With[{secondedit = 
              timeAccuracy[
                second]}, 
date▫time[
            year, month, day, hour, minute, secondedit, 
            FromDate[{year, month, day, hour, minute, secondedit}]/
              86400. ]];

datetimeTuple[
          date▫time[y_, m_, d_, h_, mn_, s_, 
            v_]] := {y, m, d, h, mn, s};

yearDT[
          date▫time[year_, month_, day_, hour_, 
            minute_, second_, value_]] := year;
monthDT[
          date▫time[year_, month_, day_, hour_, 
            minute_, second_, value_]] := month;
dayDT[
          date▫time[year_, month_, day_, hour_, 
            minute_, second_, value_]] := day;
hourDT[
          date▫time[year_, month_, day_, hour_, 
            minute_, second_, value_]] := hour;
minuteDT[
          date▫time[year_, month_, day_, hour_, 
            minute_, second_, value_]] := 
        minute;
secondDT[
          date▫time[year_, month_, day_, hour_, 
            minute_, second_, value_]] := 
        second;
valueDT[
          date▫time[year_, month_, day_, hour_, 
            minute_, second_, value_]] := value;

date▫time Constants

zero▫date = 
        make…date▫time[
          epoch];
valueDT[zero▫date] = 0;

Operations

datetimeTuple[zero▫date] == 
        ToDate[valueDT[zero▫date]];

date▫time /: 
        xDT_date▫time + c_?NumericQ := 
        make…date▫time[
          ToDate[86400. *valueDT[xDT] + timeAccuracy[c]]];
date▫time /: 
        xDT_date▫time - 
          yDT_date▫time := 
        valueDT[xDT] - valueDT[yDT];
date▫time /: 
        xDT_date▫time > 
          yDT_date▫time := 
        valueDT[xDT] > valueDT[yDT];
date▫time /: 
        xDT_date▫time ≥ 
          yDT_date▫time := (Abs[
                86400.  (valueDT[xDT] - valueDT[yDT])] < 
              timeResolution) || (xDT > 
              yDT);
date▫time /: 
        macDate[xDT_date▫time] := 
        xDT + epochToMac;

valueDTtoDT[xValue_] := 
        zero▫date + xValue*86400. ;

Predicates

equalDTtuplesQ[xTuple_List, yTuple_List] := 
        With[{diff = (xTuple - yTuple)}, 
          Most[diff] == {0, 0, 0, 0, 0} && 
            Abs[Last[diff]] < 1. ];

date▫time /: 
        date▫timeQ[dt_date▫time] := 
        equalDTtuplesQ[datetimeTuple[dt], 
            ToDate[Floor[86400.  valueDT[dt]]]] || 
          equalDTtuplesQ[datetimeTuple[dt], 
            ToDate[Ceiling[
                86400.  valueDT[dt]]]];
valid▫dateQ[date_String] := 
        date▫timeQ[
          make…date▫time[
            deconstructDate[date]]];
valid▫timeQ[time_String] := 
        date▫timeQ[
          make…date▫time[
            deconstructTime[time]]];
date▫timeQ[_] := 
        False;
valid▫dateQ[_] := 
        False;
valid▫timeQ[_] := False;

Ouput Formatting

\(\(displayDTG[dt_date▫time, format_String] := 
        Module[{hr, ampmIndicator, fAMPM, xAMPM}, 
hr = 
            hourDT[dt]; 
ampmIndicator = 
            StringCount[format, ampm] > 0; 
fAMPM = 
            If[ampmIndicator, 
              First[StringCases[format, 
                  ampm]], "Null"]; 
xAMPM = 
            Which[ampmIndicator && \((hr < 
                    12)\), "AM", 
ampmIndicator && \((hr ≥ 
                    12)\), "PM", 
\(! ampmIndicator\), "Null"]; 
StringReplace[
            format, {
"yyyy" -> 
                ToString[yearDT[dt]], 
"yy" -> 
                yearAbbr[yearDT[dt]], 
"MMM" -> 
                monthAbbr[[
                  monthDT[
                    dt]]], 
"month" -> monthFullName[[
                  monthDT[
                    dt]]], 
"mm" -> ToString[monthDT[dt]], 
"dd" -> 
                ToString[dayDT[dt]], 
"hh" -> 
                Which[xAMPM == "AM", 
                  amHHstring[[
                    Mod[hr, 12] + 
                      1]], 
xAMPM == "PM", 
                  pmHHstring[[
                    Mod[hr, 12] + 
                      1]], 
xAMPM == "Null", twodigitString[hr]], 
"mn" -> 
                twodigitString[
                  minuteDT[dt]], 
"ss" -> 
                twodigitString[
                  Round[secondDT[dt]]], 
fAMPM -> 
                xAMPM}]];\)\)

date▫time /: 
        xDT_date▫time \[VerticalSeparator] f_String := 
        displayDTG[xDT, f];

Format[ddMMMyyyy[dt_date▫time], StandardForm] := 
        StringForm["`3`·`2`·`1`", yearDT[dt], 
          monthAbbr[[monthDT[dt]]], 
          dayDT[dt]];
Format[ddMMMyy[dt_date▫time], StandardForm] := 
        StringForm["`3`·`2`·`1`", 
          yearAbbr[yearDT[dt]], 
          monthAbbr[[monthDT[dt]]], 
          dayDT[dt]];
Format[ddMMM[dt_date▫time], StandardForm] := 
        StringForm["`2`·`1`", 
          monthAbbr[[monthDT[dt]]], 
          dayDT[dt]];
Format[monthyyyy[dt_date▫time], StandardForm] := 
        StringForm["`2`·`1`", yearDT[dt], 
          monthFullName[[
            monthDT[dt]]]];
Format[monthyy[dt_date▫time], StandardForm] := 
        StringForm["`2`·`1`", yearAbbr[yearDT[dt]], 
          monthFullName[[
            monthDT[dt]]]];
Format[monthddyyyy[dt_date▫time], 
          StandardForm] := 
        StringForm["`2` `3`, `1`", yearDT[dt], 
          monthFullName[[monthDT[dt]]], 
          dayDT[dt]];
Format[mmddyyyy[dt_date▫time], StandardForm] := 
        StringForm["`2`/`3`/`1`", yearDT[dt], monthDT[dt], 
          dayDT[dt]];
Format[mmddyy[dt_date▫time], StandardForm] := 
        StringForm["`2`/`3`/`1`", yearAbbr[yearDT[dt]], monthDT[dt], 
          dayDT[dt]];
Format[mmdd[dt_date▫time], StandardForm] := 
        StringForm["`1`/`2`", monthDT[dt], dayDT[dt]];

Format[hhmnss[dt_date▫time], StandardForm] := 
        StringForm["`1`:`2`:`3`", twodigitString[hourDT[dt]], 
          twodigitString[minuteDT[dt]], 
          twodigitString[Round[secondDT[dt]]]];
Format[hhmn[dt_date▫time], StandardForm] := 
        StringForm["`1`:`2`", twodigitString[hourDT[dt]], 
          twodigitString[minuteDT[dt]]];
\(\(Format[hhmnssEXP[dt_date▫time], StandardForm] := 
        StringForm[\*""\!\(`1`\^\(\(\\ \)\(h\)\)\) \!\(`2`\^\(\(\\ \)\(m\)\)\) \!\(`3`\^\(\(\\ \)\(s\)\)\)"", hourDT[dt], minuteDT[dt], 
          Round[secondDT[dt]]];\)\)
\(\(Format[hhmnEXP[dt_date▫time], StandardForm] := 
        StringForm[\*""\!\(`1`\^\(\(\\ \)\(h\)\)\) \!\(`2`\^\(\(\\ \)\(m\)\)\)"", hourDT[dt], minuteDT[dt]];\)\)
Format[hhmnssPM[dt_date▫time], StandardForm] := 
        With[{hr = hourDT[dt]}, 
          StringForm["`1`:`2`:`3` `4`", 
            Apply[Sequence, 
If[
                hr < 12, {amHHstring[[
                    Mod[hr, 12] + 1]], 
                  twodigitString[minuteDT[dt]], 
                  twodigitString[
                    Round[secondDT[
                        dt]]], "AM"}, {pmHHstring[[
                    Mod[hr, 12] + 1]], 
                  twodigitString[minuteDT[dt]], 
                  twodigitString[
                    Round[secondDT[
                        dt]]], "PM"}]]]];

    
Format[hhmnPM[dt_date▫time], StandardForm] := 
        With[{hr = hourDT[dt]}, 
          StringForm["`1`:`2` `3`", 
            Apply[Sequence, 
If[
                hr < 12, {amHHstring[[
                    Mod[hr, 12] + 1]], 
                  twodigitString[
                    minuteDT[dt]], "AM"}, {pmHHstring[[
                    Mod[hr, 12] + 1]], 
                  twodigitString[minuteDT[dt]], "PM"}]]]];

End the private context

End[ ]

Epilog

This section protects exported symbols and ends the package.

Protect exported symbol

Protect[date▫time,dayDT,ddMMM,ddMMMyy,ddMMMyyyy,deconstructDate,deconstructDateTime,deconstructTime,
displayDTG,epoch,findDateTimeStrings,hhmn,hhmnEXP,hhmnPM,hhmnss,hhmnssEXP,hhmnssPM,hourDT,make…date▫time,minuteDT,
mmdd,mmddyy,mmddyyyy,monthddyyyy,monthDT,monthyy,monthyyyy,ReadDateTime,secondDT,valueDT,valueDTtoDT,
valid▫dateQ,valid▫timeQ,yearDT,zero▫date]

Alternative: protect all exported symbols

(*Protect[Evaluate[$Context <> "*"]]*)

Re-able annoying spell warnings

On[General::spell,General::spell1];

End the package context

EndPackage[ ]

Examples, Tests

Examples, tests for the use of the package can go here.

\(Map[
      deconstructTime, {"1:35PM", "2:23:45 pm", "13:22:55.1", "1:35"}]\)

\(Map[
      deconstructDate, {"20-dec-2006", "15nov05", "13-Mar", "october03", "12/5/2001", "12/4/01", "aug05", "3/12"}]\)

deconstructDateTime[{"March 3, 2005", "13:35:17.3"}]

deconstructDateTime[{"3/12", "13:35:17.3"}]

Map[
      valid▫dateQ, {"20-dec-2006", "15nov05", "\
13-Mar", "october03", "12/30/2001", "12/4/01", "aug05", "\
3/12", "march 5 2000"}]

\(Map[
      valid▫timeQ, {"1:35PM", "2:23:45 pm", "13:22:55.1", "1:35", "24:35:59"}]\)

test = 
      make…date▫time[Date[]]

test + 86400.9999999999999999999999

monthddyyyy[test]

monthyyyy[test]
monthyy[test]

ddMMMyyyy[test]
ddMMMyy[test]
ddMMM[test]

mmddyyyy[test]
mmddyy[test]
mmdd[test]

testam = 
      make…date▫time[{2006, 3, 31, 9, 25, 
          16}]

hhmnss[testam]
hhmn[testam]
hhmnssEXP[testam]
hhmnEXP[testam]
hhmnssPM[testam]
hhmnPM[testam]

testam \[VerticalSeparator] "hh:mn:ss AM"

Context[make…date▫time]

testdays = 
      make…date▫time[{1850, 3, 31, 9, 25, 
          16}]
valueDTtoDT[valueDT[testdays]]

findDateTimeStrings["4:16:23 pm March 27, 2006 1440 16:23"]

FullForm[testam \[VerticalSeparator] "hh:mn:ss AM"]

Attributes[VerticalSeparator]

displayDTG[testam, "hh:mn:ss AM"]

?? Plus

?UpValues