]> git.8kb.co.uk Git - dataflex/df32func/blob - src/df32/tstamp.inc
Just pushing the latest copy of my development / staging DataFlex stuff into git...
[dataflex/df32func] / src / df32 / tstamp.inc
1 //-------------------------------------------------------------------------\r
2 // tstamp.inc\r
3 //      This file contains some DataFlex 3.2 Console Mode functions\r
4 //      to provide extended timestamp and timezone manipulation capabilities.\r
5 //      Depends on functions in both win32.inc and string.inc/\r
6 //\r
7 // This file is to be included in df32func.mk\r
8 //\r
9 // Copyright (c) 2006-2015, glyn@8kb.co.uk\r
10 // \r
11 // df32func/tstamp.inc\r
12 //-------------------------------------------------------------------------\r
13 \r
14 //-------------------------------------------------------------------------\r
15 // Global tokenizer used for splitting timezon data\r
16 //-------------------------------------------------------------------------\r
17 object tzTok is a StringTokenizer\r
18 end_object\r
19 \r
20 //-------------------------------------------------------------------------\r
21 // Functions\r
22 //-------------------------------------------------------------------------\r
23 \r
24 // Convert a textual timestamp to a posix number\r
25 // Also see posixtime and posixtime_reverse in date.inc\r
26 function timestemp_to_posix global string inTs returns number\r
27         local number l_posix l_hr l_min l_sec l_msec\r
28         local date l_date\r
29 \r
30         // leap seconds not coded        \r
31         if ((length(inTs) < 10) or (mid(inTs, 1, 3) <> "/") or (mid(inTs, 1, 6)  <> "/") or (mid(inTs, 1, 11)  <> " ")) begin\r
32             custom_error ERROR_CODE_INVALID_TIMESTAMP$ ERROR_MSG_INVALID_TIMESTAMP ERROR_DETAIL_INVALID_TIMESTAMP "DD/MM/YYYY"\r
33         end\r
34         if (((length(inTs) >= 16) and (mid(inTs, 1, 14)  <> ":")) or ((length(inTs) >= 19) and (mid(inTs, 1, 17)  <> ":"));\r
35             or ((length(inTs) >= 21) and (mid(inTs, 1, 20)  <> "."))) begin\r
36             custom_error ERROR_CODE_INVALID_TIMESTAMP$ ERROR_MSG_INVALID_TIMESTAMP ERROR_DETAIL_INVALID_TIMESTAMP "DD/MM/YYYY HH:mm:SS.mss"\r
37         end\r
38         \r
39         move (left(inTs,(pos(" ",inTs)-1))) to l_date\r
40         if (length(inTs) >= 13) move (mid(inTs, 2, 12)) to l_hr\r
41         else move 0 to l_hr\r
42         if (length(inTs) >= 16) move (mid(inTs, 2, 15)) to l_min\r
43         else move 0 to l_min\r
44         if (length(inTs) >= 19) move (mid(inTs, 2, 18)) to l_sec\r
45         else move 0 to l_sec\r
46         if (length(inTs) >= 21) move (mid(inTs, 3, 21)) to l_msec\r
47         else move 0 to l_msec\r
48         \r
49         if ((l_hr > 23) or (l_min > 59) or (l_sec > 59) or (l_msec > 999);\r
50             or (l_hr < 0) or (l_min < 0) or (l_sec < 0) or (l_msec < 0)) begin\r
51             custom_error ERROR_CODE_INVALID_TIMESTAMP$ ERROR_MSG_INVALID_TIMESTAMP\r
52         end\r
53         \r
54         calc (((integer(l_date))-(integer(date("01/01/1970"))))*86400) to l_posix\r
55         calc (((((l_hr*60)+l_min)*60)+l_sec)+(l_msec/1000)+l_posix) to l_posix\r
56         \r
57         function_return l_posix\r
58 end_function\r
59 \r
60 // Convert a posix number to a textual timestamp\r
61 function posix_to_timestamp global number argv returns string\r
62         local date l_date\r
63         local number l_subt \r
64         local integer l_hr l_min l_sec l_msec\r
65         local string l_posix_reverse\r
66         \r
67         // leap seconds not coded\r
68         calc ((argv/86400)+(integer(date("01/01/1970")))) to l_date\r
69         calc (argv-(((integer(l_date))-(integer(date("01/01/1970"))))*86400)) to l_subt\r
70         calc (l_subt/3600) to l_hr\r
71         calc ((l_subt-(l_hr*3600))/60) to l_min\r
72         calc (l_subt-(((l_hr*60)+l_min)*60)) to l_sec\r
73         calc ((l_subt-((((l_hr*60)+l_min)*60)+l_sec))*1000) to l_msec\r
74         \r
75         if ((l_hr > 23) or (l_min > 59) or (l_sec > 59) or (l_msec > 999);\r
76             or (l_hr < 0) or (l_min < 0) or (l_sec < 0) or (l_msec < 0)) begin\r
77             custom_error ERROR_CODE_INVALID_POSIX_NUMBER$ ERROR_MSG_INVALID_POSIX_NUMBER argv\r
78         end        \r
79                 \r
80     move "" to l_posix_reverse\r
81     move (string(l_date)+" "+zeropad(l_hr,2)+":"+zeropad(l_min,2)+":"+zeropad(l_sec,2)+"."+zeropad(l_msec,3)) to l_posix_reverse\r
82            \r
83         function_return l_posix_reverse\r
84 end_function\r
85 \r
86 // Adjust supplied timestamp by supplied milliseconds\r
87 function timestamp_adjust global string inTs number inMSeconds returns string\r
88     local string retTimestamp\r
89     local number nPosix\r
90     \r
91     move (timestemp_to_posix(inTs)) to nPosix\r
92     move (posix_to_timestamp(nPosix+(inMSeconds/1000))) to retTimestamp\r
93     \r
94     function_return retTimestamp\r
95 end_function\r
96 \r
97 // Limited as we'd need to know the actual timestamp referred to to do months, years etc.\r
98 // Supports millisecond, second, minute, hour, day, week\r
99 function interval_to_posix global string argv returns number\r
100     local number l_value l_return\r
101     local integer l_i\r
102     local string l_unit l_interval  l_numerics\r
103     \r
104     move (trim(argv)) to l_interval\r
105     move 0 to l_return\r
106     \r
107     if (length(l_interval) <> 0) begin\r
108         move 1 to l_i\r
109         while (l_i < length(l_interval))\r
110             move 0 to l_value\r
111             move "" to l_numerics\r
112             move "" to l_unit\r
113             while ((ascii(mid(l_interval, 1, l_i)) > 47) and (ascii(mid(l_interval, 1, l_i)) < 58))\r
114                 append l_numerics (mid(l_interval, 1, l_i))\r
115                 increment l_i\r
116             loop\r
117             while (ascii(mid(l_interval, 1, l_i)) = 32)\r
118                 increment l_i\r
119             loop\r
120 \r
121             while ((ascii(mid(l_interval, 1, l_i)) > 64) and (ascii(mid(l_interval, 1, l_i)) < 91);\r
122                 or (ascii(mid(l_interval, 1, l_i)) > 96) and (ascii(mid(l_interval, 1, l_i)) < 123))\r
123                 append  l_unit (mid(l_interval, 1, l_i))\r
124                 increment l_i\r
125             loop\r
126 \r
127             move (lowercase(trim(l_unit))) to l_unit        \r
128             case begin\r
129                 case ((mid(l_unit,11,1) = "millisecond") or (mid(l_unit,2,1) = "ms")) move (l_numerics/number(1000)) to l_value\r
130                 case break\r
131                 case ((mid(l_unit,6,1) = "second") or (mid(l_unit,1,1) = "s")) move l_numerics to l_value\r
132                 case break\r
133                 case ((mid(l_unit,6,1) = "minute") or (mid(l_unit,1,1) = "m")) move (l_numerics*60) to l_value\r
134                 case break          \r
135                 case ((mid(l_unit,4,1) = "hour") or (mid(l_unit,1,1) = "h")) move (l_numerics*3600) to l_value                  \r
136                 case break                      \r
137                 case ((mid(l_unit,3,1) = "day") or (mid(l_unit,1,1) = "d")) move (l_numerics*86400) to l_value\r
138                 case break                                  \r
139                 case ((mid(l_unit,4,1) = "week") or (mid(l_unit,1,1) = "w")) move (l_numerics*604800) to l_value\r
140                 case break                                  \r
141             case end\r
142 \r
143             move (l_return+l_value) to l_return\r
144         loop\r
145     end\r
146     \r
147     function_return l_return\r
148 end_function\r
149 \r
150 // Adjust supplied timestamp by supplied interval\r
151 // Supports microsecond, millisecond, second, minute, hour, day, week, month, year, decade, century, millennium\r
152 // Negative sinage is indicated by "ago", words without preceeding units are treated as noise. E.g:\r
153 // timestamp_adjust_interval("28/02/2009 09:00:00.000", "1century 2years 1month 1day 1hour 1minute 1second and 500 milliseconds ago")\r
154 // Note that actual timestamps will be bound by posix and dataflex timestamp implementations.\r
155 function timestamp_adjust_interval global string inTs string inInterval returns string\r
156     local string retTimestamp tmpTimestamp l_unit l_interval  l_numerics\r
157     local number nPosix l_value l_return l_year l_mon l_day\r
158     local integer l_i l_iThrow l_iSign\r
159     \r
160     if ((length(inTs) < 10) or (mid(inTs, 1, 3) <> "/") or (mid(inTs, 1, 6)  <> "/")) begin\r
161         custom_error ERROR_CODE_INVALID_TIMESTAMP$ ERROR_MSG_INVALID_TIMESTAMP ERROR_DETAIL_INVALID_TIMESTAMP "DD/MM/YYYY"\r
162     end\r
163     \r
164     move (mid(inTs, 2, 1)) to l_day\r
165     move (mid(inTs, 2, 4)) to l_mon\r
166     move (mid(inTs, 4, 7)) to l_year\r
167     \r
168     if (lowercase(inInterval) contains "ago");\r
169         move -1 to l_iSign\r
170     else;\r
171         move 1 to l_iSign\r
172     \r
173     move (trim(inInterval)) to l_interval\r
174     move 0 to l_return\r
175     \r
176     if (length(l_interval) <> 0) begin\r
177         move 1 to l_i\r
178         while (l_i < length(l_interval))\r
179             move 0 to l_value\r
180             move "" to l_numerics\r
181             move "" to l_unit\r
182             while ((ascii(mid(l_interval, 1, l_i)) > 47) and (ascii(mid(l_interval, 1, l_i)) < 58))\r
183                 append l_numerics (mid(l_interval, 1, l_i))\r
184                 increment l_i\r
185             loop\r
186             while (ascii(mid(l_interval, 1, l_i)) = 32)\r
187                 increment l_i\r
188             loop\r
189             while ((ascii(mid(l_interval, 1, l_i)) > 64) and (ascii(mid(l_interval, 1, l_i)) < 91);\r
190                 or (ascii(mid(l_interval, 1, l_i)) > 96) and (ascii(mid(l_interval, 1, l_i)) < 123))\r
191                 append  l_unit (mid(l_interval, 1, l_i))\r
192                 increment l_i\r
193             loop\r
194             move (lowercase(trim(l_unit))) to l_unit        \r
195             case begin\r
196                 case ((mid(l_unit,10,1) = "millennium") or (mid(l_unit,6,1) = "millen")) move (l_year+(l_numerics*1000*l_iSign)) to l_year\r
197                 case break\r
198                 case ((mid(l_unit,7,1) = "century") or (mid(l_unit,1,1) = "c")) move (l_year+(l_numerics*100*l_iSign)) to l_year\r
199                 case break\r
200                 case ((mid(l_unit,6,1) = "decade") or (mid(l_unit,3,1) = "dec")) move (l_year+(l_numerics*10*l_iSign)) to l_year\r
201                 case break\r
202                 case ((mid(l_unit,4,1) = "year") or (mid(l_unit,1,1) = "y")) move (l_year+(l_numerics*l_iSign)) to l_year\r
203                 case break\r
204                 case ((mid(l_unit,5,1) = "month") or (mid(l_unit,3,1) = "mon")) move (l_mon+(l_numerics*l_iSign)) to l_mon\r
205                 case break\r
206                 case ((mid(l_unit,11,1) = "millisecond") or (mid(l_unit,2,1) = "ms")) move (l_numerics/number(1000)) to l_value\r
207                 case break\r
208                 case ((mid(l_unit,6,1) = "second") or (mid(l_unit,1,1) = "s")) move l_numerics to l_value\r
209                 case break\r
210                 case ((mid(l_unit,6,1) = "minute") or (mid(l_unit,3,1) = "min")) move (l_numerics*60) to l_value\r
211                 case break          \r
212                 case ((mid(l_unit,4,1) = "hour") or (mid(l_unit,1,1) = "h")) move (l_numerics*3600) to l_value                  \r
213                 case break                      \r
214                 case ((mid(l_unit,3,1) = "day") or (mid(l_unit,1,1) = "d")) move (l_numerics*86400) to l_value\r
215                 case break                                  \r
216                 case ((mid(l_unit,4,1) = "week") or (mid(l_unit,1,1) = "w")) move (l_numerics*604800) to l_value\r
217                 case break                                  \r
218             case end\r
219             move (l_return+l_value) to l_return\r
220         loop\r
221     end \r
222     \r
223     move (timestemp_to_posix(zeropad(l_day,2)+"/"+zeropad(l_mon,2)+"/"+zeropad(l_year,4)+mid(inTs,length(inTs)-10,11))) to nPosix\r
224     move (posix_to_timestamp(nPosix+(l_return*l_iSign))) to retTimestamp\r
225     \r
226     function_return retTimestamp\r
227 end_function\r
228 \r
229 // This takes the day of the week and month occourance values from a SYSTEMTIME \r
230 // in a _TIME_ZONE_INFORMATION and works out the correct day of the month\r
231 // See description under "DaylightDate" here https://msdn.microsoft.com/en-us/library/ms724253.aspx\r
232 function get_day_of_month_for_daylight_savings global integer inYear integer inMonth integer inDayOfWeek integer inDay returns integer\r
233     local integer l_dom l_i\r
234     local date l_date\r
235     \r
236     if (inDayOfWeek = 0) move 7 to inDayOfWeek\r
237     \r
238     move ("01/"+zeropad(inMonth,2)+"/"+zeropad(inYear,4)) to l_date\r
239     \r
240     while (get_day_score(l_date) <> inDayOfWeek)\r
241         increment l_date\r
242     loop\r
243     \r
244     if (integer(mid(l_date, 2, 4)) = inMonth);\r
245         move (mid(l_date, 2, 1)) to l_dom           \r
246     \r
247     for l_i from 1 to (inDay-1)\r
248         calc (l_date+7) to l_date\r
249         \r
250     if (integer(mid(l_date, 2, 4)) = inMonth);\r
251         move (mid(l_date, 2, 1)) to l_dom           \r
252     loop\r
253     \r
254     function_return l_dom\r
255 end_function\r
256 \r
257 // Get local system time and starts to pull values to adjust to UTC (needs finishing)\r
258 function systemtime_utc global returns string\r
259     local string sSystemTime sTimeZoneInformation sStdName sDlName sFormattedTime sFormattedDate sTs\r
260     local integer iSuccess iBias iStdBias iDlBias iBiasNow iLenCcTime iLenCcDate iDataLength\r
261     local pointer lpSystemTime lpTimeZoneInformation lpsFormattedTime lpsFormattedDate\r
262 \r
263     // Get the current system local time\r
264     zerotype _SYSTEMTIME to sSystemTime\r
265     getaddress of sSystemTime to lpSystemTime\r
266 \r
267     move (GetSystemTime(lpSystemTime)) to iSuccess\r
268 \r
269     if (iSuccess <> 0) begin\r
270         // Get the current system timezone information\r
271         zerotype _TIME_ZONE_INFORMATION to sTimeZoneInformation\r
272         getaddress of sTimeZoneInformation to lpTimeZoneInformation\r
273 \r
274         move (GetTimeZoneInformation(lpTimeZoneInformation)) to iSuccess\r
275             \r
276         if (iSuccess = TIME_ZONE_ID_INVALID) begin\r
277             custom_error ERROR_CODE_INVALID_SYSTEM_TIMEZONE$ ERROR_MSG_INVALID_SYSTEM_TIMEZONE\r
278         end\r
279         else begin\r
280         getbuff from sTimeZoneInformation at TIME_ZONE_INFORMATION.Bias to iBias\r
281         move (cstring(to_ascii(mid(sTimeZoneInformation, 64, 5)))) to sStdName //getbuff from sTimeZoneInformation at TIME_ZONE_INFORMATION.StandardName to sStdName //UTF-16/wchar                      \r
282         getbuff from sTimeZoneInformation at TIME_ZONE_INFORMATION.StandardBias to iStdBias\r
283         move (cstring(to_ascii(mid(sTimeZoneInformation, 64, 89)))) to sDlName //getbuff from sTimeZoneInformation at TIME_ZONE_INFORMATION.DaylightName to sDlName //UTF-16/wcharsDlName \r
284         getbuff from sTimeZoneInformation at TIME_ZONE_INFORMATION.DaylightBias to iDlBias\r
285 \r
286         zerostring 255 to sFormattedTime\r
287         getaddress of sFormattedTime to lpsFormattedTime\r
288         move (length(sFormattedTime)) to iLenCcTime\r
289         move (GetTimeFormat("LOCALE_USER_DEFAULT", 0, lpSystemTime, 0, lpsFormattedTime, iLenCcTime)) to iDataLength                \r
290 \r
291         zerostring 255 To sFormattedDate\r
292         getaddress of sFormattedDate To lpsFormattedDate\r
293         move (length(sFormattedDate)) to iLenCcDate\r
294         move (GetDateFormat("LOCALE_USER_DEFAULT", 0, lpSystemTime, 0, lpsFormattedDate, iLenCcDate)) to iDataLength\r
295 \r
296         // Work out bias and apply to get UTC\r
297         if (iSuccess = TIME_ZONE_ID_DAYLIGHT) begin\r
298             move (iBias+iDlBias) to iBiasNow\r
299         end\r
300         else begin\r
301             move (iBias+iStdBias) to iBiasNow\r
302         end\r
303         \r
304         move (cstring(sFormattedDate) * cstring(sFormattedTime)) to sTs\r
305         move (timestamp_adjust(sTs, iBiasNow*60000)) to sTs\r
306     end\r
307     end\r
308 \r
309     function_return sTs\r
310 end_function\r
311 \r
312 // Returns UTC BIAS in minutes\r
313 function get_bias global string inDestTz integer inYear integer inMon integer inDay integer inHr integer inMin integer inSec integer inIsUTC returns integer\r
314     local string sTimeZone sResult sBuf sDaylightDate sDaylightTime sStandardDate sStandardTime StdTs DltTs inTs\r
315     local integer iSuccess iBiasNow iBias iStdBias iDaylightBias iIsDaylightSaving iThrow iTmp\r
316     local integer iEYear iEMon iEDay iEDOW iEHr iEMin iESec iBYear iBMon iBDay iBDOW iBHr iBMin iBSec iEDOM iBDOM\r
317     local number nTO nBO nEO StdPo DltPo inPo\r
318     local pointer lpTimeZone lpResult\r
319 \r
320     ASSERT ((inIsUTC >= 0) and (inIsUTC <= 1)) "Unsupported mode, inIsUTC must be either 1 or 0"\r
321             \r
322     move 0 to iIsDaylightSaving\r
323     move (trim(inDestTz)) to sTimeZone    \r
324     \r
325     if (sTimeZone <> "") begin\r
326         if (show_debug_lines = 1);\r
327             showln "Timezone:          " sTimeZone\r
328     \r
329         getaddress of sTimeZone to lpTimeZone\r
330                 \r
331         zerostring 128 to sResult\r
332         getaddress of sResult to lpResult\r
333                 \r
334         move (GetTzi(lpTimeZone, lpResult)) to iSuccess\r
335                     \r
336         if (iSuccess = -1) begin\r
337         \r
338             send set_string to (tzTok(current_object)) sResult ","\r
339             get token_value of (tzTok(current_object)) item 0 to iBias\r
340             get token_value of (tzTok(current_object)) item 1 to iStdBias\r
341             get token_value of (tzTok(current_object)) item 2 to iDaylightBias\r
342             get token_value of (tzTok(current_object)) item 3 to sStandardDate\r
343             get token_value of (tzTok(current_object)) item 4 to sStandardTime\r
344             get token_value of (tzTok(current_object)) item 5 to sDaylightDate\r
345             get token_value of (tzTok(current_object)) item 6 to sDaylightTime\r
346             \r
347             // Once all of our data has been retrieved we need to determine daylight saving time\r
348             //showln  sDaylightDate " " sDaylightTime " = " sStandardDate " " sStandardTime\r
349 \r
350             send set_string to (tzTok(current_object)) sDaylightDate "/"            \r
351             get token_value of (tzTok(current_object)) item 0 to iBYear\r
352             get token_value of (tzTok(current_object)) item 1 to iBMon\r
353             get token_value of (tzTok(current_object)) item 2 to iBDay\r
354             get token_value of (tzTok(current_object)) item 3 to iBDOW\r
355             \r
356             send set_string to (tzTok(current_object)) sStandardDate "/"            \r
357             get token_value of (tzTok(current_object)) item 0 to iEYear\r
358             get token_value of (tzTok(current_object)) item 1 to iEMon\r
359             get token_value of (tzTok(current_object)) item 2 to iEDay\r
360             get token_value of (tzTok(current_object)) item 3 to iEDOW\r
361             \r
362             ASSERT ((iBMon <> 0) and (iEMon <> 0)) "Daylight saving not supported"\r
363             \r
364             if (show_debug_lines = 1) begin\r
365                 if ((iBMon = 0) or (iEMon = 0));\r
366                     showln "Supports Dls:      no" \r
367                 else;\r
368                     showln "Supports Dls:      yes" \r
369             end         \r
370                                             \r
371             if ((iBMon <> 0) and (iEMon <> 0)) begin\r
372                 if ((iBYear = 0) or (iBYear = inYear)) begin\r
373                 \r
374                     move (get_day_of_month_for_daylight_savings((iBYear max inYear),iBMon,iBDOW,iBday)) to iBDOM\r
375                     move (get_day_of_month_for_daylight_savings((iEYear max inYear),iEMon,iEDOW,iEday)) to iEDOM              \r
376                     \r
377                     send set_string to (tzTok(current_object)) sDaylightTime ":"\r
378                     get token_value of (tzTok(current_object)) item 0 to iBHr\r
379                     get token_value of (tzTok(current_object)) item 1 to iBMin\r
380                     get token_value of (tzTok(current_object)) item 2 to iBSec\r
381 \r
382                     send set_string to (tzTok(current_object)) sStandardTime ":"\r
383                     get token_value of (tzTok(current_object)) item 0 to iEHr\r
384                     get token_value of (tzTok(current_object)) item 1 to iEMin\r
385                     get token_value of (tzTok(current_object)) item 2 to iESec\r
386 \r
387                     move (zeropad(iBDOM,2)+"/"+zeropad(iBMon,2)+"/"+string(iEYear max inYear)+" "+zeropad(iBHr,2)+":"+zeropad(iBMin,2)+":"+zeropad(iBSec,2)) to DltTs\r
388                     move (zeropad(iEDOM,2)+"/"+zeropad(iEMon,2)+"/"+string(iEYear max inYear)+" "+zeropad(iEHr,2)+":"+zeropad(iEMin,2)+":"+zeropad(iESec,2)) to StdTs\r
389                     move (zeropad(inDay,2)+"/"+zeropad(inMon,2)+"/"+string(inYear)+" "+zeropad(inHr,2)+":"+zeropad(inMin,2)+":"+zeropad(inSec,2)) to inTs\r
390                     \r
391                     move (timestemp_to_posix(DltTs)) to DltPo\r
392                     move (timestemp_to_posix(StdTs)) to StdPo\r
393                     move (timestemp_to_posix(inTs)+(iBias*-60*inIsUTC)) to inPo\r
394                     \r
395                     if (show_debug_lines = 1) begin\r
396                         showln "InTimestamp:       " inTs\r
397                         showln "CmpTimestamp:      " (posix_to_timestamp(inPo))\r
398                         showln "DaylightTimestamp: " DltTs " Offset="  (iBias+iDaylightBias)                    \r
399                         showln "StandardTimestamp: " StdTs " Offset="  (iBias+iStdBias)\r
400                     end\r
401                     \r
402                     if (DltPo < StdPo) begin\r
403                         if ((inPo >= DltPo) and (inPo < StdPo)) move 1 to iIsDaylightSaving\r
404                     end\r
405                     else begin\r
406                         if not ((inPo >= StdPo) and (inPo < DltPo)) move 1 to iIsDaylightSaving\r
407                     end\r
408                 end\r
409                 \r
410             end\r
411             \r
412             // If the clocks go forward at 1.00am then the 1.00am-1.59am doesn't exist in localtime; \r
413             // conversins back and forth between nonexistent times will look wrong, but should never occour\r
414             // If the clocks go back at 1.00am then 1.00am effectively occours twice \r
415             // More info: http://www.timeanddate.com/time/dst/transition.html\r
416             \r
417             // Now we can figure out the actual bias\r
418             if (iIsDaylightSaving = 1) begin\r
419                 move (iBias+iDaylightBias) to iBiasNow\r
420                 if (show_debug_lines = 1) begin\r
421                     showln "Zone currently in daylight saving"\r
422                 end\r
423             end\r
424             else begin\r
425                 move (iBias+iStdBias) to iBiasNow\r
426             end\r
427             if (show_debug_lines = 1) begin\r
428                 showln "Bias:              " iBiasNow\r
429             end\r
430             \r
431             send destroy_object to tzTok\r
432         end\r
433         else begin\r
434             custom_error ERROR_CODE_INVALID_TIMESTAMP$ ERROR_MSG_INVALID_TIMESTAMP\r
435         end        \r
436     end\r
437 \r
438     function_return iBiasNow\r
439 end_function\r
440 \r
441 // Returns UTC time converted from the current time in the passed timezone \r
442 function get_utc_time_from_timezone_time global string inDestTz string inTs returns string\r
443     local integer l_bias\r
444     local string l_ts\r
445     local integer inGmtYear inGmtMon inGmtDay inGmtHr inGmtMin inGmtSec \r
446 \r
447     if ((length(inTs) < 10) or (mid(inTs, 1, 3) <> "/") or (mid(inTs, 1, 6)  <> "/") or (mid(inTs, 1, 11)  <> " ")) begin\r
448         custom_error ERROR_CODE_INVALID_TIMESTAMP$ ERROR_MSG_INVALID_TIMESTAMP ERROR_DETAIL_INVALID_TIMESTAMP "DD/MM/YYYY"\r
449     end\r
450     if (((length(inTs) >= 16) and (mid(inTs, 1, 14)  <> ":")) or ((length(inTs) >= 19) and (mid(inTs, 1, 17)  <> ":"));\r
451         or ((length(inTs) >= 21) and (mid(inTs, 1, 20)  <> "."))) begin\r
452         custom_error ERROR_CODE_INVALID_TIMESTAMP$ ERROR_MSG_INVALID_TIMESTAMP ERROR_DETAIL_INVALID_TIMESTAMP "DD/MM/YYYY HH:mm:SS.mss"\r
453     end\r
454             \r
455     move (mid(inTs, 2, 1)) to inGmtDay\r
456     move (mid(inTs, 2, 4)) to inGmtMon\r
457     move (mid(inTs, 4, 7)) to inGmtyear\r
458     \r
459     if (length(inTs) >= 13) move (mid(inTs, 2, 12)) to inGmtHr\r
460     else move 0 to inGmtHr\r
461     if (length(inTs) >= 16) move (mid(inTs, 2, 15)) to inGmtMin\r
462     else move 0 to inGmtMin\r
463     if (length(inTs) >= 19) move (mid(inTs, 2, 18)) to inGmtSec\r
464     else move 0 to inGmtSec\r
465     \r
466     move (get_bias(inDestTz, inGmtYear, inGmtMon, inGmtDay, inGmtHr, inGmtMin, inGmtSec, 0)) to l_bias\r
467     move (zeropad(inGmtDay,2)+"/"+zeropad(inGmtMon,2)+"/"+zeropad(inGmtYear,4)+" "+zeropad(inGmtHr,2)+":"+zeropad(inGmtMin,2)+":"+zeropad(inGmtSec,2)) to l_ts\r
468     \r
469     move (timestamp_adjust(l_ts, l_bias*60000)) to l_ts\r
470     \r
471     function_return l_ts\r
472 end_function\r
473 \r
474 //Returns current time in the passed timezone from the time passed as UTC\r
475 function get_timezone_time_from_utc_time global string inDestTz string inTs returns string\r
476     local integer l_bias\r
477     local string l_ts\r
478     local integer inUtcYear inUtcMonth inUtcDay inUtcHr inUtcMin inUtcSec \r
479 \r
480     if ((length(inTs) < 10) or (mid(inTs, 1, 3) <> "/") or (mid(inTs, 1, 6)  <> "/") or (mid(inTs, 1, 11)  <> " ")) begin\r
481         custom_error ERROR_CODE_INVALID_TIMESTAMP$ ERROR_MSG_INVALID_TIMESTAMP ERROR_DETAIL_INVALID_TIMESTAMP "DD/MM/YYYY"\r
482     end\r
483     if (((length(inTs) >= 16) and (mid(inTs, 1, 14)  <> ":")) or ((length(inTs) >= 19) and (mid(inTs, 1, 17)  <> ":"));\r
484         or ((length(inTs) >= 21) and (mid(inTs, 1, 20)  <> "."))) begin\r
485         custom_error ERROR_CODE_INVALID_TIMESTAMP$ ERROR_MSG_INVALID_TIMESTAMP ERROR_DETAIL_INVALID_TIMESTAMP "DD/MM/YYYY HH:mm:SS.mss"\r
486     end\r
487             \r
488     move (mid(inTs, 2, 1)) to inUtcDay\r
489     move (mid(inTs, 2, 4)) to inUtcMonth\r
490     move (mid(inTs, 4, 7)) to inUtcyear\r
491     \r
492     if (length(inTs) >= 13) move (mid(inTs, 2, 12)) to inUtcHr\r
493     else move 0 to inUtcHr\r
494     if (length(inTs) >= 16) move (mid(inTs, 2, 15)) to inUtcMin\r
495     else move 0 to inUtcMin\r
496     if (length(inTs) >= 19) move (mid(inTs, 2, 18)) to inUtcSec\r
497     else move 0 to inUtcSec\r
498             \r
499     move (get_bias(inDestTz, inUtcYear, inUtcMonth, inUtcDay, inUtcHr, inUtcMin, inUtcSec, 1)) to l_bias\r
500     move (zeropad(inUtcDay,2)+"/"+zeropad(inUtcMonth,2)+"/"+zeropad(inUtcYear,4)+" "+zeropad(inUtcHr,2)+":"+zeropad(inUtcMin,2)+":"+zeropad(inUtcSec,2)) to l_ts\r
501     \r
502     move (timestamp_adjust(l_ts, l_bias*-60000)) to l_ts\r
503     \r
504     function_return l_ts\r
505 end_function\r
506 \r
507 //Convert a timestamp from one local zone to another\r
508 function get_timezone_time_from_timezone_time global string inSourceTz string inDestTz string inTs returns string\r
509     local string l_ts\r
510     \r
511     move (get_timezone_time_from_utc_time(inDestTz, (get_utc_time_from_timezone_time(inSourceTz, inTs)))) to l_ts\r
512     \r
513     function_return l_ts\r
514 end_function\r
515 \r