File: Time.k

package info (click to toggle)
kaya 0.4.2-4
  • links: PTS
  • area: main
  • in suites: lenny
  • size: 4,448 kB
  • ctags: 1,694
  • sloc: cpp: 9,536; haskell: 7,461; sh: 3,013; yacc: 910; makefile: 816; perl: 90
file content (403 lines) | stat: -rw-r--r-- 17,235 bytes parent folder | download | duplicates (4)
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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
/** -*-C-*-ish
    Kaya standard library
    Copyright (C) 2004, 2005, 2006, 2007 Edwin Brady, Chris Morris,
                                         Boris Jakubith

    This file is distributed under the terms of the GNU Lesser General
    Public Licence. See COPYING for licence.
*/
"<summary>Time and date functions</summary>
<prose>This module contains functions to store date/time data, and to format dates and times in ways suitable for interaction with other systems such as email, HTTP or databases.</prose>"
module Time;

import Prelude;
import Array;
import Builtins;
import Strings;
import Regex;

%include "time.h";

"<summary>Months of the year.</summary>
<prose>This data type represents the months of the year</prose>
<related><dataref>Time</dataref></related>"
public data Month = Jan | Feb | Mar | Apr | May | Jun 
                  | Jul | Aug | Sep | Oct | Nov | Dec;

"<summary>Days of the week.</summary>
<prose>This data type represents the days of the week</prose>
<related><dataref>Time</dataref></related>"
public data Day = Sunday | Monday | Tuesday | Wednesday 
                | Thursday | Friday | Saturday;

"<summary>Representation of time.</summary>
<prose>This data type represents a time. <code>wday</code> is the day of the week, <code>yday</code> is the day of the year, and <code>dst</code> is true if daylight savings time applies.</prose>"
public data Time = Time(Int second, 
			Int minute, 
			Int hour,
			Int mday,
			Month mon,
			Int year,
			Day wday,
			Int yday,
			Bool dst);

"<summary>Invalid month number</summary>
<prose>This Exception is thrown if <functionref index='1'>month</functionref> is passed an invalid month number (must be 1-12).</prose>"
Exception NoSuchMonthNumber(Int m);
"<summary>Invalid month name</summary>
<prose>This Exception is thrown if <functionref index='0'>month</functionref> is passed an invalid month name.</prose>"
Exception NoSuchMonthName(String s);
"<summary>Invalid day name</summary>
<prose>This Exception is thrown if <functionref>day</functionref> is passed an invalid day name.</prose>"
Exception NoSuchDay(String s);
"<summary>Can't parse string to time</summary>
<prose>This Exception is thrown if a string cannot be parsed to a Time.</prose>
<functionref>parseISO8601</functionref>
<functionref>parseRFC2822</functionref>"
Exception CantParseTime;

foreign "stdfuns.o" {
    "<summary>Get the current time.</summary>
<prose>Get the current time in seconds since Jan 1st 1970.</prose>
<related><functionref>localTime</functionref></related>
<related><functionref>gmTime</functionref></related>
<related><functionref>microTime</functionref></related>
<related><functionref>mkTime</functionref></related>"
    public Int now() = gettime;
    "<summary>Get microsecond time</summary>
<prose>Get the current number of microseconds so far this second. On windows, this is rounded to a millisecond resolution.</prose>
<related><functionref>time</functionref></related>"
    public Int microTime() = dogettimeofday;
    Time dogmtime(Int secs) = dogmtime;
    Time dolocaltime(Int secs) = dolocaltime;
    Int domktime(Time t) = domktime;
}

"<argument name='secs'>The number of seconds since Jan 1 1970. This argument is optional and defaults to the current time.</argument>
<summary>Convert a time to GMT.</summary>
<prose>Converts a time expressed in seconds since Jan 1 1970 to a <dataref>Time</dataref>, using the GMT timezone.</prose>
<related><dataref>Time</dataref></related>
<related><functionref>localTime</functionref></related>
<related><functionref>mkTime</functionref></related>
<related><functionref>time</functionref></related>"
public Time gmTime(Int secs = now()) = dogmtime(secs);

"<argument name='secs'>The number of seconds since Jan 1 1970. This argument is optional and defaults to the current time.</argument>
<summary>Convert a time to local time.</summary>
<prose>Converts a time expressed in seconds since Jan 1 1970 to a <dataref>Time</dataref>, using the current local timezone.</prose>
<related><dataref>Time</dataref></related>
<related><functionref>gmTime</functionref></related>
<related><functionref>mkTime</functionref></related>
<related><functionref>time</functionref></related>"
public Time localTime(Int secs = now()) = dolocaltime(secs);

