//
//  File:   hzDate.cpp
//
//  Legal Notice:   This file is part of the HadronZoo C++ Class Library. Copyright 2025 HadronZoo Project (http://www.hadronzoo.com)
//
//  The HadronZoo C++ Class Library is free software: You can redistribute it, and/or modify it under the terms of the GNU Lesser General Public License, as published by the Free
//  Software Foundation, either version 3 of the License, or any later version.
//
//  The HadronZoo C++ Class Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
//  A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public License along with the HadronZoo C++ Class Library. If not, see http://www.gnu.org/licenses.
//
//
//  Implimentation of Dates
//
#include <iostream>
#include <sys/time.h>
#include <stdarg.h>
#include "hzChars.h"
#include "hzTextproc.h"
#include "hzErrcode.h"
#include "hzProcess.h"
#include "hzDate.h"
using namespace std ;
/*
**  Global Variables
*/
global  const char* hz_daynames_abrv    [] =
{
    //  Abreviated day names (Default language English)
    "Mon","Tue","Wed","Thu","Fri","Sat","Sun",""
} ;
global  const char* hz_daynames_full    [] =
{
    //  Full day names (Default language English)
    "Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday",""
} ;
global  const char* hz_monthnames_abrv  [] =
{
    //  Abreviated month names (Default language English)
    "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec",""
} ;
global  const char* hz_monthnames_full  [] =
{
    //  Full month names (Default language English)
    "January","February","March","April","May","June","July","August","September","October","November","December",""
} ;
global  const   hzXDate _hz_null_hzXDate ;      //  Null full date
global  const   hzSDate _hz_null_hzSDate ;      //  Null short date
global  const   hzTime  _hz_null_hzTime ;       //  Null time
struct  hzTimezone
{
    //  Category:   Time
    //
    //  International Timezone. Maps timezone code to GMT offset
    const char* code ;      //  Timezone code
    int32_t     hour ;      //  Hours ahead of GMT
    int32_t     min ;       //  Minutes ahead of GMT
    void    clear   (void)  { code = 0 ; hour = min = 0 ; }
} ;
global  hzTimezone  _hzGlobal_Timezones []  =
{
    { "ADT",    -3,  0, },  //  Atlantic Daylight Time          North America   UTC - 3 hours
    { "AFT",     4,  0, },  //  Afghanistan Time                Asia            UTC + 4:30 hours
    { "AKDT",   -8,  0, },  //  Alaska Daylight Time            North America   UTC - 8 hours
    { "AKST",   -9,  0, },  //  Alaska Std Time                 North America   UTC - 9 hours
    { "ALMT",    6,  0, },  //  Alma-Ata Time                   Asia            UTC + 6 hours
    { "AMST",    5,  0, },  //  Armenia Summer Time             Asia            UTC + 5 hours
    { "AMST",   -3,  0, },  //  Amazon Summer Time              South America   UTC - 3 hours
    { "AMT",     4,  0, },  //  Armenia Time                    Asia            UTC + 4 hours
    { "AMT",    -4,  0, },  //  Amazon Time                     South America   UTC - 4 hours
    { "ANAST",  12,  0, },  //  Anadyr Summer Time              Asia            UTC + 12 hours
    { "ANAT",   12,  0, },  //  Anadyr Time                     Asia            UTC + 12 hours
    { "AQTT",    5,  0, },  //  Aqtobe Time                     Asia            UTC + 5 hours
    { "ART",    -3,  0, },  //  Argentina Time                  South America   UTC - 3 hours
    { "AST",     3,  0, },  //  Arabia Std Time                 Asia            UTC + 3 hours
    { "AST",    -4,  0, },  //  Atlantic Std Time               Atlantic        UTC - 4 hours
    { "AST",    -4,  0, },  //  Atlantic Std Time               Caribbean       UTC - 4 hours
    { "AST",    -4,  0, },  //  Atlantic Std Time               North America   UTC - 4 hours
    { "AZOST",   4,  0, },  //  Azores Summer Time              Atlantic        UTC
    { "AZOT",   -1,  0, },  //  Azores Time                     Atlantic        UTC - 1 hour
    { "AZST",    5,  0, },  //  Azerbaijan Summer Time          Asia            UTC + 5 hours
    { "AZT",     4,  0, },  //  Azerbaijan Time                 Asia            UTC + 4 hours
    { "BNT",     8,  0, },  //  Brunei Darussalam Time          Asia            UTC + 8 hours
    { "BOT",    -4,  0, },  //  Bolivia Time                    South America   UTC - 4 hours
    { "BRST",   -2,  0, },  //  Brasilia Summer Time            South America   UTC - 2 hours
    { "BRT",    -3,  0, },  //  Brasa time                      South America   UTC - 3 hours
    { "BST",     6,  0, },  //  Bangladesh Std Time             Asia            UTC + 6 hours
    { "BST",     1,  0, },  //  British Summer Time             Europe          UTC + 1 hour
    { "BTT",     6,  0, },  //  Bhutan Time                     Asia            UTC + 6 hours
    { "CAST",    8,  0, },  //  Casey Time                      Antarctica      UTC + 8 hours
    { "CAT",     2,  0, },  //  Central Africa Time             Africa          UTC + 2 hours
    { "CCT",     6,  0, },  //  Cocos Islands Time              Indian Ocean    UTC + 6:30 hours
    { "CDT",    10,  0, },  //  Central Daylight Time           Australia       UTC + 10:30 hours
    { "CDT",    -4,  0, },  //  Cuba Daylight Time              Caribbean       UTC - 4 hours
    { "CDT",    -5,  0, },  //  Central Daylight Time           North America   UTC - 5 hours
    { "CEST",    2,  0, },  //  Central European Summer Time    Europe          UTC + 2 hours
    { "CET",     1,  0, },  //  Central European Time           Africa          UTC + 1 hour
    { "CET",     1,  0, },  //  Central European Time           Europe          UTC + 1 hour
    { "CHADT",  13, 45, },  //  Chatham Island Daylight Time    Pacific         UTC + 13:45 hours
    { "CHAST",  12, 45, },  //  Chatham Island Std Time         Pacific         UTC + 12:45 hours
    { "CKT",    -10, 0, },  //  Cook Island Time                Pacific         UTC - 10 hours
    { "CLST",   -3,  0, },  //  Chile Summer Time               South America   UTC - 3 hours
    { "CLT",    -4,  0, },  //  Chile Std Time                  South America   UTC - 4 hours
    { "COT",    -5,  0, },  //  Colombia Time                   South America   UTC - 5 hours
    { "CST",     8,  0, },  //  China Std Time                  Asia            UTC + 8 hours
    { "CST",     9, 30, },  //  Central Std Time                Australia       UTC + 9:30 hours
    { "CST",    -6, 0,  },  //  Central Std Time                Central America UTC - 6 hours
    { "CST",    -5, 0,  },  //  Cuba Std Time                   Caribbean       UTC - 5 hours
    { "CST",    -6, 0,  },  //  Central Std Time                North America   UTC - 6 hours
    { "CVT",    -1, 0,  },  //  Cape Verde Time                 Africa          UTC - 1 hour
    { "CXT",     7, 0,  },  //  Christmas Island Time           Australia       UTC + 7 hours
    { "ChST",   10, 0,  },  //  Chamorro Std Time               Pacific         UTC + 10 hours
    { "DAVT",    7, 0,  },  //  Davis Time                      Antarctica      UTC + 7 hours
    { "EASST",  -5, 0,  },  //  Easter Island Summer Time       Pacific         UTC - 5 hours
    { "EAST",   -6, 0,  },  //  Easter Island Std Time          Pacific         UTC - 6 hours
    { "EAT",     3, 0,  },  //  Eastern Africa Time             Africa          UTC + 3 hours
    { "EAT",     3, 0,  },  //  East Africa Time                Indian Ocean    UTC + 3 hours
    { "ECT",    -5, 0,  },  //  Ecuador Time                    South America   UTC - 5 hours
    { "EDT",    11, 0,  },  //  Eastern Daylight Time           Australia       UTC + 11 hours
    { "EDT",    -4, 0,  },  //  Eastern Daylight Time           Caribbean       UTC - 4 hours
    { "EDT",    -4, 0,  },  //  Eastern Daylight Time           North America   UTC - 4 hours
    { "EDT",    11, 0,  },  //  Eastern Daylight Time           Pacific         UTC + 11 hours
    { "EEST",    3, 0,  },  //  Eastern European Summer Time    Africa          UTC + 3 hours
    { "EEST",    3, 0,  },  //  Eastern European Summer Time    Asia            UTC + 3 hours
    { "EEST",    3, 0,  },  //  Eastern European Summer Time    Europe          UTC + 3 hours
    { "EET",     2, 0,  },  //  Eastern European Time           Africa          UTC + 2 hours
    { "EET",     2, 0,  },  //  Eastern European Time           Asia            UTC + 2 hours
    { "EET",     2, 0,  },  //  Eastern European Time           Europe          UTC + 2 hours
    { "EGST",    0, 0,  },  //  East Greenland Summer Time      North America   UTC
    { "EGT",    -1, 0,  },  //  East Greenland Time             North America   UTC - 1 hour
    { "EST",    10, 0,  },  //  Eastern Std Time                Australia       UTC + 10 hours
    { "EST",    -5, 0,  },  //  Eastern Std Time                Central America UTC - 5 hours
    { "EST",    -5, 0,  },  //  Eastern Std Time                Caribbean       UTC - 5 hours
    { "EST",    -5, 0,  },  //  Eastern Standard Time           North America   UTC - 5 hours
    { "ET",     -5, 0,  },  //  Tiempo del Este                 Central America UTC - 5 hours
    { "ET",     -5, 0,  },  //  Tiempo del Este                 Caribbean       UTC - 5 hours
    { "ET",     -5, 0,  },  //  Tiempo Del Este                 North America   UTC - 5 hours
    { "FJST",   13, 0,  },  //  Fiji Summer Time                Pacific         UTC + 13 hours
    { "FJT",    12, 0,  },  //  Fiji Time                       Pacific         UTC + 12 hours
    { "FKST",   -3, 0,  },  //  Falkland Islands Summer Time    South America   UTC - 3 hours
    { "FKT",    -4, 0,  },  //  Falkland Island Time            South America   UTC - 4 hours
    { "FNT",    -2, 0,  },  //  Fernando de Noronha Time        South America   UTC - 2 hours
    { "GALT",   -6, 0,  },  //  Galapagos Time                  Pacific         UTC - 6 hours
    { "GAMT",   -9, 0,  },  //  Gambier Time                    Pacific         UTC - 9 hours
    { "GET",     4, 0,  },  //  Georgia Std Time                Asia            UTC + 4 hours
    { "GFT",    0,  0,  },  //  French Guiana Time              South America   UTC - 3 hours
    { "GILT",   0,  0,  },  //  Gilbert Island Time             Pacific         UTC + 12 hours
    { "GMT",    0,  0,  },  //  Greenwich Mean Time             Africa          UTC
    { "GMT",    0,  0,  },  //  Greenwich Mean Time             Europe          UTC
    { "GST",    0,  0,  },  //  Gulf Std Time                   Asia            UTC + 4 hours
    { "GYT",    0,  0,  },  //  Guyana Time                     South America   UTC - 4 hours
    { "HAA",    0,  0,  },  //  Heure Avanc l'Atlantique        Atlantic        UTC - 3 hours
    { "HAA",    0,  0,  },  //  Heure Avanc l'Atlantique        North America   UTC - 3 hours
    { "HAC",    0,  0,  },  //  Heure Avanc Centre              North America   UTC - 5 hours
    { "HADT",   0,  0,  },  //  Hawaii-Aleutian Daylight Time   North America   UTC - 9 hours
    { "HAE",    0,  0,  },  //  Heure Avanc l'Est               Caribbean       UTC - 4 hours
    { "HAE",    0,  0,  },  //  Heure Avanc l'Est               North America   UTC - 4 hours
    { "HAP",    0,  0,  },  //  Heure Avanc Pacifique           North America   UTC - 7 hours
    { "HAR",    0,  0,  },  //  Heure Avanc Rocheuses           North America   UTC - 6 hours
    { "HAST",   0,  0,  },  //  Hawaii-Aleutian Std Time        North America   UTC - 10 hours
    { "HAT",    0,  0,  },  //  Heure Avanc Terre-Neuve         North America   UTC - 2:30 hours
    { "HAY",    0,  0,  },  //  Heure Avanc Yukon               North America   UTC - 8 hours
    { "HKT",    0,  0,  },  //  Hong Kong Time                  Asia            UTC + 8 hours
    { "HLV",    0,  0,  },  //  Hora Legal de Venezuela         South America   UTC - 4:30 hours
    { "HNA",    0,  0,  },  //  Heure Normale de l'Atlantique   Atlantic        UTC - 4 hours
    { "HNA",    0,  0,  },  //  Heure Normale de l'Atlantique   Caribbean       UTC - 4 hours
    { "HNA",    0,  0,  },  //  Heure Normale de l'Atlantique   North America   UTC - 4 hours
    { "HNC",    0,  0,  },  //  Heure Normale du Centre         Central America UTC - 6 hours
    { "HNC",    0,  0,  },  //  Heure Normale du Centre         North America   UTC - 6 hours
    { "HNE",    0,  0,  },  //  Heure Normale de l'Est          Central America UTC - 5 hours
    { "HNE",    0,  0,  },  //  Heure Normale de l'Est          Caribbean       UTC - 5 hours
    { "HNE",    0,  0,  },  //  Heure Normale de l'Est          North America   UTC - 5 hours
    { "HNP",    0,  0,  },  //  Heure Normale du Pacifique      North America   UTC - 8 hours
    { "HNR",    0,  0,  },  //  Heure Normale des Rocheuses     North America   UTC - 7 hours
    { "HNT",    0,  0,  },  //  Heure Normale de Terre-Neuve    North America   UTC - 3:30 hours
    { "HNY",    0,  0,  },  //  Heure Normale du Yukon          North America   UTC - 9 hours
    { "HOVT",   0,  0,  },  //  Hovd Time                       Asia            UTC + 7 hours
    { "ICT",    0,  0,  },  //  Indochina Time                  Asia            UTC + 7 hours
    { "IDT",    0,  0,  },  //  Israel Daylight Time            Asia            UTC + 3 hours
    { "IOT",    0,  0,  },  //  Indian Chagos Time              Indian Ocean    UTC + 6 hours
    { "IRDT",   0,  0,  },  //  Iran Daylight Time              Asia            UTC + 4:30 hours
    { "IRKST",  0,  0,  },  //  Irkutsk Summer Time             Asia            UTC + 9 hours
    { "IRKT",   0,  0,  },  //  Irkutsk Time                    Asia            UTC + 8 hours
    { "IRST",   0,  0,  },  //  Iran Std Time                   Asia            UTC + 3:30 hours
    { "IST",    0,  0,  },  //  Israel Std Time                 Asia            UTC + 2 hours
    { "IST",    0,  0,  },  //  India Std Time                  Asia            UTC + 5:30 hours
    { "IST",    0,  0,  },  //  Irish Std Time                  Europe          UTC + 1 hour
    { "JST",    0,  0,  },  //  Japan Std Time                  Asia            UTC + 9 hours
    { "KGT",    0,  0,  },  //  Kyrgyzstan Time                 Asia            UTC + 6 hours
    { "KRAST",  0,  0,  },  //  Krasnoyarsk Summer Time         Asia            UTC + 8 hours
    { "KRAT",   0,  0,  },  //  Krasnoyarsk Time                Asia            UTC + 7 hours
    { "KST",    0,  0,  },  //  Korea Std Time                  Asia            UTC + 9 hours
    { "KUYT",   0,  0,  },  //  Kuybyshev Time                  Europe          UTC + 4 hours
    { "LHDT",   0,  0,  },  //  Lord Howe Daylight Time         Australia       UTC + 11 hours
    { "LHST",   0,  0,  },  //  Lord Howe Std Time              Australia       UTC + 10:30 hours
    { "LINT",   0,  0,  },  //  Line Islands Time               Pacific         UTC + 14 hours
    { "MAGST",  0,  0,  },  //  Magadan Summer Time             Asia            UTC + 12 hours
    { "MAGT",   0,  0,  },  //  Magadan Time                    Asia            UTC + 11 hours
    { "MART",   0,  0,  },  //  Marquesas Time                  Pacific         UTC - 9:30 hours
    { "MAWT",   0,  0,  },  //  Mawson Time                     Antarctica      UTC + 5 hours
    { "MDT",    0,  0,  },  //  Mountain Daylight Time          North America   UTC - 6 hours
    { "MHT",    0,  0,  },  //  Marshall Islands Time           Pacific         UTC + 12 hours
    { "MMT",    0,  0,  },  //  Myanmar Time                    Asia            UTC + 6:30 hours
    { "MSD",    0,  0,  },  //  Moscow Daylight Time            Europe          UTC + 4 hours
    { "MSK",    0,  0,  },  //  Moscow Std Time                 Europe          UTC + 3 hours
    { "MST",    0,  0,  },  //  Mountain Std Time               North America   UTC - 7 hours
    { "MUT",    0,  0,  },  //  Mauritius Time                  Africa          UTC + 4 hours
    { "MVT",    0,  0,  },  //  Maldives Time                   Asia            UTC + 5 hours
    { "MYT",    0,  0,  },  //  Malaysia Time                   Asia            UTC + 8 hours
    { "NCT",    0,  0,  },  //  New Caledonia Time              Pacific         UTC + 11 hours
    { "NDT",    0,  0,  },  //  Newfoundland Daylight Time      North America   UTC - 2:30 hours
    { "NFT",    0,  0,  },  //  Norfolk Time                    Australia       UTC + 11:30 hours
    { "NOVST",  0,  0,  },  //  Novosibirsk Summer Time         Asia            UTC + 7 hours
    { "NOVT",   0,  0,  },  //  Novosibirsk Time                Asia            UTC + 6 hours
    { "NPT",    0,  0,  },  //  Nepal Time                      Asia            UTC + 5:45 hours
    { "NST",    0,  0,  },  //  Newfoundland Std Time           North America   UTC - 3:30 hours
    { "NUT",    0,  0,  },  //  Niue Time                       Pacific         UTC - 11 hours
    { "NZDT",   0,  0,  },  //  New Zealand Daylight Time       Antarctica      UTC + 13 hours
    { "NZDT",   0,  0,  },  //  New Zealand Daylight Time       Pacific         UTC + 13 hours
    { "NZST",   0,  0,  },  //  New Zealand Std Time            Antarctica      UTC + 12 hours
    { "NZST",   0,  0,  },  //  New Zealand Std Time            Pacific         UTC + 12 hours
    { "OMSST",  0,  0,  },  //  Omsk Summer Time                Asia            UTC + 7 hours
    { "OMST",   0,  0,  },  //  Omsk Std Time                   Asia            UTC + 6 hours
    { "PDT",    0,  0,  },  //  Pacific Daylight Time           North America   UTC - 7 hours
    { "PET",    0,  0,  },  //  Peru Time                       South America   UTC - 5 hours
    { "PETST",  0,  0,  },  //  Kamchatka Summer Time           Asia            UTC + 12 hours
    { "PETT",   0,  0,  },  //  Kamchatka Time                  Asia            UTC + 12 hours
    { "PGT",    0,  0,  },  //  Papua New Guinea Time           Pacific         UTC + 10 hours
    { "PHOT",   0,  0,  },  //  Phoenix Island Time             Pacific         UTC + 13 hours
    { "PHT",    0,  0,  },  //  Philippine Time                 Asia            UTC + 8 hours
    { "PKT",    0,  0,  },  //  Pakistan Std Time               Asia            UTC + 5 hours
    { "PMDT",   0,  0,  },  //  Pierre & Miquelon Daylight Time North America   UTC - 2 hours
    { "PMST",   0,  0,  },  //  Pierre & Miquelon Std Time      North America   UTC - 3 hours
    { "PONT",   0,  0,  },  //  Pohnpei Std Time                Pacific         UTC + 11 hours
    { "PST",    0,  0,  },  //  Pacific Std Time                North America   UTC - 8 hours
    { "PST",    0,  0,  },  //  Pitcairn Std Time               Pacific         UTC - 8 hours
    { "PT",     0,  0,  },  //  Tiempo del Pac                  North America   UTC - 8 hours
    { "PWT",    0,  0,  },  //  Palau Time                      Pacific         UTC + 9 hours
    { "PYST",   0,  0,  },  //  Paraguay Summer Time            South America   UTC - 3 hours
    { "PYT",    0,  0,  },  //  Paraguay Time                   South America   UTC - 4 hours
    { "RET",    0,  0,  },  //  Reunion Time                    Africa          UTC + 4 hours
    { "SAMT",   0,  0,  },  //  Samara Time                     Europe          UTC + 4 hours
    { "SAST",   0,  0,  },  //  South Africa Std Time           Africa          UTC + 2 hours
    { "SBT",    0,  0,  },  //  Solomon IslandsTime             Pacific         UTC + 11 hours
    { "SCT",    0,  0,  },  //  Seychelles Time                 Africa          UTC + 4 hours
    { "SGT",    0,  0,  },  //  Singapore Time                  Asia            UTC + 8 hours
    { "SRT",    0,  0,  },  //  Suriname Time                   South America   UTC - 3 hours
    { "SST",    0,  0,  },  //  Samoa Std Time                  Pacific         UTC - 11 hours
    { "TAHT",   0,  0,  },  //  Tahiti Time                     Pacific         UTC - 10 hours
    { "TFT",    0,  0,  },  //  French Southern & Antarctic     Indian Ocean    UTC + 5 hours
    { "TJT",    0,  0,  },  //  Tajikistan Time                 Asia            UTC + 5 hours
    { "TKT",    0,  0,  },  //  Tokelau Time                    Pacific         UTC - 10 hours
    { "TLT",    0,  0,  },  //  East Timor Time                 Asia            UTC + 9 hours
    { "TMT",    0,  0,  },  //  Turkmenistan Time               Asia            UTC + 5 hours
    { "TVT",    0,  0,  },  //  Tuvalu Time                     Pacific         UTC + 12 hours
    { "ULAT",   0,  0,  },  //  Ulaanbaatar Time                Asia            UTC + 8 hours
    { "UYST",   0,  0,  },  //  Uruguay Summer Time             South America   UTC - 2 hours
    { "UYT",    0,  0,  },  //  Uruguay Time                    South America   UTC - 3 hours
    { "UZT",    0,  0,  },  //  Uzbekistan Time                 Asia            UTC + 5 hours
    { "VET",    0,  0,  },  //  Venezuelan Std Time             South America   UTC - 4:30 hours
    { "VLAST",  0,  0,  },  //  Vladivostok Summer Time         Asia            UTC + 11 hours
    { "VLAT",   0,  0,  },  //  Vladivostok Time                Asia            UTC + 10 hours
    { "VUT",    0,  0,  },  //  Vanuatu Time                    Pacific         UTC + 11 hours
    { "WAST",   0,  0,  },  //  West Africa Summer Time         Africa          UTC + 2 hours
    { "WAT",    0,  0,  },  //  West Africa Time                Africa          UTC + 1 hour
    { "WDT",    0,  0,  },  //  Western Daylight Time           Australia       UTC + 9 hours
    { "WEST",   0,  0,  },  //  Western European Summer Time    Africa          UTC + 1 hour
    { "WEST",   0,  0,  },  //  Western European Summer Time    Europe          UTC + 1 hour
    { "WET",    0,  0,  },  //  Western European Time           Africa          UTC
    { "WET",    0,  0,  },  //  Western European Time           Europe          UTC
    { "WFT",    0,  0,  },  //  Wallis and Futuna Time          Pacific         UTC + 12 hours
    { "WGST",   0,  0,  },  //  Western Greenland Summer Time   North America   UTC - 2 hours
    { "WGT",    0,  0,  },  //  West Greenland Time             North America   UTC - 3 hours
    { "WIB",    0,  0,  },  //  Western Indonesian Time         Asia            UTC + 7 hours
    { "WIT",    0,  0,  },  //  Eastern Indonesian Time         Asia            UTC + 9 hours
    { "WITA",   0,  0,  },  //  Central Indonesian Time         Asia            UTC + 8 hours
    { "WST",    0,  0,  },  //  Western Sahara Summer Time      Africa          UTC + 1 hour
    { "WST",    0,  0,  },  //  Western Std Time                Australia       UTC + 8 hours
    { "WST",    0,  0,  },  //  West Samoa Time                 Pacific         UTC - 11 hours
    { "WT",     0,  0,  },  //  Western Sahara Std Time         Africa          UTC
    { "YAKST",  0,  0,  },  //  Yakutsk Summer Time             Asia            UTC + 10 hours
    { "YAKT",   0,  0,  },  //  Yakutsk Time                    Asia            UTC + 9 hours
    { "YAPT",   0,  0,  },  //  Yap Time                        Pacific         UTC + 10 hours
    { "YEKST",  0,  0,  },  //  Yekaterinburg Summer Time       Asia            UTC + 6 hours
    { "YEKT",   0,  0,  },  //  Yekaterinburg Time              Asia            UTC + 5 hours
    { "",       0,  0,  }   //  Null timezone
} ;
/*
**  Functions applicable to all date classes
*/
static  uint32_t    _isdow  (const char* cpStr)
{
    //  Determine if current string amounts to a day-name
    //
    //  Arguments:  1)  cpStr   The string to be tested for a valid day-name
    //
    //  Returns:    0+  If the supplied cstr is a dayname. 0xff if not.
    int32_t nIndex ;    //  Day names iterator
    for (nIndex = 0 ; nIndex < 7 ; nIndex++)
    {
        if (!CstrCompareI(cpStr, hz_daynames_abrv[nIndex]))
            return nIndex ;
        if (!CstrCompareI(cpStr, hz_daynames_full[nIndex]))
            return nIndex ;
    }
    return NULL_DOW ;
}
static  uint32_t    _ismonth    (const char* cpStr)
{
    //  Determine if current string amounts to a month-name
    //
    //  Arguments:  1)  cpStr   The string to be tested for a valid month-name
    //
    //  Returns:    >0  If the supplied cstr is a month name
    //              0   Otherwise
    int32_t nIndex ;    //  Month names iterator
    for (nIndex = 0 ; nIndex < 12 ; nIndex++)
    {
        if (!CstrCompareI(cpStr, hz_monthnames_abrv[nIndex]))
            return nIndex + 1 ;
        if (!CstrCompareI(cpStr, hz_monthnames_full[nIndex]))
            return nIndex + 1 ;
    }
    return 0 ;
}
//  FnGrp:      IsTime
//  Category:   Text Processing
//
//  Determines if the supplied string value constitues a time and if so, set the supplied hour, minute and seconds references
//
//  Arguments:  1)  h       Hour reference
//              2)  m       Minute reference
//              3)  s       Seconds reference
//              4)  str     String value
//
//  Returns:    >0  The number of chars used in forming the time if the supplied string amounts to one.
//              0   otherwise.
uint32_t    IsTime  (uint32_t& h, uint32_t& m, uint32_t& s, const char* cpStr)
{
    //  Argument:   4)  cpStr   String value
    const char* i = cpStr ;     //  Input string iterator
    if (!i || i[0] == 0)
        return 0 ;
    if (IsDigit(i[0]) && IsDigit(i[1]) && IsDigit(i[2]) && IsDigit(i[3]) && IsDigit(i[4]) && IsDigit(i[5]) && i[6] <= CHAR_SPACE)
    {
        h = (10 * (i[0] - '0')) + (i[1] - '0') ;
        m = (10 * (i[2] - '0')) + (i[3] - '0') ;
        s = (10 * (i[4] - '0')) + (i[5] - '0') ;
        return (h < 24 && m < 60 && s < 60) ? 6 : 0 ;
    }
    if (IsDigit(i[0]) && IsDigit(i[1]) && i[2] == CHAR_COLON && IsDigit(i[3]) && IsDigit(i[4]) && i[5] == CHAR_COLON && IsDigit(i[6]) && IsDigit(i[7]) && i[8] <= CHAR_SPACE)
    {
        h = (10 * (i[0] - '0')) + (i[1] - '0') ;
        m = (10 * (i[3] - '0')) + (i[4] - '0') ;
        s = (10 * (i[6] - '0')) + (i[7] - '0') ;
        return (h < 24 && m < 60 && s < 60) ? 8 : 0 ;
    }
    return 0 ;
}
uint32_t    IsTime  (uint32_t& Y, uint32_t& M, uint32_t& D, hzString& S)
{
    //  Argument:   4)  S   String value
    return IsTime(Y, M, D, *S) ;
}
uint32_t    IsTime  (uint32_t& h, uint32_t& m, uint32_t& s, hzChain::Iter& ci)
{
    //  Argument:   4)  ci  String value
    hzChain::Iter   xi ;    //  Iterator for input chain
    uint32_t    nCount ;    //  Length of time entity
    char        buf [12] ;  //  Test buffer
    if (ci.eof())       return 0 ;
    if (!IsDigit(*ci))  return 0 ;
    xi = ci ;
    for (nCount = 0 ; !xi.eof() && (IsDigit(*xi) || *xi == CHAR_COLON) && nCount < 8 ; nCount++, xi++)
        buf[nCount] = *xi ;
    buf[nCount] = 0 ;
    return IsTime(h, m, s, buf) ? 8 : 0 ;
}
//  FnGrp:      IsDate
//  Category:   Text Processing
//
//  Determine if the supplied string amounts to a date.
//
//  Arguments:  1)  Y       Year reference
//              2)  M       Month reference
//              3)  D       Day reference
//              4)  cpStr   The test charachter string
//
//  Returns:    >0  The number of chars used in forming the date if the supplied string does amount to one.
//              0   If the supplied string does not amount to to a valid date.
uint32_t    IsDate  (uint32_t& Y, uint32_t& M, uint32_t& D, const char* cpStr)
{
    const       char*   i = cpStr ;     //  String iterator
    uint32_t    nDigit = 0 ;            //  Number of digits encountered
    uint32_t    nSlash = 0 ;            //  Number of slashes encountered
    uint32_t    nAlpha = 0 ;            //  Number of alphas encountered
    uint32_t    nSpace = 0 ;            //  Number of spaces encountered
    uint32_t    nComma = 0 ;            //  Number of commas encountered
    uint32_t    dow = NULL_DOW ;        //  Day of week
    uint32_t    len = 0 ;               //  Test string lenght
    Y = M = D = 0 ;
    if (!i || i[0] == 0)
        return 0 ;
    //  Check standard formats first
    if (IsDigit(i[0]) && IsDigit(i[1]))
    {
        if (IsDigit(i[2]) && IsDigit(i[3]))
        {
            if (i[4] == CHAR_FWSLASH && IsDigit(i[5]) && IsDigit(i[6]) && i[7] == CHAR_FWSLASH && IsDigit(i[8]) && IsDigit(i[9])) 
            {
                Y = ((i[0]-'0')*1000) + ((i[1]-'0')*100) + ((i[2]-'0')*10) + (i[3]-'0') ;
                M = ((i[5]-'0')*10) + (i[6]-'0') ;
                D = ((i[8]-'0')*10) + (i[9]-'0') ;
                return (Y > 9999 || !M || M > 12 || !D || D > monlen(Y, M)) ? 0 : 10 ;
            }
            if (IsDigit(i[4]) && IsDigit(i[5]) && IsDigit(i[6]) && IsDigit(i[7]))
            {
                Y = ((i[0]-'0')*1000) + ((i[1]-'0')*100) + ((i[2]-'0')*10) + (i[3]-'0') ;
                M = ((i[4]-'0')*10) + (i[5]-'0') ;
                D = ((i[6]-'0')*10) + (i[7]-'0') ;
                return (Y > 9999 || !M || M > 12 || !D || D > monlen(Y, M)) ? 0 : 8 ;
            }
        }
    }
    //  Check more elaborate formats
    for (i = cpStr ; *i ; len++, i++)
    {
        if      (IsDigit(*i))           nDigit++ ;
        else if (*i == CHAR_FWSLASH)    nSlash++ ;
        else if (IsAlpha(*i))           nAlpha++ ;
        else if (*i <= ' ')             nSpace++ ;
        else if (*i <= ',')             nComma++ ;
        else
            return 0 ;
    }
    if (nDigit > 4 && nAlpha > 2 && nSpace > 1)
    {
        //  Could have a date
        for (i = cpStr ; *i ; len++, i++)
        {
            if (IsAlpha(*i))
            {
                if (dow == NULL_DOW)
                    dow = _isdow(i) ;
                if (M <= 0)
                    M = _ismonth(i) ;
                for (; IsAlpha(*i) ; i++) ;
            }
            if (IsDigit(*i))
            {
                if (!D)
                    for (; IsDigit(*i) ; i++)   { D *= 10 ; D += (*i - '0') ; }
                if (!Y)
                    for (; IsDigit(*i) ; i++)   { Y *= 10 ; Y += (*i - '0') ; }
            }
            if (Y && M > 0 && D > 0)
                break ;
        }
    }
    else
    {
        /*
        **  Check numeric format
        */
        nDigit = 0 ;
        nSlash = 0 ;
        for (i = cpStr ; *i ; len++, i++)
        {
            if (*i == CHAR_FWSLASH)
                nSlash++ ;
            else if (IsDigit(*i))
                nDigit++ ;
            else
                break ;
        }
        if (*i > CHAR_SPACE && *i != CHAR_MINUS)
            return 0 ;
        i = cpStr ;
        if (nSlash == 0)
        {
            if (nDigit == 8)
            {
                Y = ((i[0]-'0')*1000) + ((i[1]-'0')*100) + ((i[2]-'0')*10) + (i[3]-'0') ;
                M = ((i[4]-'0')*10) + (i[5]-'0') ;
                D = ((i[6]-'0')*10) + (i[7]-'0') ;
            }
        }
        if (nSlash == 2)
        {
            for (i = cpStr ; IsDigit(*i) ; len++, i++)  { D *= 10 ; D += (*i - '0') ; }
            for (i++ ; IsDigit(*i) ; len++, i++)        { M *= 10 ; M += (*i - '0') ; }
            for (i++ ; IsDigit(*i) ; len++, i++)        { Y *= 10 ; Y += (*i - '0') ; }
        }
    }
    return (Y > 9999 || !M || M > 12 || !D || D > monlen(Y, M)) ? 0 : len ;
}
uint32_t    IsDate  (uint32_t& Y, uint32_t& M, uint32_t& D, hzString& S)    { return IsDate(Y, M, D, *S) ; }
//  FnGrp:      IsDateTime
//  Category:   Text Processing
//
//  Determine if the supplied string amounts to a date and time (does not have to be null terminated)
//
//  Arguments:  1)  Y   Year reference
//              2)  M   Month reference
//              3)  D   Day reference
//              4)  h   Hour reference
//              5)  m   Minute reference
//              6)  s   Seconds reference
//              7)  str The test charachter string
//
//  Returns:    Number of chars used in forming the time if the supplied string is at the start of a time or 0 otherwise.
uint32_t    IsDateTime  (uint32_t& Y, uint32_t& M, uint32_t& D, uint32_t& h, uint32_t& m, uint32_t& s, const char* str)
{
    const char* i = str ;       //  String iterator
    uint32_t    lenD = 0 ;      //  Length of date part
    uint32_t    lenT = 0 ;      //  Length of time part
    if (i)
    {
        lenD = IsDate(Y, M, D, i) ;
        if (lenD)
        {
            i += lenD ;
            if (*i == CHAR_SPACE || *i == CHAR_MINUS)
                i++ ;
            lenT = IsTime(h, m, s, i) ;
            if (lenT)
                return lenD + lenT + 1 ;
        }
    }
    return 0 ;
}
uint32_t    IsDateTime  (uint32_t& Y, uint32_t& M, uint32_t& D, uint32_t& h, uint32_t& m, uint32_t& s, hzString& S)
{
    return IsDateTime(Y, M, D, h, m, s, *S) ;
}
hzEcode _daysfromdate   (uint32_t& nDays, uint32_t Y, uint32_t M, uint32_t D)
{
    //  Convert calenda date into the number of days since of days since 00000101.
    //
    //  Arguments:  1)  nDays   Number of days (set by this operation)
    //              2)  Y       The given year
    //              3)  M       The given month
    //              4)  D       The given day
    //
    //  Returns:    E_RANGE If the supplied arguments Y, M and D do not amount to a valid date
    //              E_OK    If the supplied arguments Y, M and D do form a valid date
    uint32_t    y = Y ;         //  Set to stated year and then decremented to 0
    uint32_t    m = M ;         //  Start at 1 and count up to month before M (to increment days see below)
    uint32_t    days = 0 ;      //  Days since start of calenda (0000/01/01 = 0)
    bool        leap = true ;   //  Leap year indicator
    nDays = NULL_DATE ;
    if (Y < 0 || Y > 9999)          return E_RANGE ;
    if (M < 1 || M > 12)            return E_RANGE ;
    if (D < 1 || D > monlen(Y, M))  return E_RANGE ;
    //  Consider 400 year blocks
    for (; y >= 400 ; days += DAYS_IN_4C, y -= 400) ;
    //  Consider centuries (1st of which will have extra leap year)
    if (y >= 100)
        { days += DAYS_IN_LC ; y -= 100 ; leap = false ; }
    if (y >= 100)
    {
        for (; y >= 100 ; days += DAYS_IN_1C, y -= 100) ;
        leap = false ;
    }
    //  Consider 4 year blocks
    if (!leap)
    {
        if (y >= 4)
            { days += 1460 ; y -= 4 ; leap = true ; }
    }
    if (y >= 4)
    {
        for (; y >= 4 ; days += DAYS_IN_4Y, y -= 4) ;
        leap = true ;
    }
    //  Consider trailing years
    if (y && leap)
        { y-- ; days += 366 ; }
    for (; y ; days += 365, y--) ;
    //  Consider month and day
    for (m = 1 ; m < M ; m++)
        days += monlen(Y, m) ;
    days += (D - 1) ;
    nDays = days ;
    return E_OK ;
}
void    _datefromdays   (uint32_t& Y, uint32_t& M, uint32_t& D, uint32_t nDays)
{
    //  Convert the number of days since the start of the calenda, into Year, Month and Day. This is achieved by the following process
    //  1)  First divide the day number by the no of days in four centuries
    //
    //  Arguments:  1)  Y       The derived year
    //              2)  M       The derived month
    //              3)  D       The derived day
    //              4)  nDays   The number of days since the start of the calenda
    //
    //  Returns:    None
    uint32_t    d = nDays ;     //  Used to calculate date by incremental reduction of the day number
    uint32_t    len = 0 ;       //  Month length
    bool        leap ;          //  Is year derived so far a leap year?
    Y = M = D = 0 ;
    if (nDays <= 0)
        return ;
    leap = true ;
    //  Count 400 year periods
    for (d = nDays ; d >= DAYS_IN_4C ; Y += 400, d -= DAYS_IN_4C) ;
    //  Count 100 year periods (1st will have 36525 days including 25 leap years but the rest will have only 36524)
    if (d >= DAYS_IN_LC)
        { Y += 100 ; d -= DAYS_IN_LC ; leap = false ; }
    if (d >= DAYS_IN_1C)
    {
        for (; d >= DAYS_IN_1C ; Y += 100, d -= DAYS_IN_1C) ;
        leap = false ;
    }
    //  Count 4 year periods. Note that if single centries were added the year so far will not be a leap year.
    if (!leap)
    {
        if (d >= 1460)
            { Y += 4 ; d -= 1460 ; leap = true ; }
    }
    if (d >= DAYS_IN_4Y)
    {
        for (; d >= DAYS_IN_4Y ; Y += 4, d -= DAYS_IN_4Y) ;
        leap = true ;
    }
    //  Count 1 year periods. If year so far is a leap the first period is 366 days.
    if (!leap)
        for (; d >= 365 ; Y++, d -= 365) ;
    else
    {
        if (d >= 366)
        {
            Y++ ;
            d -= 366 ;
            //leap = false ;
            for (; d >= 365 ; Y++, d -= 365) ;
        }
    }
    //  Now sort month & days
    M = D = 1 ;
    for (;;)
    {
        len = monlen(Y, M) ;
        if (d < len)
            break ;
        d -= len ;
        M += 1 ;
    }
    D += d ;
}
uint64_t    RealtimeMicro   (void)
{
    //  Category:   Timer/Scheduling
    //
    //  Arguments:  None
    //  Returns:    Value of real time in microseconds
    struct timeval  tv ;    //  Recepticle for system time
    uint64_t    usecs ;     //  Real time in microseconds
    gettimeofday(&tv, 0) ;
    usecs = tv.tv_sec ;
    usecs *= 1000000 ;
    usecs += tv.tv_usec ;
    return usecs ;
}
uint64_t    RealtimeNano    (void)
{
    //  Category:   Timer/Scheduling
    //
    //  Arguments:  None
    //  Returns:    Value of real time in nanoseconds
    struct timespec tx ;    //  Recepticle for system time
    uint64_t    nano ;      //  Real time in nanoseconds
    clock_gettime(CLOCK_MONOTONIC, &tx) ;
    nano = tx.tv_sec ;
    nano *= 1000000000 ;    //L ;
    nano += tx.tv_nsec ;
    return nano ;
}
/*
**  Section 2:  Time Functions
*/
void    hzTime::SysTime (void)
{
    //  Set this hzTime instance to the system clock
    //
    //  Arguments:  None
    //  Returns:    None
    time_t      pt ;    //  Recepticle for system time
    struct tm*  t ;     //  Converted system time
    pt = time(&pt) ;
    t = localtime(&pt) ;
    m_secs = (t->tm_hour * 3600) + (t->tm_min * 60) + t->tm_sec ;
}
void    hzTime::SetTime (const hzTime& op)
{
    //  Set this hzTime instance to the supplied hzTime value
    //
    //  Arguments:  1)  op  The hzTime operand
    //  Returns:    None
    m_secs = op.m_secs ;
}
hzEcode hzTime::SetTime (uint32_t h, uint32_t m, uint32_t s)
{
    //  Set this hzTime instance to time supplied as hours, minutes and seconds
    //
    //  Arguments:  1)  h   The hour component
    //              2)  m   The minute component
    //              3)  s   The seconds component
    //
    //  Returns:    E_RANGE If the supplied hour, minute and seconds are not a valid time
    //              E_OK    If the time has been set
    if (h < 0 || h > 23)    return E_RANGE ;
    if (m < 0 || m > 59)    return E_RANGE ;
    if (s < 0 || s > 59)    return E_RANGE ;
    m_secs = (h * 3600) + (m * 60) + s ;
    return E_OK ;
}
hzEcode hzTime::SetTime (uint32_t nSecs)
{
    //  Set this hzTime instance using the number of seconds since midnight.
    //
    //  Arguments:  1)  nSecs   Number of seconds since midnight
    //
    //  Returns:    E_RANGE If the supplied number of seconds exceeds the number in a day
    //              E_OK    If the time has been set
    if (nSecs < 0 || nSecs > 86399)
        return E_RANGE ;
    m_secs = nSecs ;
    return E_OK ;
}
hzEcode hzTime::SetTime (const char* cpTimeStr)
{
    //  Set this hzTime using a string representation of the time. Allowed formats are hh:mm:ss, hh:mm and hhmmss
    //
    //  Arguments:  1)  cpTimeStr   Time char string
    //
    //  Returns:    E_ARGUMENT  If the time cstr is not supplied or blank
    //              E_BADVALUE  If the supplied cstr is not a valid time
    //              E_OK        If the time is set
    const char* i = cpTimeStr ;     //  Time string iterator
    uint32_t    h ;                 //  Hours
    uint32_t    m ;                 //  Minutes
    uint32_t    s ;                 //  Seconds
    if (!i || !i[0])
        return E_ARGUMENT ;
    if (!strcmp(cpTimeStr, "Not set"))
    {
        m_secs = NULL_TIME ;
        return E_OK ;
    }
    if (i[2] == ':' && i[5] == ':')
    {
        h  = ((i[0] - CHAR_0) * 10) ;
        h +=  (i[1] - CHAR_0) ;
        m  = ((i[3] - CHAR_0) * 10) ;
        m +=  (i[4] - CHAR_0) ;
        s  = ((i[6] - CHAR_0) * 10) ;
        s +=  (i[7] - CHAR_0) ;
    }
    else if (i[2] == ':' && i[5] == 0)
    {
        h  = ((i[0] - CHAR_0) * 10) ;
        h +=  (i[1] - CHAR_0) ;
        m  = ((i[3] - CHAR_0) * 10) ;
        m +=  (i[4] - CHAR_0) ;
        s = 0 ;
    }
    else
    {
        h  = ((i[0] - CHAR_0) * 10) ;
        h +=  (i[1] - CHAR_0) ;
        m  = ((i[2] - CHAR_0) * 10) ;
        m +=  (i[3] - CHAR_0) ;
        s  = ((i[4] - CHAR_0) * 10) ;
        s +=  (i[5] - CHAR_0) ;
    }
    if (h < 0 || h > 23)    return E_BADVALUE ;
    if (m < 0 || m > 59)    return E_BADVALUE ;
    if (s < 0 || s > 59)    return E_BADVALUE ;
    m_secs = (h * 3600) + (m * 60) + s ;
    return E_OK ;
}
#if 0
hzString  hzTime::Str  (hzDateFmt eFmt) const
{
    //  Create a hzString and populate it as a string of the supplied format. Return the hzString by value.
    //
    //  Arguments:  1)  eFmt    Requested format of output string
    //
    //  Returns:    Instance of hzString by value being the time in text form.
    hzString    v ;             //  Output string (Time)
    char        buf [32] ;      //  Working buffer
    if (m_secs == NULL_TIME)
        strcpy(buf, "Not set") ;
    else
    {
        if (eFmt & FMT_TIME_DFLT)
            sprintf(buf, "%02d%02d%02d", Hour(), Min(), Sec()) ;
        else if (eFmt & FMT_TIME_STD)
            sprintf(buf, "%02d:%02d:%02d", Hour(), Min(), Sec()) ;
        else
            strcpy(buf, "Invalid Time Format") ;
    }
    v = buf ;
    return v ;
}
#endif
const char*  hzTime::Txt  (hzDateFmt eFmt) const
{
    //  Write the time as per the supplied format.
    //
    //  Arguments:  1)  eFmt    Requested format of output string
    //
    //  Returns:    Pointer to buffer populated with the time in text form.
    //
    //  Note:   The space required is allocated from the thread scratch pad and must not be deleted by the calling function.
    _hzfunc("hzTime::Txt") ;
    char*   pBuf ;  //  Text recepticle
    pBuf = _thisfn.ScratchPad(16) ;
    if (m_secs == NULL_TIME)
        strcpy(pBuf, "Not set") ;
    else
    {
        if (eFmt & FMT_TIME_DFLT)
            sprintf(pBuf, "%02d%02d%02d", Hour(), Min(), Sec()) ;
        else if (eFmt & FMT_TIME_STD)
            sprintf(pBuf, "%02d:%02d:%02d", Hour(), Min(), Sec()) ;
        else
            strcpy(pBuf, "Invalid Time") ;
    }
    return pBuf ;
}
std::ostream&   operator<<  (std::ostream& os, const hzTime& t)
{
    //  Category:   Data Output
    //
    //  Append an outgoing stream with the time formatted as hh:mm:ss
    //
    //  Arguments:  1)  os  The output stream
    //              2)  t   The hzTime (const)
    char    buf [12] ;  //  Working buffer
    sprintf(buf, "%02d:%02d:%02d", (char) t.Hour(), (char) t.Min(), (char) t.Sec()) ;
    os << buf ;
    return os ;
}
/*
**  Section 3:  Short Date Functions
*/
void    hzSDate::SysDate    (void)
{
    //  Set a short date by the system clock
    //
    //  Arguments:  None
    //  Returns:    None
    time_t      pt ;    //  Recepticle for system time
    struct tm*  t ;     //  Converted system time
    pt = time(&pt) ;
    t = localtime(&pt) ;
    _daysfromdate(m_days, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday) ;
}
hzEcode hzSDate::SetDate    (const hzSDate& op)
{
    //  Set the short date to the supplied short date operand
    //
    //  Arguments:  1)  op  The operant short form date
    //
    //  Returns:    E_OK
    m_days = op.m_days ;
    return E_OK ;
}
hzEcode hzSDate::SetDate    (uint32_t Y, uint32_t M, uint32_t D)
{
    //  Set the short date by three supplied integers of year, month and day
    //
    //  Arguments:  1)  Y   Year
    //              2)  M   Month
    //              3)  D   Day
    //
    //  Returns:    E_RANGE If the supplied arguments Y, M and D do not amount to a valid date
    //              E_OK    If the supplied arguments Y, M and D do form a valid date
    return _daysfromdate(m_days, Y, M, D) ;
}
hzEcode hzSDate::SetDate    (uint32_t nDays)
{
    //  Set the short date by the number of days since the start of the calenda (Jan 1st 0000)
    //
    //  Arguments:  1)  nDays   Number of days
    //
    //  Returns:    E_RANGE If the supplied arguments Y, M and D do not amount to a valid date
    //              E_OK    If the supplied arguments Y, M and D do form a valid date
    if (nDays < 0 || nDays > DAYS_IN_10K)
        return E_RANGE ;
    m_days = nDays ;
    return E_OK ;
}
hzEcode hzSDate::SetDate    (const char* cpDateStr)
{
    //  Set the short date by a text string of an acceptable format
    //
    //  Arguments:  1)  cpDateStr   Date as string
    //
    //  Returns:    E_RANGE If the supplied arguments Y, M and D do not amount to a valid date
    //              E_OK    If the supplied arguments Y, M and D do form a valid date
    uint32_t    Y ;     //  Year part
    uint32_t    M ;     //  Month part
    uint32_t    D ;     //  Day part
    hzEcode     rc ;    //  Return code
    if (!cpDateStr || !cpDateStr[0])
        { m_days = NULL_DATE ; return E_OK ; }
    if (!IsDate(Y, M, D, cpDateStr))
    {
        m_days = NULL_DATE ;
        return E_FORMAT ;
    }
    return _daysfromdate(m_days, Y, M, D) ;
}
void    hzSDate::SetByEpoch (uint32_t nEpochTime)
{
    //  Set the short date using a epoch time
    //
    //  Arguments:  1)  nEpochTime  Epoch time
    //  Returns:    None
    time_t      pt ;    //  Recepticle for system time
    struct tm*  t ;     //  Converted system time
    pt = nEpochTime ;
    t = localtime(&pt) ;
    _daysfromdate(m_days, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday) ;
}
uint32_t    hzSDate::Year   (void) const
{
    //  Provide the year value of this date
    //
    //  Arguments:  None
    //  Returns:    Number being the year value of this date
    uint32_t    Y ;     //  Year part
    uint32_t    M ;     //  Month part
    uint32_t    D ;     //  Day part
    _datefromdays(Y, M, D, m_days) ;
    return Y ;
}
uint32_t    hzSDate::Month  (void) const
{
    //  Provide the month value of this date
    //
    //  Arguments:  None
    //  Returns:    Number being the month value of this date
    uint32_t    Y ;     //  Year part
    uint32_t    M ;     //  Month part
    uint32_t    D ;     //  Day part
    _datefromdays(Y, M, D, m_days) ;
    return M ;
}
uint32_t    hzSDate::Day    (void) const
{
    //  Provide the day value of this date
    //
    //  Arguments:  None
    //  Returns:    Number being the day value of this date
    uint32_t    Y ;     //  Year part
    uint32_t    M ;     //  Month part
    uint32_t    D ;     //  Day part
    _datefromdays(Y, M, D, m_days) ;
    return D ;
}
#if 0
hzString  hzSDate::Str  (hzDateFmt eFmt) const
{
    //  Produce text form of short form date in one of the following formats:-
    //
    //      1)  As YYYYMMDD
    //      2)  As dayname, monthname day year (American)
    //      3)  As dayname, day monthname year (International)
    //
    //  Arguments:  1)  eFmt    Output format
    //
    //  Returns:    Instance of hzString by value being text form of date.
    hzString    v ;         //  Result string
    uint32_t    Y ;         //  Year part
    uint32_t    M ;         //  Month part
    uint32_t    D ;         //  Day part
    char        buf[32] ;   //  Working buffer
    if (m_days == NULL_DATE)
        strcpy(buf, "Not set") ;
    else
    {
        _datefromdays(Y, M, D, m_days) ;
        switch  (eFmt)
        {
        case FMT_DATE_DFLT: //  yyyy/mm/dd
            sprintf(buf, "%04d%02d%02d", Y, M, D) ;
            break ;
        case FMT_DATE_STD:  //  Dow, day month year
            if (eFmt & FMT_DATE_USA)
                sprintf(buf, "%s %s %d %04d", hz_daynames_abrv[Dow()], hz_monthnames_abrv[M - 1], D, Y) ;
            else
                sprintf(buf, "%s %d %s %04d", hz_daynames_abrv[Dow()], D, hz_monthnames_abrv[M - 1], Y) ;
            break ;
        default:
            sprintf(buf, "Format unavailable") ;
            break ;
        }
    }
    v = buf ;
    return v ;
}
#endif
const char* hzSDate::Txt  (hzDateFmt eFmt) const
{
    //  Write the date as per the supplied format. The options are:-
    //
    //      1)  As YYYYMMDD
    //      2)  As dayname, monthname day year (American)
    //      3)  As dayname, day monthname year (International)
    //
    //  Arguments:  1)  eFmt    Requested format of output string
    //
    //  Returns:    Pointer to buffer populated with the date in text form.
    //
    //  Note:   The space required is allocated from the thread scratch pad and must not be deleted by the calling function.
    _hzfunc("hzSDate::Txt") ;
    char*       pBuf ;      //  Text recepticle
    uint32_t    Y ;         //  Year part
    uint32_t    M ;         //  Month part
    uint32_t    D ;         //  Day part
    pBuf = _thisfn.ScratchPad(32) ;
    if (m_days == NULL_DATE)
        strcpy(pBuf, "Not set") ;
    else
    {
        _datefromdays(Y, M, D, m_days) ;
        switch  (eFmt)
        {
        case FMT_DATE_DFLT: //  yyyy/mm/dd
            sprintf(pBuf, "%04d%02d%02d", Y, M, D) ;
            break ;
        case FMT_DATE_STD:  //  Dow, day month year
            if (eFmt & FMT_DATE_USA)
                sprintf(pBuf, "%s %s %d %04d", hz_daynames_abrv[Dow()], hz_monthnames_abrv[M - 1], D, Y) ;
            else
                sprintf(pBuf, "%s %d %s %04d", hz_daynames_abrv[Dow()], D, hz_monthnames_abrv[M - 1], Y) ;
            break ;
        default:
            sprintf(pBuf, "Format unavailable") ;
            break ;
        }
    }
    return pBuf ;
}
hzEcode hzSDate::AltDay     (int32_t interval)
{
    //  Alter this short date by a number of days into future/past. The changed value will not be assumed if it amounts to an out of range date.
    //
    //  Arguments:  1)  interval    Number of days into future/past
    //
    //  Returns:    E_RANGE If the supplied interval rendered this date invalid
    //              E_OK    If the supplied interval was applied
    if (interval < 0 && (m_days + interval) < 0)
        return E_RANGE ;
    if ((m_days + interval) > DAYS_IN_10K)
        return E_RANGE ;
    m_days += interval ;
    return E_OK ;
}
hzEcode hzSDate::AltMonth   (int32_t interval)
{
    //  Alter this short date by a number of months into future/past. The changed value will not be assumed if it amounts to an out of range
    //  date.
    //
    //  Arguments:  1)  interval    Number of months into future/past
    //
    //  Returns:    E_RANGE If the supplied interval rendered this date invalid
    //              E_OK    If the supplied interval was applied
    uint32_t    Y ;         //  Year part
    uint32_t    M ;         //  Month part
    uint32_t    D ;         //  Day part
    int32_t     months ;    //  Total months
    if (!interval)
        return E_OK ;
    _datefromdays(Y, M, D, m_days) ;
    months = (Y * 12) + M ;
    months += interval ;
    if (months < 0 || months >= 120000)
        return E_RANGE ;
    Y = months / 12 ;
    M = months % 12 ;
    return _daysfromdate(m_days, Y, M, D) ;
}
hzEcode hzSDate::AltYear    (int32_t interval)
{
    //  Alter this short date by a number of years into future/past. The changed value will not be assumed if it amounts to an out of range
    //  date.
    //
    //  Arguments:  1)  x   Number of years into future/past
    //
    //  Returns:    E_RANGE If the supplied interval rendered this date invalid
    //              E_OK    If the supplied interval was applied
    uint32_t    Y ;         //  Year part
    uint32_t    M ;         //  Month part
    uint32_t    D ;         //  Day part
    if (interval < 0 && (m_days + interval) < 0)
        return E_RANGE ;
    if ((m_days + interval) > DAYS_IN_10K)
        return E_RANGE ;
    _datefromdays(Y, M, D, m_days) ;
    Y += interval ;
    return _daysfromdate(m_days, Y, M, D) ;
}
const hzSDate&  hzSDate::operator=  (const hzXDate& op)
{
    //  Set a short date to the date component of a full date/time.
    //
    //  Arguments:  1)  op  Operand date
    //
    //  Returns:    Reference to this hzSDate instance
    m_days = op.NoDays() ;
    return *this ;
}
bool    hzSDate::operator== (const hzXDate& op) const
{
    //  Test if a short date is 'equal' to a long date (ignoring the time in the long date)
    //
    //  Arguments:  1)  op  Operand date
    //
    //  Returns:    True    If this short form date is equal to the date part of the supplied full date
    //              False   Otherwise
    return m_days == op.NoDays() ? true : false ;
}
std::ostream &  operator<<  (std::ostream &os, const hzSDate &d)
{
    //  Category:   Data Output
    //
    //  Stream out the textual manefestation of a short date to s stream
    //
    //  Arguments:  1)  os  Output stream
    //              2)  d   Short form date to output
    //
    //  Returns:    Reference to the supplied output stream
    char    buf[12] ;   //  Date formation buffer
    sprintf(buf, "%04d/%02d/%02d", d.Year(), d.Month(), d.Day()) ;
    os << buf ;
    return os ;
}
/*
**  Section 4:  Full Date Functions
*/
void    hzXDate::altsec     (int32_t units)
{
    //  Advance or retard a hzXDate by a number of seconds. This will alter the day date if the alteration is sufficient to cross one or more
    //  midnight boundaries.
    //
    //  Arguments:  1)  nounits Number of units (seconds) into future/past
    //
    //  Returns:    None
    int64_t S ;     //  No of full seconds
    int64_t X ;     //  No of micro-seconds
    m_hour += (units/3600) ;
    units %= 3600 ;
    S = m_usec ;
    X = units ;
    X *= 1000000 ;
    S += X ;
    if (S > 3600000000)
        { S -= 3600000000 ; m_hour++ ; }
    m_usec = (uint32_t) S ;
}
void    hzXDate::altmin (int32_t nounits)
{
    //  Advance or retard a hzXDate by a number of minutes. This will alter the day date if the alteration is sufficient to cross one or more
    //  midnight boundaries.
    //
    //  Arguments:  1)  nounits Number of units (minutes) into future/past
    //  Returns:    None
    altsec(60 * nounits) ;
}
void    hzXDate::althour    (int32_t nounits)
{
    //  Advance or retard a hzXDate by a number of hours. This will alter the day date if the alteration is sufficient to cross one or more
    //  midnight boundaries.
    //
    //  Arguments:  1)  nounits Number of units (hours) into future/past
    //  Returns:    None
    altsec(3600 * nounits) ;
}
void    hzXDate::altday (int32_t nounits)
{
    //  Advance or retard a hzXDate by a number of days.
    //
    //  Arguments:  1)  nounits Number of units (days) into future/past
    //  Returns:    None
    m_hour += (nounits * 24) ;
}
void    hzXDate::altyear    (int32_t nounits)
{
    //  Advance or retard a hzXDate by a number of years.
    //
    //  Arguments:  1)  nounits Number of units (years) into future/past
    //  Returns:    None
    uint32_t    days ;      //  Used by date - no of days conversion
    uint32_t    Y ;         //  Year part
    uint32_t    M ;         //  Month part
    uint32_t    D ;         //  Day part
    days = m_hour/24 ;
    _datefromdays(Y, M, D, days) ;
    Y += nounits ;
    _daysfromdate(days, Y, M, D) ;
    days *= 24 ;
    days += (m_hour%24) ;
    m_hour = days ;
}
void    hzXDate::altmon (int32_t nounits)
{
    //  Advance or retard a hzXDate by a number of months.
    //
    //  Arguments:  1)  nounits Number of units (months) into future/past
    //  Returns:    None
    uint32_t    days ;      //  Used by date - no of days conversion
    uint32_t    Y ;         //  Year part
    uint32_t    M ;         //  Month part
    uint32_t    D ;         //  Day part
    days = m_hour/24 ;
    _datefromdays(Y, M, D, days) ;
    if (nounits > 0)
    {
        for (; nounits > 0 ; nounits--)
        {
            if (nounits > 12)
                { Y++ ; nounits -= 11 ; continue ; }
            M = M == 12 ? 1 : M + 1 ;
        }
        if (D > monlen(Y, M))
            D = monlen(Y, M) ;
    }
    if (nounits < 0)
    {
        for (; nounits < 0 ; nounits++)
        {
            if (nounits < -12)
                { Y-- ; nounits += 11 ; continue ; }
            M = M == 1 ? 12 : M - 1 ;
        }
        if (D > monlen(Y, M))
            D = monlen(Y, M) ;
    }
    _daysfromdate(days, Y, M, D) ;
    days *= 24 ;
    days += (m_hour%24) ;
    m_hour = days ;
}
//  public functions
void    hzXDate::SysDateTime    (void)
{
    //  Set this hzXDate instance by the system clock
    //
    //  Arguments:  None
    //  Returns:    None
    struct timeval  tv ;        //  Recepticle for system time
    struct tm*      t ;         //  Converted system time
    time_t          pt ;        //  Time recepticle
    uint32_t        days ;      //  Used by date - no of days conversion
    gettimeofday(&tv, 0) ;
    pt = tv.tv_sec ;
    t = localtime(&pt) ;
    _daysfromdate(days, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday) ;
    m_hour = (days * 24) + t->tm_hour ;
    m_usec = (t->tm_min * 60) + t->tm_sec ;
    m_usec *= 1000000 ;
    m_usec += tv.tv_usec ;
}
hzEcode hzXDate::SetDate    (const char* cpDateStr)
{
    //  Set the date component of this hzXDate instance by a text string of an acceptable format
    //
    //  Arguments:  1)  cpDateStr   Date as string
    //
    //  Returns:    E_FORMAT    If the supplied cstr does not amount to a full date and time
    //              E_OK        If this full date and time was set
    uint32_t    days ;      //  Used by date - no of days conversion
    uint32_t    Y ;         //  Year part
    uint32_t    M ;         //  Month part
    uint32_t    D ;         //  Day part
    hzEcode     rc ;        //  Return code
    if (!cpDateStr || !cpDateStr[0])
        { m_hour = 0 ; m_usec = 0 ; return E_OK ; }
    if (!strcmp(cpDateStr, "Not set"))
        { m_hour = 0 ; m_usec = 0 ; return E_OK ; }
    if (!IsDate(Y, M, D, cpDateStr))
        return E_FORMAT ;
    rc = _daysfromdate(days, Y, M, D) ;
    if (rc != E_OK)
        return rc ;
    //Y = m_hour % 24 ;
    m_hour = (days * 24) ;
    //m_hour += Y ;
    return E_OK ;
}
hzEcode hzXDate::SetDateTime    (const char* i)
{
    //  Set both the date and time components of this hzXDate instance by a text string of an acceptable format
    //
    //  Arguments:  1)  cpDateStr   Time and date as string
    //
    //  Returns:    E_FORMAT    If the supplied cstr does not amount to a full date and time
    //              E_OK        If this full date and time was set
    hzTimezone  tz ;                //  Timezone
    uint32_t    dow = NULL_DOW ;    //  Doy of week
    uint32_t    len = 0 ;           //  Length of examined text
    uint32_t    Y = 0 ;             //  Year
    uint32_t    M = 0 ;             //  Month
    uint32_t    D = 0 ;             //  Day
    uint32_t    h = 0 ;             //  Hour
    uint32_t    m = 0 ;             //  Minute
    uint32_t    s = 0 ;             //  Second
    uint32_t    n ;                 //  Counter
    uint32_t    hOset = 0 ;         //  Hours in timezone offset to GMT
    uint32_t    mOset = 0 ;         //  Minutes in timezone offset to GMT
    char        buf [4] ;           //  For reading numbers
    if (!i || !i[0])
        { m_hour = 0 ; m_usec = 0 ; return E_OK ; }
    tz.clear() ;
    n = IsDateTime(Y, M, D, h, m, s, i) ;
    if (!n)
    {
        Y = M = D = 0 ;
        for (; *i ;)
        {
            if (*i < CHAR_SPACE)
                break ;
            if (*i == CHAR_SPACE || *i == CHAR_COMMA || *i == CHAR_PAROPEN || *i == CHAR_PARCLOSE)
                { len++ ; i++ ; continue ; }
            if (IsDigit(*i))
            {
                //  We are looking for a day of the month, a year or the start of a time sequence
                n = IsTime(h, m, s, i) ;
                if (n)
                    i += n ;
                else
                {
                    if (IsPosint(n, i))
                    {
                        if      (n < 32 && D == 0)      D = n ;
                        else if (n < 10000 && Y == 0)   Y = n ;
                        else
                            return E_FORMAT ;
                        for (; IsDigit(*i) ; i++) ;
                    }
                }
            }
            if (IsAlpha(*i))
            {
                //  We are looking for a day name, a month name or a timezone code
                if (dow == NULL_DOW)
                {
                    for (n = 0 ; n < 7 ; n++)
                        if (!CstrCompareI(i, hz_daynames_full[n]))
                            { dow = n ; break ; }
                    if (dow == NULL_DOW)
                    {
                        for (n = 0 ; n < 7 ; n++)
                            if (!CstrCompareI(i, hz_daynames_abrv[n]))
                                { dow = n ; break ; }
                    }
                    if (dow != 8)
                        { for (; IsAlpha(*i) ; i++) ; continue ; }
                }
                if (M == 0)
                {
                    for (n = 0 ; n < 12 ; n++)
                        if (!CstrCompareI(i, hz_monthnames_full[n]))
                            { M = n + 1 ; break ; }
                    if (M == 0)
                    {
                        for (n = 0 ; n < 12 ; n++)
                            if (!CstrCompareI(i, hz_monthnames_abrv[n]))
                                { M = n + 1 ; break ; }
                    }
                    if (M >= 0)
                        { for (; IsAlpha(*i) ; i++) ; continue ; }
                }
                if (!tz.code)
                {
                    for (n = 0 ; _hzGlobal_Timezones[n].code ; n++)
                    {
                        if (!CstrCompareI(i, _hzGlobal_Timezones[n].code))
                            { tz = _hzGlobal_Timezones[n] ; break ; }
                    }
                    if (tz.code)
                        { for (; IsAlpha(*i) ; i++) ; continue ; }
                }
                return E_FORMAT ;
            }
            if (*i == CHAR_PLUS || *i == CHAR_MINUS)
            {
                //  We are looking for a timezone offset to GMT (of the form +/-dddd)
                i++ ;   buf[0] = *i ;
                i++ ;   buf[1] = *i ;
                i++ ;   buf[2] = *i ;
                i++ ;   buf[3] = *i ;
    
                if (IsDigit(buf[0]) && IsDigit(buf[1]) && IsDigit(buf[2]) && IsDigit(buf[3]))
                {
                    hOset = ((buf[0] - '0') * 10) + (buf[1] - '0') ;
                    mOset = ((buf[2] - '0') * 10) + (buf[3] - '0') ;
                    if (hOset > 23 || mOset > 59)
                        return E_FORMAT ;
                    i++ ;
                    continue ;
                }
                return E_FORMAT ;
            }
        }
    }
    if (SetDate(Y, M, D) != E_OK)   return E_FORMAT ;
    if (SetTime(h, m, s) != E_OK)   return E_FORMAT ;
    return E_OK ;
}
hzEcode hzXDate::SetDate    (uint32_t Y, uint32_t M, uint32_t D)
{
    //  Set the date component of this hzXDate instance by three numeric arguments of Y, M and D
    //
    //  Arguments:  1)  Y   Year
    //              2)  M   Month
    //              3)  D   Day
    //
    //  Returns:    E_RANGE If the supplied year, month and day does not amount to a valid date
    //              E_OK    If this date was set
    uint32_t    hour ;      //  Current hour
    uint32_t    days ;      //  No of days since 0000/01/01
    hzEcode     rc ;        //  Return code
    if (m_hour == 0)
        hour = 0 ;
    else
        hour = m_hour % 24 ;
    rc = _daysfromdate(days, Y, M, D) ;
    if (rc != E_OK)
        { m_hour = 0 ; m_usec = 0 ; return rc ; }
    m_hour = (days * 24) + hour ;
    return E_OK ;
}
hzEcode hzXDate::SetDate    (uint32_t nDays, uint32_t nSecs)
{
    //  Set the date and time components of this hzXDate as two numeric arguments, the number of days since Jan 1 0000 and the number of seconds
    //  since midnight.
    //
    //  Arguments:  1)  nDays   Total days since Jan 1 0000
    //              2)  nSecs   Seconds since midnight
    //
    //  Returns:    E_RANGE If the supplied day count equals or exceeds the number of days in 10,000 years or the seconds exceed number in a day
    //              E_OK    If the time is set
    if (nDays < 0 || nDays > DAYS_IN_10K)   return E_RANGE ;
    if (nSecs < 0 || nSecs > SECS_IN_DAY)   return E_RANGE ;
    m_hour = (nDays * 24) + (nSecs/3600) ;
    m_usec = (nSecs%3600) * 1000000 ;
    return E_OK ;
}
hzEcode hzXDate::SetDate    (uint64_t xdVal)
{
    //  Set the date and time components of this hzXDate by a single 64-bit value. This function is provided in order to retrieve dates from repositories in the
    //  HadronZoo Database Suite which stores full dates as 64 bit unsigned numbers.
    //
    //  Argument:   The date as a 64 bit value
    //
    //  Returns:    E_RANGE If the supplied day count equals or exceeds the number of days in 10,000 years or the seconds exceed number in a day
    //              E_OK    If the time is set
    m_usec = xdVal & 0xffffffff ;
    xdVal >>= 32 ;
    m_hour = xdVal & 0xffffffff ;
    
    //m_hour = ((xdVal & 0xffffffff00000000) >> 32) ;
    return E_OK ;
}
hzEcode hzXDate::SetDate    (uint32_t nDays)
{
    //  Set the date component of this hzXDate instance by number of days since Jan 1 0000
    //
    //  Arguments:  1)  nDays   Total days since Jan 1 0000
    //
    //  Returns:    E_RANGE If the supplied day count equals or exceeds the number of days in 10,000 years.
    //              E_OK    If the time is set
    if (nDays >= DAYS_IN_10K)
        return E_RANGE ;
    uint32_t    hour ;      //  Current hour
    hour = m_hour % 24 ;
    m_hour = nDays * 24 ;
    m_hour += hour ;
    return E_OK ;
}
hzEcode hzXDate::SetTime    (const char* i)
{
    //  Set the time component of this hzXDate using a string representation of the time. Allowed formats are hh:mm:ss, hh:mm and hhmmss
    //
    //  Arguments:  1)  nSecs   Seconds since midnight
    //
    //  Returns:    E_RANGE If the supplied cstr does not amount to a legal time
    //              E_OK    If the time is set
    uint32_t    h ;     //  Hours
    uint32_t    m ;     //  Minutes
    uint32_t    s ;     //  Seconds
    if (!i)
        return E_OK ;
    if (i[2] == ':' && i[5] == ':')
    {
        h  = ((i[0] - CHAR_0) * 10) ;
        h +=  (i[1] - CHAR_0) ;
        m  = ((i[3] - CHAR_0) * 10) ;
        m +=  (i[4] - CHAR_0) ;
        s  = ((i[6] - CHAR_0) * 10) ;
        s +=  (i[7] - CHAR_0) ;
    }
    else
    {
        h  = ((i[0] - CHAR_0) * 10) ;
        h +=  (i[1] - CHAR_0) ;
        m  = ((i[2] - CHAR_0) * 10) ;
        m +=  (i[3] - CHAR_0) ;
        s  = ((i[4] - CHAR_0) * 10) ;
        s +=  (i[5] - CHAR_0) ;
    }
    if (h < 0 || h > 23)    return E_RANGE ;
    if (m < 0 || m > 59)    return E_RANGE ;
    if (s < 0 || s > 59)    return E_RANGE ;
    m_hour -= (m_hour % 24) ;
    m_hour += h ;
    m_usec = ((m * 60) + s) * 1000000 ;
    return E_OK ;
}
hzEcode hzXDate::SetTime    (uint32_t h, uint32_t m, uint32_t s)
{
    //  Set the time component of this hzXDate to time supplied as hours, minutes and seconds
    //
    //  Arguments:  1)  h   Hour
    //              2)  m   Minutes
    //              3)  s   Seconds
    //
    //  Returns:    E_RANGE If the supplied hour, minute and seconds do not amount to a legal time
    //              E_OK    If the time is set
    if (h < 0 || h > 23)    return E_RANGE ;
    if (m < 0 || m > 59)    return E_RANGE ;
    if (s < 0 || s > 59)    return E_RANGE ;
    m_hour -= (m_hour % 24) ;
    m_hour += h ;
    m_usec = ((m * 60) + s) * 1000000 ;
    return E_OK ;
}
hzEcode hzXDate::SetTime    (uint32_t nSecs)
{
    //  Set the time component of this hzXDate to time supplied as number of seconds since midnight
    //
    //  Arguments:  1)  nEpochTime  The date supplied as an epoch value
    //
    //  Returns:    E_RANGE If the supplied number of seconds exceed the number in a day
    //              E_OK    If the time is set
    if (nSecs > SECS_IN_DAY)
        return E_RANGE ;
    m_hour -= (m_hour % 24) ;
    m_hour += (nSecs / 3600) ;
    m_usec = (nSecs % 3600) * 1000000 ;
    return E_OK ;
}
void    hzXDate::SetByEpoch (uint32_t nEpochTime)
{
    //  Set the date and time components of this hzXDate using the epoch time (number of seconds since midnight , Jan 1 1970)
    //
    //  Arguments:  1)  nEpochTime  The date supplied as an epoch value
    //  Returns:    None
    struct tm*  t ;         //  Converted system time
    time_t      pt ;        //  Time recepticle
    uint32_t    days ;      //  Used by date - no of days conversion
    pt = nEpochTime ;
    t = localtime(&pt) ;
    _daysfromdate(days, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday) ;
    m_hour = (days * 24) + t->tm_hour ;
    m_usec = ((t->tm_min * 60) + t->tm_sec) * 1000000 ;
}
void    hzXDate::SetByEpoch (uint32_t nEpochTime, uint32_t usec)
{
    //  Set the date and time components of this hzXDate using the epoch time (number of seconds since midnight , Jan 1 1970). Also set the
    //  number of microseconds
    //
    //  Arguments:  1)  nEpochTime  The date supplied as an epoch value
    //              2)  usec        Number of microseconds
    //  Returns:    None
    struct tm*  t ;         //  Converted system time
    time_t      pt ;        //  Time recepticle
    uint32_t    days ;      //  Used by date - no of days conversion
    pt = nEpochTime ;
    t = localtime(&pt) ;
    _daysfromdate(days, t->tm_year + 1900, t->tm_mon + 1, t->tm_mday) ;
    m_hour = (days * 24) + t->tm_hour ;
    m_usec = ((t->tm_min * 60) + t->tm_sec) * 1000000 ;
    m_usec += usec ;
}
void    hzXDate::altdate    (hzInterval unit, int32_t nounits)
{
    //  Alter this hzXDate by a number of units which may be either days, months, years, hours, minutes or seconds
    //
    //  Arguments:  1)  unit    Interval duration
    //              2)  nounits Number of interval durations
    //
    //  Returns:    None
    switch (unit)
    {
    case DAY:       altday(nounits) ;       break ;
    case MONTH:     altmon(nounits) ;       break ;
    case YEAR:      altyear(nounits) ;      break ;
    case HOUR:      althour(nounits) ;      break ;
    case MINUTE:    altmin(nounits) ;       break ;
    case SECOND:    altsec(nounits) ;       break ;
    }
}
uint32_t    hzXDate::Year   (void) const
{
    //  Return the year part from this hzXDate
    //
    //  Arguments:  None
    //  Returns:    Number being year part of this date
    uint32_t    Y ;     //  Year
    uint32_t    M ;     //  Month
    uint32_t    D ;     //  Day of month
    _datefromdays(Y, M, D, m_hour/24) ;
    return Y ;
}
uint32_t    hzXDate::Month  (void) const
{
    //  Return the month part from this hzXDate
    //
    //  Arguments:  None
    //  Returns:    Number being month of year
    uint32_t    Y ;     //  Year
    uint32_t    M ;     //  Month
    uint32_t    D ;     //  Day of month
    _datefromdays(Y, M, D, m_hour/24) ;
    return M ;
}
uint32_t    hzXDate::Day    (void) const
{
    //  Return the day of month part from this hzXDate
    //
    //  Arguments:  None
    //  Returns:    Number being day of month
    uint32_t    Y ;     //  Year
    uint32_t    M ;     //  Month
    uint32_t    D ;     //  Day of month
    _datefromdays(Y, M, D, m_hour/24) ;
    return D ;
}
uint64_t    hzXDate::AsVal  (void) const
{
    //  Return the day of month part from this hzXDate
    //
    //  Arguments:  None
    //  Returns:    Number being day of month
    uint64_t    v ;
    //v = (m_hour << 32) + m_usec ;
    v = m_hour ;
    v <<= 32 ;
    v |= m_usec ;
    return v ;
}
uint32_t    hzXDate::DaysInYear (void) const
{
    //  Arguments:  None
    //  Returns:    Number of days since Jan 1
    uint32_t    d ;     //  Days since 0000/01/01
    d = m_hour / 24 ;
    for (; d >= DAYS_IN_4C ; d -= DAYS_IN_4C) ;
    for (; d >= DAYS_IN_1C ; d -= DAYS_IN_1C) ;
    for (; d >= DAYS_IN_4Y ; d -= DAYS_IN_4Y) ;
    if (d >= DAYS_IN_LY)
    {
        d -= DAYS_IN_LY ;
        for (; d >= DAYS_IN_1Y ; d -= DAYS_IN_1Y) ;
    }
    return d ;
}
hzSDate hzXDate::Date   (void) const
{
    //  Extract the date part of the full dte and time as hzSDate (short form date) instance
    //
    //  Arguments:  None
    //  Returns:    Instance of hzSDate as the date part of the full date
    hzSDate d ;     //  hzTime instance
    d.SetDate(NoDays()) ;
    return d ;
}
hzTime  hzXDate::Time   (void) const
{
    //  Extract the time part of the full data and time as a hzTime instance
    //
    //  Arguments:  None
    //  Returns:    Instance of hzTime as the time part of the full date
    hzTime  t ;     //  hzTime instance
    t.SetTime(NoSecs()) ;
    return t ;
}
uint32_t    hzXDate::AsEpoch    (void) const
{
    //  Return the time & date as an epoch
    //
    //  Arguments:  None
    //  Returns:    Number of seconds passed since epoch start
    uint32_t    epoch ;     //  Target epoch
    if (m_hour < HOURS_B4_EPOCH || m_hour > HOURS_EPOCH_END)
        return 0 ;
    epoch = m_hour - HOURS_B4_EPOCH ;
    epoch *= SECS_IN_HOUR ;
    epoch += (m_usec/1000000) ;
    return epoch ;
}
const char* hzXDate::Txt    (hzDateFmt eFmt) const
{
    //  Write the date and time as per the supplied format. The options are:-
    //
    //      1)  As YYYYMMDD
    //      2)  As dayname, monthname day year (American)
    //      3)  As dayname, day monthname year (International)
    //
    //  Arguments:  1)  eFmt    Requested format of output string
    //
    //  Returns:    Pointer to buffer populated with the date in text form.
    //
    //  Note:   The space required is allocated from the thread scratch pad and must not be deleted by the calling function.
    _hzfunc("hzXDate::Txt") ;
    char*   pBuf ;          //  Text recepticle
    bool    bUS ;           //  True if American format required
    bool    bFN ;           //  True if full names are required
    int32_t nSofar = 0 ;    //  Length so far.
    if (!m_hour && !m_usec)
        return 0 ;
    pBuf = _thisfn.ScratchPad(32) ;
    //  if (m_hour & 0x80000000)
    //      { strcpy(pBuf, "Not set") ; return pBuf ; }
    //  Do we have the full blown internet format?
    if (eFmt == FMT_DT_INET)
    {
        if (bUS)
            sprintf(pBuf, "%s %s %d %04d %02d:%02d:%02d +0000 (GMT)", hz_daynames_abrv[Dow()], hz_monthnames_abrv[Month() - 1], Day(), Year(), Hour(), Min(), Sec()) ;
        else
            sprintf(pBuf, "%s %d %s %04d %02d:%02d:%02d +0000 (GMT)", hz_daynames_abrv[Dow()], Day(), hz_monthnames_abrv[Month() - 1], Year(), Hour(), Min(), Sec()) ;
        return pBuf ;
    }
    //  US or International
    bUS = eFmt & FMT_DATE_USA ? true : false ;
    bFN = eFmt & FMT_DATE_FULL ? true : false ;
    //  Do we have a dow? if so print this first
    if (eFmt & FMT_DATE_DOW)
    {
        if (bFN)
            strcpy(pBuf, hz_daynames_full[Dow()]) ;
        else
            strcpy(pBuf, hz_daynames_abrv[Dow()]) ;
        nSofar = strlen(pBuf) ;
    }
    //  Now print date
    if (eFmt & FMT_DT_MASK_DATES)
    {
        if (nSofar)
        {
            pBuf[nSofar] = CHAR_SPACE ;
            nSofar++ ;
        }
        switch (eFmt & FMT_DT_MASK_DATES)
        {
        case FMT_DATE_DFLT: //  YYYYMMDD
                            nSofar += sprintf(pBuf + nSofar, "%04d%02d%02d", Year(), Month(), Day());
                            break ;
        case FMT_DATE_STD:  //  YYYY/MM/DD
                            nSofar += sprintf(pBuf + nSofar, "%04d/%02d/%02d", Year(), Month(), Day());
                            break ;
        case FMT_DATE_NORM: //  DD/MM/YYYY (UK) or MM/DD/YYYY (US)
                            if (bUS)
                                nSofar += sprintf(pBuf + nSofar, "%02d/%02d/%04d", Month(), Day(), Year()) ;
                            else
                                nSofar += sprintf(pBuf + nSofar, "%02d/%02d/%04d", Day(), Month(), Year()) ;
                            break ;
        case FMT_DATE_FULL: //  Day_of_month+monthname+YYYY (UK) or monthname+day_of_month+YYYY (US)
                            if (bUS)
                                nSofar += sprintf(pBuf + nSofar, "%s %d %04d", hz_monthnames_abrv[Month() - 1], Day(), Year()) ;
                            else
                                nSofar += sprintf(pBuf + nSofar, "%d %s %04d", Day(), hz_monthnames_abrv[Month() - 1], Year()) ;
                            break ;
        }
    }
    //  Now print time
    if (eFmt & FMT_DT_MASK_TIMES)
    {
        if (nSofar)
        {
            pBuf[nSofar] = CHAR_MINUS ;
            nSofar++ ;
        }
        switch (eFmt & FMT_DT_MASK_TIMES)
        {
        case FMT_TIME_DFLT: //  Time HHMMSS
                            nSofar += sprintf(pBuf + nSofar, "%02d%02d%02d", Hour(), Min(), Sec()) ;
                            break ;
        case FMT_TIME_STD:  //  Time HH:MM:SS
                            nSofar += sprintf(pBuf + nSofar, "%02d:%02d:%02d", Hour(), Min(), Sec()) ;
                            break ;
        case FMT_TIME_USEC: //  Time HH:MM:SS.uSec
                            nSofar += sprintf(pBuf + nSofar, "%02d:%02d:%02d.%06d", Hour(), Min(), Sec(), uSec()) ;
                            break ;
        }
    }
    //  Now print timezone if applicable
    if (eFmt & FMT_DT_MASK_TZONE)
    {
        if (nSofar)
        {
            pBuf[nSofar] = CHAR_SPACE ;
            nSofar++ ;
        }
        switch (eFmt & FMT_DT_MASK_TIMES)
        {
        case FMT_TZ_CODE:   strcpy(pBuf + nSofar, "GMT") ;          break ;
        case FMT_TZ_NUM:    strcpy(pBuf + nSofar, "+0000") ;        break ;
        case FMT_TZ_BOTH:   strcpy(pBuf + nSofar, "+0000 (GMT)") ;  break ;
        }
    }
    return pBuf ;
}
bool    hzXDate::operator== (const hzXDate& op) const
{
    //  Return true if the operand hzXDate is the same as this
    //
    //  Arguments:  1)  op  Operand date
    return (m_hour == op.m_hour && m_usec == op.m_usec) ;
}
bool    hzXDate::operator!= (const hzXDate& op) const
{
    //  Return true if the operand hzXDate is not the same as this
    //
    //  Arguments:  1)  op  Operand date
    return (m_hour != op.m_hour || m_usec != op.m_usec) ;
}
bool    hzXDate::operator<  (const hzXDate& op) const
{
    //  Return true if this hzXDate is earlier than the operand
    //
    //  Arguments:  1)  op  Operand date
    return (m_hour < op.m_hour || (m_hour == op.m_hour && m_usec < op.m_usec)) ;
}
bool    hzXDate::operator<= (const hzXDate& op) const
{
    //  Return true if this hzXDate is earlier than or the same as the operand
    //
    //  Arguments:  1)  op  Operand date
    return ((m_hour == op.m_hour && m_usec == op.m_usec)
        || (m_hour < op.m_hour || (m_hour == op.m_hour && m_usec < op.m_usec))) ;
}
bool    hzXDate::operator>  (const hzXDate& op) const
{
    //  Return true if this hzXDate is later than the operand
    //
    //  Arguments:  1)  op  Operand date
    return (m_hour > op.m_hour || (m_hour == op.m_hour && m_usec > op.m_usec)) ;
}
bool    hzXDate::operator>= (const hzXDate& op) const
{
    //  Return true if this hzXDate is later than or the same as the operand
    //
    //  Arguments:  1)  op  Operand date
    return ((m_hour == op.m_hour && m_usec == op.m_usec) || (m_hour > op.m_hour || (m_hour == op.m_hour && m_usec > op.m_usec))) ;
}
//  static functions (may get rid of these)
int32_t hzXDate::datecmp    (hzXDate& a, hzXDate& b)
{
    //  Compare two hzXDate instances, A and B. Return 1 if A>B, -1 if A<B and 0 if A and B are equal
    //
    //  Arguments:  1)  a   First date
    //              2)  b   Second date
    //
    //  Returns:    +1  If a > b
    //              -1  If a < b
    //              0   If a and b are equal
    if ( a.m_hour > b.m_hour)
        return 1 ;
    if (a.m_hour < b.m_hour)
        return -1 ;
    if (a.m_usec > b.m_usec)
        return 1 ;
    if (a.m_usec < b.m_usec)
        return -1 ;
    return 0 ;
    //return    a.m_hour > b.m_hour ?  1 : a.m_hour < b.m_hour ? -1 : a.m_usec > b.m_usec ?  1 : a.m_usec < b.m_usec ? -1 : 0 ;
}
std::ostream &  operator<<  (std::ostream &os, const hzXDate &d)
{
    //  Category:   Data Output
    //
    //  Stream out the textual manefestation of a hzXDate to s stream
    //
    //  Arguments:  1)  os  Output stream
    //              2)  d   Date/time to output
    //
    //  Returns:    Reference to the supplied output stream.
    char    buf[40] ;   //  Sprintf buffer
    sprintf(buf, "%04d/%02d/%02d %02d:%02d:%02d.%06d",
        (int16_t) d.Year(),
        (char) d.Month(),
        (char) d.Day(),
        (char) d.Hour(),
        (char) d.Min(),
        (char) d.Sec(),
        (int32_t) d.uSec()) ;
    os << buf ;
    return os ;
}
uint32_t    IsFormalDate    (hzXDate& date, hzChain::Iter& ci)
{
    //  Category:   Text Processing
    //
    //  Determine if the supplied chain iterator is at the begining of a legal date and/or time. If it is then the supplied hzXDate reference is populated. The
    //  lenth of the date/time sequence is returned so that the calling process can choose to advance the iterator by this length. The iterator is not advanced
    //  by this function
    //
    //  Expects dates of the form "day_name, dd month_name yyyy hh:mm:ss Time-Zone"
    //
    //  Arguments:  1)  date    The full date
    //              2)  ci      Chain iterator to be tested
    //
    //  Returns:    Number being length of the date string if found
 
    chIter      zi ;                //  Internal chain iterator
    hzTimezone  tz ;                //  Timezone
    uint32_t    n ;                 //  Counter
    uint32_t    dow = NULL_DOW ;    //  Day of week
    uint32_t    len = 0 ;           //  Length of examined text
    uint32_t    Y = 0 ;             //  Year
    uint32_t    M = 0 ;             //  Month
    uint32_t    D = 0 ;             //  Day
    uint32_t    h = 0 ;             //  Hour
    uint32_t    m = 0 ;             //  Minute
    uint32_t    s = 0 ;             //  Second
    uint32_t    hOset = 0 ;         //  Hours in timezone offset to GMT
    uint32_t    mOset = 0 ;         //  Minutes in timezone offset to GMT
    char        numBuf[4] ;         //  For reading numbers
    zi = ci ;
    tz.clear() ;
    for (; !zi.eof() ;)
    {
        if (*zi < CHAR_SPACE)
            break ;
        if (*zi == CHAR_SPACE || *zi == CHAR_COMMA || *zi == CHAR_PAROPEN || *zi == CHAR_PARCLOSE)
            { len++ ; zi++ ; continue ; }
        if (IsDigit(*zi))
        {
            //  We are looking for a day of the month, a year or the start of a time sequence
            n = IsTime(h, m, s, zi) ;
            if (n)
                zi += n ;
            else
            {
                for (n = 0 ; IsDigit(*zi) ; zi++)
                    { n *= 10 ; n += (*zi - '0') ; }
                if      (n < 32 && D == 0)      D = n ;
                else if (n < 10000 && Y == 0)   Y = n ;
                else
                    return 0 ;
            }
        }
        if (IsAlpha(*zi))
        {
            //  We are looking for a day name, a month name or a timezone code
            if (dow == NULL_DOW)
            {
                for (n = 0 ; n < 7 ; n++)
                    if (zi == hz_daynames_full[n])
                        { dow = n ; break ; }
                if (dow == NULL_DOW)
                {
                    for (n = 0 ; n < 7 ; n++)
                        if (zi == hz_daynames_abrv[n])
                            { dow = n ; break ; }
                }
                if (dow != 8)
                    { for (; IsAlpha(*zi) ; zi++) ; continue ; }
            }
            if (M == 0)
            {
                for (n = 0 ; n < 12 ; n++)
                    if (zi == hz_monthnames_full[n])
                        { M = n + 1 ; break ; }
                if (M == 0)
                {
                    for (n = 0 ; n < 12 ; n++)
                        if (zi == hz_monthnames_abrv[n])
                            { M = n + 1 ; break ; }
                }
                if (M >= 0)
                    { for (; IsAlpha(*zi) ; zi++) ; continue ; }
            }
            if (!tz.code)
            {
                for (n = 0 ;; n++)
                {
                    if (zi == _hzGlobal_Timezones[n].code)
                        { tz = _hzGlobal_Timezones[n] ; break ; }
                }
                if (tz.code)
                    { for (; IsAlpha(*zi) ; zi++) ; continue ; }
            }
            return 0 ;
        }
        if (*zi == CHAR_PLUS || *zi == CHAR_MINUS)
        {
            //  We are looking for a timezone offset to GMT (of the form +/-dddd)
            zi++ ;  numBuf[0] = *zi ;
            zi++ ;  numBuf[1] = *zi ;
            zi++ ;  numBuf[2] = *zi ;
            zi++ ;  numBuf[3] = *zi ;
            if (IsDigit(numBuf[0]) && IsDigit(numBuf[1]) && IsDigit(numBuf[2]) && IsDigit(numBuf[3]))
            {
                hOset = ((numBuf[0] - '0') * 10) + (numBuf[1] - '0') ;
                mOset = ((numBuf[2] - '0') * 10) + (numBuf[3] - '0') ;
                if (hOset > 23 || mOset > 59)
                    return 0 ;
                zi++ ;
                continue ;
            }
            return 0 ;
        }
    }
    if (date.SetDate(Y, M, D) != E_OK)  return 0 ;
    if (date.SetTime(h, m, s) != E_OK)  return 0 ;
    ci = zi ;
    return len ;
}
/*
**  Date Formats
*/
const char* s_dt_types[] =
{
    "FMT_DT_UNKNOWN",   //  Invalid or uninitialized type
    //  Dates contrl flags
    "FMT_DATE_DOW",     //  Print the dow (this will appear first)
    "FMT_DATE_USA",     //  Where applicable, put day before month
    "FMT_DATE_ABBR",    //  Write words (eg dow and monthname) out in short form
    "FMT_DATE_FULL",    //  Write words (eg dow and monthname) out in full
    //  Date only formats
    "FMT_DATE_DFLT",    //  YYYYMMDD
    "FMT_DATE_STD",     //  YYYY/MM/DD
    "FMT_DATE_NORM",    //  DD/MM/YYYY (UK) or MM/DD/YYYY (US)
    "FMT_DATE_FULL",    //  Day_of_month+monthname+YYYY (UK) or monthname+day_of_month+YYYY (US)
    //  Time only formats
    "FMT_TIME_DFLT",    //  Time HHMMSS
    "FMT_TIME_STD",     //  Time HH:MM:SS
    "FMT_TIME_USEC",    //  Time HH:MM:SS.uSec
    //  Timezones (always last)
    "FMT_TZ_CODE",      //  Timezone as code
    "FMT_TZ_DIGITS",    //  Timezone as digits
    "FMT_TZ_BOTH",      //  Timezone as digits plus (code in braces)
} ;
const hzString  DateFmt2Str (hzDateFmt fmt)
{
    //  Category:   Diagnostics
    //
    //  Convert a HadroZoo Format to its text name for diagnostic purposes
    //
    //  Arguments:  1)  fmt     Enumerated HadronZoo text format
    //
    //  Returns:    Instance of hzString by value being format/layout description
    hzChain     Z ;                 //  Output chain
    hzString    S ;                 //  Target hzString instance
    //  Dates contrl flags
    if (fmt & FMT_DATE_DOW)     { Z.AddByte(CHAR_PLUS) ; Z << s_dt_types[1] ; }
    if (fmt & FMT_DATE_USA)     { Z.AddByte(CHAR_PLUS) ; Z << s_dt_types[2] ; }
    if (fmt & FMT_DATE_ABBR)    { Z.AddByte(CHAR_PLUS) ; Z << s_dt_types[3] ; }
    if (fmt & FMT_DATE_FULL)    { Z.AddByte(CHAR_PLUS) ; Z << s_dt_types[4] ; }
    //  Date only formats
    if (fmt & FMT_DATE_DFLT)    { Z.AddByte(CHAR_PLUS) ; Z << s_dt_types[5] ; }
    if (fmt & FMT_DATE_STD)     { Z.AddByte(CHAR_PLUS) ; Z << s_dt_types[6] ; }
    if (fmt & FMT_DATE_NORM)    { Z.AddByte(CHAR_PLUS) ; Z << s_dt_types[7] ; }
    if (fmt & FMT_DATE_FULL)    { Z.AddByte(CHAR_PLUS) ; Z << s_dt_types[8] ; }
    //  Time only formats
    if (fmt & FMT_TIME_DFLT)    { Z.AddByte(CHAR_PLUS) ; Z << s_dt_types[9] ; }
    if (fmt & FMT_TIME_STD)     { Z.AddByte(CHAR_PLUS) ; Z << s_dt_types[10] ; }
    if (fmt & FMT_TIME_USEC)    { Z.AddByte(CHAR_PLUS) ; Z << s_dt_types[11] ; }
    //  Timezones (always last)
    if (fmt & FMT_TZ_CODE)      { Z.AddByte(CHAR_PLUS) ; Z << s_dt_types[12] ; }
    if (fmt & FMT_TZ_NUM)       { Z.AddByte(CHAR_PLUS) ; Z << s_dt_types[13] ; }
    if (fmt & FMT_TZ_BOTH)      { Z.AddByte(CHAR_PLUS) ; Z << s_dt_types[14] ; }
    if (Z.Size())
        S = Z ;
    else
        S = s_dt_types[FMT_DT_UNKNOWN] ;
    return S ;
}
hzDateFmt   Str2DateFmt (const hzString& S)
{
    //  Category:   Config
    //
    //  Convert the name of a HadronZoo Format to the enum
    //
    //  Arguments:  1)  S   String presumed to indicate an enumerated HadronZoo text format
    //
    //  Returns:    Enum value being the format/layout matching supplied description
    int32_t x = 0 ;     //  Format flagset
    //  Dates contrl flags
    if (S.Contains("FMT_DATE_DOW"))     x |= FMT_DATE_DOW ;
    if (S.Contains("FMT_DATE_USA"))     x |= FMT_DATE_USA ;
    if (S.Contains("FMT_DATE_ABBR"))    x |= FMT_DATE_ABBR ;
    if (S.Contains("FMT_DATE_FULL"))    x |= FMT_DATE_FULL ;
    //  Date only formats
    if (S.Contains("FMT_DATE_DFLT"))    x |= FMT_DATE_DFLT ;
    if (S.Contains("FMT_DATE_STD"))     x |= FMT_DATE_STD ;
    if (S.Contains("FMT_DATE_NORM"))    x |= FMT_DATE_NORM ;
    if (S.Contains("FMT_DATE_FORM"))    x |= FMT_DATE_FORM ;
    //  Time only formats
    if (S.Contains("FMT_TIME_DFLT"))    x |= FMT_TIME_DFLT ;
    if (S.Contains("FMT_TIME_STD"))     x |= FMT_TIME_STD ;
    if (S.Contains("FMT_TIME_USEC"))    x |= FMT_TIME_USEC ;
    //  Timezones (always last)
    if (S.Contains("FMT_TZ_CODE"))      x |= FMT_TZ_CODE ;
    if (S.Contains("FMT_TZ_DIGITS"))    x |= FMT_TZ_NUM ;
    if (S.Contains("FMT_TZ_BOTH"))      x |= FMT_TZ_BOTH ;
    return (hzDateFmt) x ;
}