summaryrefslogtreecommitdiffstats
path: root/tapset/ctime.stp
blob: 3ecd6ddfb31bb9dc9e07737a7e6fb37aab586f93 (plain)
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
/* ctime.stp - Convert seconds to human readable date string.
 *
 * This code was adapted from the newlib mktm_r() and asctime_r()
 * functions.  In newlib, mktm_r.c states that it was adapted from
 * tzcode maintained by Arthur David Olson.  In newlib, asctime_r.c
 * doesn't have any author/copyright information.
 *
 * Changes copyright (C) 2006, 2008 Red Hat Inc.
 */

/**
 * sfunction ctime - Convert seconds since epoch into human readable date/time string.
 * @epochsecs: Number of seconds since epoch (as returned by gettimeofday_s()).
 *
 * Description: Takes an argument of seconds since the epoch as returned by
 * gettimeofday_s(). Returns a string of the form
 *
 *   "Wed Jun 30 21:49:08 1993"
 *
 * The string will always be exactly 24 characters. If the time would
 * be unreasonable far in the past (before what can be represented
 * with a 32 bit offset in seconds from the epoch) the returned string
 * will be "a long, long time ago...". If the time would be
 * unreasonable far in the future the returned string will be "far far
 * in the future..." (both these strings are also 24 characters wide).
 *
 * Note that the epoch (zero) corresponds to
 *
 *   "Thu Jan  1 00:00:00 1970"
 *
 * The earliest full date given by ctime, corresponding to epochsecs
 * -2147483648 is "Fri Dec 13 20:45:52 1901". The latest full date
 * given by ctime, corresponding to epochsecs 2147483647 is
 * "Tue Jan 19 03:14:07 2038".
 *
 * The abbreviations for the days of the week are ‘Sun’, ‘Mon’, ‘Tue’,
 * ‘Wed’, ‘Thu’, ‘Fri’, and ‘Sat’.  The abbreviations for the months
 * are ‘Jan’, ‘Feb’, ‘Mar’, ‘Apr’, ‘May’, ‘Jun’, ‘Jul’, ‘Aug’, ‘Sep’,
 * ‘Oct’, ‘Nov’, and ‘Dec’.
 *
 * Note that the real C library ctime() function puts a newline ('\n')
 * character at the end of the string that this function does not.
 * Also note that since the kernel has no concept of timezones, the
 * returned time is always in GMT.
 */
function ctime:string(epochsecs:long)
%{ /* pure */

#define SECSPERMIN	60L
#define MINSPERHOUR	60L
#define HOURSPERDAY	24L
#define SECSPERHOUR	(SECSPERMIN * MINSPERHOUR)
#define SECSPERDAY	(SECSPERHOUR * HOURSPERDAY)
#define DAYSPERWEEK	7
#define MONSPERYEAR	12

#define EPOCH_YEAR      1970
#define EPOCH_WDAY      4

#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)

    static const int mon_lengths[2][MONSPERYEAR] = {
	{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
	{31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
    } ;

    static const int year_lengths[2] = {
	365,
	366
    } ;

    static const char day_name[7][3] = {
	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
    };
    static const char mon_name[12][3] = {
	"Jan", "Feb", "Mar", "Apr", "May", "Jun", 
	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
    };

    long days, rem;
    time_t lcltime;
    int yleap;
    const int *ip;
    int tm_sec;         /* seconds */
    int tm_min;         /* minutes */
    int tm_hour;        /* hours */
    int tm_mday;        /* day of the month */
    int tm_mon;         /* month */
    int tm_year;        /* year */
    int tm_wday;        /* day of the week */

    // Check that the numer of seconds is "reasonable".
    // Otherwise (especially on 64bit machines) we will be spending
    // way too much time calculating the correct year, month and
    // day. Also we would like the returned string to always be 24 chars.
    // So cap to what can be represented normally on a 32bit machine.
    int64_t MAX_POS_SECS =  2147483647LL;
    int64_t MIN_NEG_SECS = -2147483648LL;

    if (THIS->epochsecs > MAX_POS_SECS)
      {
        strlcpy(THIS->__retvalue, "far far in the future...", MAXSTRINGLEN);
	return;
      }
    if (THIS->epochsecs < MIN_NEG_SECS)
      {
        strlcpy(THIS->__retvalue, "a long, long time ago...", MAXSTRINGLEN);
        return;
      }

    lcltime = THIS->epochsecs;
   
    days = ((long)lcltime) / SECSPERDAY;
    rem = ((long)lcltime) % SECSPERDAY;
    while (rem < 0) 
    {
	rem += SECSPERDAY;
	--days;
    }
    while (rem >= SECSPERDAY)
    {
	rem -= SECSPERDAY;
	++days;
    }
 
    /* compute hour, min, and sec */  
    tm_hour = (int) (rem / SECSPERHOUR);
    rem %= SECSPERHOUR;
    tm_min = (int) (rem / SECSPERMIN);
    tm_sec = (int) (rem % SECSPERMIN);

    /* compute day of week */
    if ((tm_wday = ((EPOCH_WDAY + days) % DAYSPERWEEK)) < 0)
	tm_wday += DAYSPERWEEK;

    /* compute year & day of year */
    tm_year = EPOCH_YEAR;
    if (days >= 0)
    {
	for (;;)
	{
	    yleap = isleap(tm_year);
	    if (days < year_lengths[yleap])
		break;
	    tm_year++;
	    days -= year_lengths[yleap];
	}
    }
    else
    {
	do
	{
	    --tm_year;
	    yleap = isleap(tm_year);
	    days += year_lengths[yleap];
	} while (days < 0);
    }

    ip = mon_lengths[yleap];
    for (tm_mon = 0; days >= ip[tm_mon]; ++tm_mon)
	days -= ip[tm_mon];
    tm_mday = days + 1;

    /*
     * At this point we have all our information.  Now we need to
     * convert it to an ascii representation.
     */

    snprintf (THIS->__retvalue, MAXSTRINGLEN, "%.3s %.3s%3d %.2d:%.2d:%.2d %d",
	      day_name[tm_wday], mon_name[tm_mon],
	      tm_mday, tm_hour, tm_min,
	      tm_sec, tm_year);
%}