"<argument name='t'>The Time.</argument>
<summary>Convert a Time to seconds.</summary>
<prose>Converts a <dataref>Time</dataref> to a number of seconds since Jan 1 1970.</prose>
<related><dataref>Time</dataref></related>
<related><functionref>gmTime</functionref></related>
<related><functionref>localTime</functionref></related>
<related><functionref>time</functionref></related>"
public Int mkTime(Time t) = domktime(t);

"<argument name='t'>The Time</argument>
<argument name='days'>The number of days to add (if negative, subtracts)</argument>
<summary>Add some days to a date.</summary>
<prose>Add some days to a date.</prose>
<related><dataref>Time</dataref></related>"
public Time addDays(Time t, Int days) = gmTime(mkTime(t)+(24*60*60*days));

"<argument name='m'>The Month to convert</argument>
<summary>Convert a Month to a string</summary>
<prose>Convert a Month to a string.</prose>
<related><dataref>Month</dataref></related>
<related><functionref>int</functionref></related>
<related><functionref>month</functionref></related>"
public String string(Month m) {
    return case m of {
	Jan -> "January";
	| Feb -> "February";
	| Mar -> "March";
	| Apr -> "April";
	| May -> "May";
	| Jun -> "June";
	| Jul -> "July";
	| Aug -> "August";
	| Sep -> "September";
	| Oct -> "October";
	| Nov -> "November";
	| Dec -> "December";
    };
}

"<argument name='s'>The String to convert</argument>
<summary>Convert a String to a Month</summary>
<prose>Convert a String to a Month.</prose>
<related><dataref>Month</dataref></related>
<related><functionref index='1'>month</functionref></related>
<related><functionref>string</functionref></related>"
public Month month(String s) {
  ms = substr(s,0,3);
  if (ms == "Jan") { return Jan; }
  else if (ms == "Feb") { return Feb; }
  else if (ms == "Mar") { return Mar; }
  else if (ms == "Apr") { return Apr; }
  else if (ms == "May") { return May; }
  else if (ms == "Jun") { return Jun; }
  else if (ms == "Jul") { return Jul; }
  else if (ms == "Aug") { return Aug; }
  else if (ms == "Sep") { return Sep; }
  else if (ms == "Oct") { return Oct; }
  else if (ms == "Nov") { return Nov; }
  else if (ms == "Dec") { return Dec; }
  else { throw(NoSuchMonthName(s)); }
}

"<argument name='m'>The Month to convert</argument>
<summary>Convert a Month to an Int</summary>
<prose>Convert a Month to an Int.</prose>
<related><dataref>Month</dataref></related>
<related><functionref index='1'>month</functionref></related>
<related><functionref>string</functionref></related>"
public Int int(Month m) {
    return case m of {
	Jan -> 1;
	| Feb -> 2;
	| Mar -> 3;
	| Apr -> 4;
	| May -> 5;
	| Jun -> 6;
	| Jul -> 7;
	| Aug -> 8;
	| Sep -> 9;
	| Oct -> 10;
	| Nov -> 11;
	| Dec -> 12;
    };
}

"<argument name='m'>The Int to convert</argument>
<summary>Convert an Int to a Month</summary>
<prose>Convert an Int to a Month.</prose>
<related><dataref>Month</dataref></related>
<related><functionref>int</functionref></related>
<related><functionref index='0'>month</functionref></related>"
public Month month(Int m) {
    if (m==1) return Jan;
    if (m==2) return Feb;
    if (m==3) return Mar;
    if (m==4) return Apr;
    if (m==5) return May;
    if (m==6) return Jun;
    if (m==7) return Jul;
    if (m==8) return Aug;
    if (m==9) return Sep;
    if (m==10) return Oct;
    if (m==11) return Nov;
    if (m==12) return Dec;
    throw(NoSuchMonthNumber(m));
}

"<argument name='d'>The Day to convert</argument>
<summary>Convert a Day to a string</summary>
<prose>Convert a Day to a string.</prose>
<related><dataref>Day</dataref></related>
<related><functionref>day</functionref></related>"
public String string(Day d) {
    return case d of {
	Sunday -> "Sunday";
	| Monday -> "Monday";
	| Tuesday -> "Tuesday";
	| Wednesday -> "Wednesday";
	| Thursday -> "Thursday";
	| Friday -> "Friday";
	| Saturday -> "Saturday";
    };
}

"<argument name='d'>The String to convert</argument>
<summary>Convert a String to a Day</summary>
<prose>Convert a String to a Day.</prose>
<related><dataref>Day</dataref></related>
<related><functionref index='1'>string</functionref></related>"
public Day day(String s) {
    ms = substr(s,0,3);
    if (ms == "Sun") { return Sunday; }
    else if (ms == "Mon") { return Monday; }
    else if (ms == "Tue") { return Tuesday; }
    else if (ms == "Wed") { return Wednesday; }
    else if (ms == "Thu") { return Thursday; }
    else if (ms == "Fri") { return Friday; }
    else if (ms == "Sat") { return Saturday; }
    else { throw(NoSuchDay(s)); }
}

