File: timestamp.sl

package info (click to toggle)
slang2 2.3.3-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 11,488 kB
  • sloc: ansic: 101,756; sh: 3,435; makefile: 1,046; pascal: 440
file content (207 lines) | stat: -rw-r--r-- 6,526 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
% Copyright (C) 2020-2021, 2022 John E. Davis
%
% This file is part of the S-Lang Library and may be distributed under the
% terms of the GNU General Public License.  See the file COPYING for
% more information.
%---------------------------------------------------------------------------
% The code here attemps to convert a human readable representation of a
% timestamp, such as Wed May 13 02:38:34 2020 to a Unix time (number
% of secs since the Unix epoch)
%
% Public funtions:
%
%    timestamp_parse:
%       Parses a timestamp and Returns the number of seconds since
%       the Unix EPOCH (1970-01-01T00:00:00Z)
%
private variable Months
  = ["jan", "feb", "mar", "apr", "may", "jun",
     "jul", "aug", "sep", "oct", "nov", "dec"];

% There is an extensive list of timezone abbreviations at
% <https://www.timeanddate.com/time/zones/>.  The problem with
% abbreviations is that they are not unique.  For example, CST
% could refer to Austrailia, North America, or China.
% The ones here are used by slrn.
private variable TZMap = Assoc_Type[Int_Type, 0];
TZMap["EDT"] = -400;		       % US Eastern Daylight Time
TZMap["EST"] = -500;		       % US Eastern Standard Time
TZMap["CDT"] = -500;		       % US Central Daylight Time
TZMap["CST"] = -600;		       % US Central
TZMap["MDT"] = -600;		       % US Mountain Daylight Time
TZMap["MST"] = -700;		       % US Mountain
TZMap["PDT"] = -700;		       % US Pacific Daylight Time
TZMap["PST"] = -800;		       % US Pacific
TZMap["GMT"] = 0;
TZMap["UTC"] = 0;
TZMap["Z"] = 0;
TZMap["CET"] = 100;		       % Central European
TZMap["MET"] = 100;		       % Middle European
TZMap["MEZ"] = 100;		       % Middle European
TZMap["EET"] = 200;		       % Eastern European   
TZMap["MSK"] = 300;		       % Moscow
TZMap["HKT"] = 800;		       % Hong Kong   
TZMap["JST"] = 900;		       % Japan Standard   
TZMap["KST"] = 900;		       % Korean Standard   
TZMap["CAST"] = 930;		       % Central Autsralian   
TZMap["EAST"] = 1000;		       % Eastern Autsralian   
TZMap["NZST"] = 1200;		       % New Zealand Autsralian   

private define map_tzstr_string (tzstr)
{
   return TZMap[strtrim (strup(tzstr))];
}

private variable Cumulative_Days_Per_Month =
  int (cumsum ([0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]));

private variable TS_Formats = {};
private define add_ts_format (re, indices, month_is_int)
{
   variable s = struct
     {
	re = re,
	month_is_int = month_is_int,
	indices = indices,
     };

   list_append (TS_Formats, s);
}
% Tue 12 May 2020 04:37:54 [PM] EDT
add_ts_format (`^[a-zA-Z,]*`		       %  weekday  
	       + ` *\(\d\d?\)[ -]\([a-zA-Z]+\)[ -]\(\d\d\d*\),?`   %  day month year
	       + ` *\(\d\d?\)[:.]\(\d\d\)[:.]?\(\d*\)`  %  hh:mm:ss
	       + ` *\(.*\)`,		       %  AM/PM + tz
	       [1,2,3,4,5,6,7], 0);

%  Sun, Dec 04, 1994 11:05:52 GMT
add_ts_format (`^[a-zA-Z,]+`
	       + ` +\([a-zA-Z]\{3,\}\) \(\d+\),? *\(\d\d\d*\)`%  month, day, year  
	       + ` +\(\d\d?\):\(\d\d\):?\(\d*\)`%  hh:mm:ss  
	       + ` *\(.*\)`,		       %  tz
	       [2,1,3,4,5,6,7], 0);

% Dec 04, 1994 11:05:52 GMT
add_ts_format (`^\([a-zA-Z]\{3,\}\) \(\d+\),? *\(\d\d\d*\)`%  month, day, year  
	       + ` +\(\d\d?\):\(\d\d\):?\(\d*\)`%  hh:mm:ss  
	       + ` *\(.*\)`,		       %  tz
	       [2,1,3,4,5,6,7], 0);

