LCOV - code coverage report
Current view: top level - slsh/lib - timestamp.sl (source / functions) Hit Total Coverage
Test: all.lcov Lines: 90 91 98.9 %
Date: 2022-08-02 14:41:00 Functions: 4 4 100.0 %

          Line data    Source code
       1             : % Copyright (C) 2020-2021, 2022 John E. Davis
       2             : %
       3             : % This file is part of the S-Lang Library and may be distributed under the
       4             : % terms of the GNU General Public License.  See the file COPYING for
       5             : % more information.
       6             : %---------------------------------------------------------------------------
       7             : % The code here attemps to convert a human readable representation of a
       8             : % timestamp, such as Wed May 13 02:38:34 2020 to a Unix time (number
       9             : % of secs since the Unix epoch)
      10             : %
      11             : % Public funtions:
      12             : %
      13             : %    timestamp_parse:
      14             : %       Parses a timestamp and Returns the number of seconds since
      15             : %       the Unix EPOCH (1970-01-01T00:00:00Z)
      16             : %
      17             : private variable Months
      18           1 :   = ["jan", "feb", "mar", "apr", "may", "jun",
      19             :      "jul", "aug", "sep", "oct", "nov", "dec"];
      20             : 
      21             : % There is an extensive list of timezone abbreviations at
      22             : % <https://www.timeanddate.com/time/zones/>.  The problem with
      23             : % abbreviations is that they are not unique.  For example, CST
      24             : % could refer to Austrailia, North America, or China.
      25             : % The ones here are used by slrn.
      26           1 : private variable TZMap = Assoc_Type[Int_Type, 0];
      27           1 : TZMap["EDT"] = -400;                 % US Eastern Daylight Time
      28           1 : TZMap["EST"] = -500;                 % US Eastern Standard Time
      29           1 : TZMap["CDT"] = -500;                 % US Central Daylight Time
      30           1 : TZMap["CST"] = -600;                 % US Central
      31           1 : TZMap["MDT"] = -600;                 % US Mountain Daylight Time
      32           1 : TZMap["MST"] = -700;                 % US Mountain
      33           1 : TZMap["PDT"] = -700;                 % US Pacific Daylight Time
      34           1 : TZMap["PST"] = -800;                 % US Pacific
      35           1 : TZMap["GMT"] = 0;
      36           1 : TZMap["UTC"] = 0;
      37           1 : TZMap["Z"] = 0;
      38           1 : TZMap["CET"] = 100;                  % Central European
      39           1 : TZMap["MET"] = 100;                  % Middle European
      40           1 : TZMap["MEZ"] = 100;                  % Middle European
      41           1 : TZMap["EET"] = 200;                  % Eastern European   
      42           1 : TZMap["MSK"] = 300;                  % Moscow
      43           1 : TZMap["HKT"] = 800;                  % Hong Kong   
      44           1 : TZMap["JST"] = 900;                  % Japan Standard   
      45           1 : TZMap["KST"] = 900;                  % Korean Standard   
      46           1 : TZMap["CAST"] = 930;                 % Central Autsralian   
      47           1 : TZMap["EAST"] = 1000;                % Eastern Autsralian   
      48           1 : TZMap["NZST"] = 1200;                % New Zealand Autsralian   
      49             : 
      50             : private define map_tzstr_string (tzstr)
      51             : {
      52          20 :    return TZMap[strtrim (strup(tzstr))];
      53             : }
      54             : 
      55             : private variable Cumulative_Days_Per_Month =
      56           1 :   int (cumsum ([0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]));
      57             : 
      58           1 : private variable TS_Formats = {};
      59             : private define add_ts_format (re, indices, month_is_int)
      60             : {
      61           7 :    variable s = struct
      62             :      {
      63           7 :         re = re,
      64           7 :         month_is_int = month_is_int,
      65           7 :         indices = indices,
      66             :      };
      67             : 
      68           7 :    list_append (TS_Formats, s);
      69             : }
      70             : % Tue 12 May 2020 04:37:54 [PM] EDT
      71           1 : add_ts_format (`^[a-zA-Z,]*`                   %  weekday  
      72             :                + ` *\(\d\d?\)[ -]\([a-zA-Z]+\)[ -]\(\d\d\d*\),?`   %  day month year
      73             :                + ` *\(\d\d?\)[:.]\(\d\d\)[:.]?\(\d*\)`  %  hh:mm:ss
      74             :                + ` *\(.*\)`,                   %  AM/PM + tz
      75             :                [1,2,3,4,5,6,7], 0);
      76             : 
      77             : %  Sun, Dec 04, 1994 11:05:52 GMT
      78           1 : add_ts_format (`^[a-zA-Z,]+`
      79             :                + ` +\([a-zA-Z]\{3,\}\) \(\d+\),? *\(\d\d\d*\)`%  month, day, year  
      80             :                + ` +\(\d\d?\):\(\d\d\):?\(\d*\)`%  hh:mm:ss  
      81             :                + ` *\(.*\)`,                   %  tz
      82             :                [2,1,3,4,5,6,7], 0);
      83             : 
      84             : % Dec 04, 1994 11:05:52 GMT
      85           1 : add_ts_format (`^\([a-zA-Z]\{3,\}\) \(\d+\),? *\(\d\d\d*\)`%  month, day, year  
      86             :                + ` +\(\d\d?\):\(\d\d\):?\(\d*\)`%  hh:mm:ss  
      87             :                + ` *\(.*\)`,                   %  tz
      88             :                [2,1,3,4,5,6,7], 0);
      89             : 
      90             : % 2020-09-12T21:17:30 <tz-offset>
      91           1 : add_ts_format (`^\(\d\d\d\d\)-?\(\d\d\)-?\(\d\d\)`
      92             :                + `T\(\d\d\):?\(\d\d\):?\(\d\d\)`
      93             :                + ` *\(.*\)`,
      94             :                [3,2,1,4,5,6,7], 1);
      95             : 
      96             : % 5/12/2020, 5:31:57 PM
      97           1 : add_ts_format (`\(\d\d?\)/\(\d\d?\)/\(\d\d\d*\),?`
      98             :                + ` *\(\d\d?\):\(\d\d\):?\(\d*\)`
      99             :                + ` *\(.*\)`,
     100             :                [2,1,3,4,5,6,7], 1);
     101             : 
     102             : % Dec 4 11:05:52 2020
     103           1 : add_ts_format (`^\([a-zA-Z]\{3,\}\) +\(\d+\),?`  %  month, day
     104             :                + ` +\(\d\d?\):\(\d\d\):?\(\d*\),?`%  hh:mm:ss
     105             :                + ` +\(\d\d\d*\)`       %  year
     106             :                + ` *\(.*\)`,           %  tz  
     107             :                [2,1,6,3,4,5,7], 0);
     108             : 
     109             : % Tue Dec 4 11:05:52 2020
     110           1 : add_ts_format (`^[A-Za-z,]+`
     111             :                + ` +\([a-zA-Z]\{3,\}\) +\(\d+\),?`  %  month, day
     112             :                + ` +\(\d\d?\):\(\d\d\):?\(\d*\),?`%  hh:mm:ss
     113             :                + ` +\(\d\d\d*\)`       %  year  
     114             :                + ` *\(.*\)`,           %  tz  
     115             :                [2,1,6,3,4,5,7], 0);
     116             : 
     117           1 : private variable Last_TS_Index = 0;
     118             : private define guess_local_timezone_offset ()
     119             : {
     120           2 :    variable now = _time(), tm = gmtime(now);
     121           1 :    tm.tm_isdst = -1;  % Force a lookup to see if DST is in effect
     122           1 :    variable secs = now - mktime (tm);
     123           1 :    variable hours = secs/3600;
     124           1 :    return 100*hours + (secs - 3600*hours)/60;
     125             : }
     126             : 
     127             : define timestamp_parse (timestamp)
     128             : {
     129          29 :    timestamp = strtrim (timestamp);
     130             :    variable day, month, year, hours, minutes, secs, tz, tzstr;
     131          29 :    variable num = length (TS_Formats);
     132          29 :    loop (num)
     133             :      {
     134          80 :         variable fmt = TS_Formats[Last_TS_Index];
     135             : 
     136          80 :         variable matches = string_matches (timestamp, fmt.re);
     137          80 :         if (matches == NULL)
     138             :           {
     139          51 :              Last_TS_Index = (Last_TS_Index + 1) mod num;
     140          51 :              continue;
     141             :           }
     142          29 :         variable ind = fmt.indices;
     143          29 :         day = atoi (matches[ind[0]]);
     144          29 :         month = matches[ind[1]];
     145          29 :         year = atoi (matches[ind[2]]);
     146          29 :         hours = atoi (matches[ind[3]]);
     147          29 :         minutes = atoi (matches[ind[4]]);
     148          29 :         secs = atoi (matches[ind[5]]);
     149          29 :         tzstr = matches[ind[6]];
     150             : 
     151          29 :         if (fmt.month_is_int)
     152          10 :           month = atoi (month) - 1; %  0 to 11
     153             :         else
     154             :           {
     155          21 :              if (strbytelen (month) > 3) month = month[[0,1,2]];
     156          19 :              month = wherefirst (Months == strlow(month));
     157          19 :              if (month == NULL) return NULL;
     158             :           }
     159          29 :         break;
     160             :      }
     161           0 :    then return NULL;
     162             : 
     163          29 :    if (year < 100)
     164             :      {
     165             :         % No century
     166           4 :         year += 1900;
     167           8 :         if (year < 1950) year += 100;
     168             :      }
     169          29 :    tzstr = strtrim (tzstr);
     170             : 
     171             :    % Take care of AM/PM
     172          29 :    if (((tzstr[0] == 'A') || (tzstr[0] == 'P'))
     173             :        && (tzstr[1] == 'M')
     174             :        && ((tzstr[2] == 0) || (tzstr[2] == ' ')))
     175             :      {
     176           9 :         if (tzstr[0] == 'P')
     177           9 :           hours += 12;
     178           9 :         tzstr = strtrim (tzstr[[2:]]);
     179             :      }
     180             : 
     181          29 :    tzstr = strreplace (tzstr, ":", "");
     182          29 :    if (tzstr == "")
     183           1 :      tz = guess_local_timezone_offset ();
     184             :    else
     185             :      {
     186          28 :         tz = atoi (tzstr);
     187          28 :         if ((tz == 0) && (tzstr[0] != '+') && (tzstr[0] != '-'))
     188          20 :           tz = map_tzstr_string (tzstr);
     189             :      }
     190             : 
     191          29 :    day--;                              %  offset from 0
     192             :    % Compute the cumulative number of days, accounting for a leap year
     193          29 :    day += Cumulative_Days_Per_Month[month];
     194          29 :    if ((month > 2)
     195             :        && (0 == (year mod 4))
     196             :        && ((year mod 100) || (0 == (year mod 400))))
     197          26 :      day++;
     198             : 
     199          29 :    year -= 1970;                       %  Unix EPOCH
     200          29 :    day += 365*year + (year+1)/4;       %  leap year, every 4 years from 72
     201             : 
     202             :    % The TZ is hhmm form, so 600 is 6 hours, and 0 minutes
     203          29 :    hours -= tz/100;
     204          29 :    minutes -= tz mod 100;
     205             : 
     206          29 :    return secs + 60L*(minutes + 60L*(hours + 24L*day));
     207             : }

Generated by: LCOV version 1.13