"<argument name='t'>The Time</argument>
<argument name='tz'>The timezone (optional, defaults to \"+0000\")</argument>
<summary>Return an RFC 2822 time</summary>
<prose>Format a Time according to RFC 2822 for use in email or HTTP.</prose>
<related><dataref>Time</dataref></related>
<related><functionref>parseRFC2822</functionref></related>"
public String rfc2822Time(Time t, String tz="+0000") {
  return substr(string(t.wday),0,3)+", "+twodigits(t.mday)+" "+substr(string(t.mon),0,3)+" "+t.year+" "+twodigits(t.hour)+":"+twodigits(t.minute)+":"+twodigits(t.second)+" "+tz;
}

"<argument name='t'>The Time, which must be in the UTC (+0000) timezone.</argument>
<summary>Return a HTTP Cookie expiry time</summary>
<prose>This function returns a String representing a time in the format used by HTTP Cookies, which is almost the same as RFC2822 but differs from it in a few ways for no apparent reason. This format should only be used for HTTP cookies, and not for any other purpose.</prose>
<related><functionref>rfc2822Time</functionref></related>
<related><functionref>WebCommon::setCookie</functionref></related>"
public String cookieTime(Time t) {
  // because they couldn't just use RFC(2)822, could they. Had to be different.
  return substr(string(t.wday),0,3)+", "+twodigits(t.mday)+"-"+substr(string(t.mon),0,3)+"-"+t.year+" "+twodigits(t.hour)+":"+twodigits(t.minute)+":"+twodigits(t.second)+" GMT";
}

"<argument name='t'>A String containing only an RFC 2822 time string</argument>
<summary>Convert an RFC 2822 string to a Time</summary>
<prose>Convert a string formatted in accordance with RFC 2822 to a Time.</prose>
<related><dataref>Time</dataref></related>
<related><functionref>rfc2822Time</functionref></related>"
public Time parseRFC2822(String t) {
  bits = split(r"\s+",t);
  timebits = split(":",bits[4]);
  if (size(bits) < 5 || size(bits) > 6 || size(timebits) != 3) {
    throw(CantParseTime);
  }
  ts = Time(Int(timebits[2]),Int(timebits[1]),Int(timebits[0]),Int(bits[1]),month(bits[2]),Int(bits[3]),day(bits[0]),0,false);
  if (bits[5] == "+0000" || bits[5] == "GMT") {
    // known bug: dates prior to 1971 appear to have a problem due to
    // permanent BST from 1968-1971
    return ts;
  } else {
    hdiff = substr(bits[5],0,2);
    ts.hour += Int(hdiff);
    return ts;
  }
}


// TODO: generalise this function since the separators can change
"<argument name='t'>A String containing only an ISO8601 time string</argument>
<summary>Convert an ISO 8601 time string to a Time</summary>
<prose>Convert a string formatted in accordance with ISO 8601 to a Time.</prose>
<related><dataref>Time</dataref></related>
<related><functionref>isoTime</functionref></related>"
public Time parseISO8601(String t) {
  tsp = split(" ",t);
  if (size(tsp) != 2) { 
    tsp = split("T",t); // might be a slightly different isoTime format
    if (size(tsp) != 2) { 
      throw(CantParseTime); 
    }
  }
  td = split("-",tsp[0]);
  if (size(td) != 3) { throw(CantParseTime); }
  tt = split(":",tsp[1]);
  if (size(tt) != 3) { throw(CantParseTime); }
  return Time(Int(tt[2]),Int(tt[1]),Int(tt[0]),
	      Int(td[2]),month(Int(td[1])),Int(td[0]),
	      Sunday,0,false);
}

String twodigits(Int i) {
  if (i > 9) { return String(i); }
  return "0"+i;
}

/* Start of Boris Jakubith's isoTime() code */

// Flags required for modifying the output of `isoTime()':
//   - DateOnly --> return only the date
"<summary>Flags for the isoTime function</summary>
<prose>Flags for modifying the output of the <functionref>isoTime</functionref> functions.</prose>
<list>
<item><code>DateOnly</code>: Return only the date portion of the date and time</item>
<item><code>TimeOnly</code>: Return only the time portion of the date and time (ignored if <code>DateOnly</code> is also specified)</item>
<item><code>WithTimeZone</code>: Include a timezone specification.</item>
<item><code>ShortForm</code>: Compact output by removing date and time delimiters (the delimiter between the date and time parts is kept)</item>
<item><code>TwoFields</code>: Use ' ' rather than 'T' as the delimiter between the date and time portions</item>
<item><code>UTC</code>: When converting from a Unix timestamp, return the time in the UTC timezone (normally it is returned in the local timezone). When using a <dataref>Time</dataref> this flag has no effect.</item>
</list>"
public data IsoTimeOpts = DateOnly | TimeOnly | WithTimeZone | ShortForm |
			  TwoFields | UTC;

"<argument name='t'>The Time</argument>
<argument name='opts'>A list of <dataref>IsoTimeOpts</dataref> options. This argument may be omitted and defaults to the empty list.</argument>
<argument name='tzOffs'>The offset of the timezone from UTC in seconds. This argument is usually only used internally by <functionref index='1'>isoTime</functionref> for conversion from Unix timestamps</argument>
<summary>Convert a Time-value into ISO-8601 format</summary>
<prose>Returns a String containing the ISO-8601 representation of a <dataref>Time</dataref>. This can then be used in interoperation with various other systems.</prose>
<related><dataref>Time</dataref></related>
<related><functionref index='1'>isoTime</functionref></related>"
public String isoTime (Time t, [IsoTimeOpts] opts = [], Int tzOffs = 0) {
  dateDelim = "-"; timeDelim = ":"; tz = "";
  // In it's short form, the ISO-date has no delimiters (with the exception of
  // the delimiter between the date-part and the `time of day'-part ...
  if (ShortForm `elem` opts) { dateDelim = ""; timeDelim = ""; }

  // Set the delimiter used between the date- and the `time of day'-part ...
  dateTimeDelim = "T"; if (TwoFields `elem` opts) { dateTimeDelim = " "; }

  // Calculate the date-part and the `time of day'-part of the output ...
  dateRes = t.year + dateDelim + twodigits (int (t.mon)) + dateDelim +
	    twodigits (t.mday);
  timeRes = twodigits (t.hour) + timeDelim + twodigits (t.minute) + timeDelim +
	    twodigits (t.second);

  // If the output of the timezone is requested, generate it ...
  if (WithTimeZone `elem` opts) {
    if (tzOffs == 0) {
      tz = "Z";
    } else {
      tz = "+"; if (tzOffs < 0) { tz = "-"; tzOffs = -tzOffs; }
      tz += twodigits (tzOffs / 3600) + timeDelim +
	    twodigits ((tzOffs / 60) % 60);
    }
  }
  // return either the date-part ...
  if (DateOnly `elem` opts) { return dateRes; }
  // ... or the `time of day'-part ...
  if (TimeOnly `elem` opts) { return timeRes + tz; }
  // Return the full ISO-8601 compatible date/time string ...
  return join ([dateRes, timeRes + tz], dateTimeDelim);
}

"<argument name='secs'>The Unix timestamp (seconds since 1970-01-01 00:00:00).</argument>
<argument name='opts'>A list of <dataref>IsoTimeOpts</dataref> options. This argument may be omitted and defaults to the empty list.</argument>
<summary>Convert a Unix timestamp into ISO-8601 format</summary>
<prose>Return an ISO-8601 format String representing the time from the Unix timestamp. The local timezone will be assumed (though not necessarily displayed) unless the <code>UTC</code> flag is in the <code>opts</code> array.</prose>
<related><dataref>Time</dataref></related>
<related><functionref>isoTime</functionref></related>"
// CIM: removed default value for first parameter. now() makes sense
// but because of the way function overloading works it's not possible as
// isoTime() could refer to this or to the other.
public String isoTime (Int secs, [IsoTimeOpts] opts = []) {
  // return the UTC if requested ...
  if (UTC `elem` opts) { return isoTime (gmTime (secs), opts); }
  // otherwise, calculate the cwlocal time, ...
  t = localTime (secs);
  // the timezone offset ...
  tzOffs = 0; if (WithTimeZone `elem` opts) { tzOffs = tzDist (secs); }
  // and generate the ISO-date/time format ...
  return isoTime (t, opts, tzOffs);
}

// return the distance between the current timezone and UTC ...
// I chose this algorithm, because it seems quite independent from the
// underlying OS ...
Int tzDist (Int secs) {
  lt = localTime (secs); ut = gmTime (secs);
  dist = (lt.second - ut.second) +
	 (lt.minute - ut.minute)*60 +
	 (lt.hour - ut.hour)*3600;
  dayOffs = 0;
  if (lt.year != ut.year) {
    dayOffs = (if (lt.year > ut.year) 1 else -1);
  } else if (lt.mon != ut.mon) {
    dayOffs = (if (int (lt.mon) > int (ut.mon)) 1 else -1);
  } else if (lt.mday != ut.mday) {
    dayOffs = (if (lt.mday > ut.mday) 1 else -1);
  }
  return dist + dayOffs*86400;
}