% 2020-09-12T21:17:30 <tz-offset>
add_ts_format (`^\(\d\d\d\d\)-?\(\d\d\)-?\(\d\d\)`
	       + `T\(\d\d\):?\(\d\d\):?\(\d\d\)`
	       + ` *\(.*\)`,
	       [3,2,1,4,5,6,7], 1);

% 5/12/2020, 5:31:57 PM
add_ts_format (`\(\d\d?\)/\(\d\d?\)/\(\d\d\d*\),?`
	       + ` *\(\d\d?\):\(\d\d\):?\(\d*\)`
	       + ` *\(.*\)`,
	       [2,1,3,4,5,6,7], 1);

% Dec 4 11:05:52 2020
add_ts_format (`^\([a-zA-Z]\{3,\}\) +\(\d+\),?`  %  month, day
	       + ` +\(\d\d?\):\(\d\d\):?\(\d*\),?`%  hh:mm:ss
	       + ` +\(\d\d\d*\)`       %  year
	       + ` *\(.*\)`,	       %  tz  
	       [2,1,6,3,4,5,7], 0);

% Tue Dec 4 11:05:52 2020
add_ts_format (`^[A-Za-z,]+`
	       + ` +\([a-zA-Z]\{3,\}\) +\(\d+\),?`  %  month, day
	       + ` +\(\d\d?\):\(\d\d\):?\(\d*\),?`%  hh:mm:ss
	       + ` +\(\d\d\d*\)`       %  year  
	       + ` *\(.*\)`,	       %  tz  
	       [2,1,6,3,4,5,7], 0);

private variable Last_TS_Index = 0;
private define guess_local_timezone_offset ()
{
   variable now = _time(), tm = gmtime(now);
   tm.tm_isdst = -1;  % Force a lookup to see if DST is in effect
   variable secs = now - mktime (tm);
   variable hours = secs/3600;
   return 100*hours + (secs - 3600*hours)/60;
}

define timestamp_parse (timestamp)
{
   timestamp = strtrim (timestamp);
   variable day, month, year, hours, minutes, secs, tz, tzstr;
   variable num = length (TS_Formats);
   loop (num)
     {
	variable fmt = TS_Formats[Last_TS_Index];

	variable matches = string_matches (timestamp, fmt.re);
	if (matches == NULL)
	  {
	     Last_TS_Index = (Last_TS_Index + 1) mod num;
	     continue;
	  }
	variable ind = fmt.indices;
	day = atoi (matches[ind[0]]);
	month = matches[ind[1]];
	year = atoi (matches[ind[2]]);
	hours = atoi (matches[ind[3]]);
	minutes = atoi (matches[ind[4]]);
	secs = atoi (matches[ind[5]]);
	tzstr = matches[ind[6]];

	if (fmt.month_is_int)
	  month = atoi (month) - 1; %  0 to 11
	else
	  {
	     if (strbytelen (month) > 3) month = month[[0,1,2]];
	     month = wherefirst (Months == strlow(month));
	     if (month == NULL) return NULL;
	  }
	break;
     }
   then return NULL;

   if (year < 100)
     {
	% No century
	year += 1900;
	if (year < 1950) year += 100;
     }
   tzstr = strtrim (tzstr);

   % Take care of AM/PM
   if (((tzstr[0] == 'A') || (tzstr[0] == 'P'))
       && (tzstr[1] == 'M')
       && ((tzstr[2] == 0) || (tzstr[2] == ' ')))
     {
	if (tzstr[0] == 'P')
	  hours += 12;
	tzstr = strtrim (tzstr[[2:]]);
     }

   tzstr = strreplace (tzstr, ":", "");
   if (tzstr == "")
     tz = guess_local_timezone_offset ();
   else
     {
	tz = atoi (tzstr);
	if ((tz == 0) && (tzstr[0] != '+') && (tzstr[0] != '-'))
	  tz = map_tzstr_string (tzstr);
     }

   day--;			       %  offset from 0
   % Compute the cumulative number of days, accounting for a leap year
   day += Cumulative_Days_Per_Month[month];
   if ((month >= 2)  % month runs 0 .. 11
       && (0 == (year mod 4))
       && ((year mod 100) || (0 == (year mod 400))))
     day++;

   year -= 1970;		       %  Unix EPOCH
   day += 365*year + (year+1)/4;       %  leap year, every 4 years from 72

   % The TZ is hhmm form, so 600 is 6 hours, and 0 minutes
   hours -= tz/100;
   minutes -= tz mod 100;

   return secs + 60L*(minutes + 60L*(hours + 24L*day));
}