1 /***********************************************************************
3 * This software is part of the ast package *
4 * Copyright (c) 1985-2010 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Common Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
13 * Information and Software Systems Research *
17 * Glenn Fowler <gsf@research.att.com> *
18 * David Korn <dgk@research.att.com> *
19 * Phong Vo <kpv@research.att.com> *
21 ***********************************************************************/
27 * Time_t conversion support
33 #define warped(t,n) ((t)<((n)-tmxsns(6L*30L*24L*60L*60L,0))||(t)>((n)+tmxsns(24L*60L*60L,0)))
36 * format n with padding p into s
45 number(register char* s
, register char* e
, register long n
, register int p
, int w
, int pad
)
51 if (p
> 0 && (pad
== 0 || pad
== '0'))
76 s
+= sfsprintf(s
, e
- s
, "%0*lu", p
, n
);
78 s
+= sfsprintf(s
, e
- s
, "%*lu", -p
, n
);
80 s
+= sfsprintf(s
, e
- s
, "%lu", n
);
86 typedef struct Stack_s
93 * format t into buf of length len
94 * end of buf is returned
98 tmxfmt(char* buf
, size_t len
, const char* format
, Time_t t
)
126 tm
= tmxtm(&ts
, t
, NiL
);
127 if (!format
|| !*format
)
128 format
= tm_info
.deformat
;
130 flags
= tm_info
.flags
;
137 if ((c
= *format
++) == delimiter
)
144 delimiter
= sp
->delimiter
;
161 switch (c
= *format
++)
169 if (!isalpha(*format
))
195 width
= width
* 10 + (c
- '0');
198 prec
= prec
* 10 + (c
- '0');
211 if (!(c
= *format
++))
218 else if (c
== ')' && !--i
)
220 else if (arg
< &argbuf
[sizeof(argbuf
) - 1])
241 if (tm_info
.deformat
!= tm_info
.format
[TM_DEFAULT
])
242 format
= tm_info
.deformat
;
244 format
= tm_info
.format
[TM_DEFAULT
];
246 case 'a': /* abbreviated day of week name */
247 n
= TM_DAY_ABBREV
+ tm
->tm_wday
;
249 case 'A': /* day of week name */
250 n
= TM_DAY
+ tm
->tm_wday
;
252 case 'b': /* abbreviated month name */
254 n
= TM_MONTH_ABBREV
+ tm
->tm_mon
;
256 case 'B': /* month name */
257 n
= TM_MONTH
+ tm
->tm_mon
;
259 case 'c': /* `ctime(3)' date sans newline */
260 p
= tm_info
.format
[TM_CTIME
];
262 case 'C': /* 2 digit century */
263 cp
= number(cp
, ep
, (long)(1900 + tm
->tm_year
) / 100, 2, width
, pad
);
265 case 'd': /* day of month */
266 cp
= number(cp
, ep
, (long)tm
->tm_mday
, 2, width
, pad
);
269 p
= tm_info
.format
[TM_DATE
];
271 case 'E': /* OBSOLETE no pad day of month */
272 cp
= number(cp
, ep
, (long)tm
->tm_mday
, 0, width
, pad
);
274 case 'e': /* blank padded day of month */
275 cp
= number(cp
, ep
, (long)tm
->tm_mday
, -2, width
, pad
);
277 case 'f': /* TM_DEFAULT override */
278 p
= tm_info
.deformat
;
280 case 'F': /* ISO 8601:2000 standard date format */
283 case 'g': /* %V 2 digit year */
284 case 'G': /* %V 4 digit year */
285 n
= tm
->tm_year
+ 1900;
288 if (tmweek(tm
, 2, -1, -1) >= 52)
291 else if (tm
->tm_yday
> 358)
293 if (tmweek(tm
, 2, -1, -1) <= 1)
303 cp
= number(cp
, ep
, (long)n
, c
, width
, pad
);
305 case 'H': /* hour (0 - 23) */
306 cp
= number(cp
, ep
, (long)tm
->tm_hour
, 2, width
, pad
);
308 case 'i': /* international `date(1)' date */
309 p
= tm_info
.format
[TM_INTERNATIONAL
];
311 case 'I': /* hour (0 - 12) */
312 if ((n
= tm
->tm_hour
) > 12) n
-= 12;
313 else if (n
== 0) n
= 12;
314 cp
= number(cp
, ep
, (long)n
, 2, width
, pad
);
316 case 'J': /* Julian date (0 offset) */
317 cp
= number(cp
, ep
, (long)tm
->tm_yday
, 3, width
, pad
);
319 case 'j': /* Julian date (1 offset) */
320 cp
= number(cp
, ep
, (long)(tm
->tm_yday
+ 1), 3, width
, pad
);
322 case 'k': /* `date(1)' date */
323 p
= tm_info
.format
[TM_DATE_1
];
329 p
= (pad
== '_') ? "%Y-%m-%d %H:%M:%S.%N %z" : "%Y-%m-%d+%H:%M:%S.%N%z";
332 p
= (pad
== '_') ? "%Y-%m-%d %H:%M:%S.%N" : "%Y-%m-%d+%H:%M:%S.%N";
335 p
= (pad
== '_') ? "%Y-%m-%d %H:%M:%S" : "%Y-%m-%d+%H:%M:%S";
339 case 'l': /* `ls -l' date */
345 p
= tm_info
.format
[TM_DISTANT
];
349 p
= tm_info
.format
[TM_RECENT
];
351 case 'L': /* TM_DEFAULT */
352 case 'O': /* OBSOLETE */
353 p
= tm_info
.format
[TM_DEFAULT
];
355 case 'm': /* month number */
356 cp
= number(cp
, ep
, (long)(tm
->tm_mon
+ 1), 2, width
, pad
);
358 case 'M': /* minutes */
359 cp
= number(cp
, ep
, (long)tm
->tm_min
, 2, width
, pad
);
365 case 'N': /* nanosecond part */
366 cp
= number(cp
, ep
, (long)tm
->tm_nsec
, 9, width
, pad
);
368 case 'o': /* set options */
372 p
= tm_info
.deformat
;
374 case 'p': /* meridian */
375 n
= TM_MERIDIAN
+ (tm
->tm_hour
>= 12);
377 case 'q': /* time zone type (nation code) */
378 if (!(flags
& TM_UTC
))
380 if ((zp
= tm
->tm_zone
) != tm_info
.local
)
381 for (; zp
>= tm_data
.zone
; zp
--)
384 else if (p
= zp
->type
)
388 case 'Q': /* %Q<alpha> or %Q<delim>recent<delim>distant<delim> */
396 case 'd': /* `ls -l' distant date */
397 p
= tm_info
.format
[TM_DISTANT
];
399 case 'r': /* `ls -l' recent date */
400 p
= tm_info
.format
[TM_RECENT
];
412 p
= warped(t
, now
) ? (char*)0 : (char*)format
;
432 p
= tm_info
.format
[TM_MERIDIAN_TIME
];
437 case 's': /* seconds[.nanoseconds] since the epoch */
445 f
+= sfsprintf(f
, &fmt
[sizeof(fmt
)] - f
, "%d", width
);
446 f
+= sfsprintf(f
, &fmt
[sizeof(fmt
)] - f
, "I%du", sizeof(Tmxsec_t
));
447 cp
+= sfsprintf(cp
, ep
- cp
, fmt
, tmxsec(now
));
450 n
= sfsprintf(cp
, ep
- cp
, ".%09I*u", sizeof(Tmxnsec_t
), tmxnsec(now
));
451 if (prec
&& n
>= prec
)
456 case 'S': /* seconds */
457 cp
= number(cp
, ep
, (long)tm
->tm_sec
, 2, width
, pad
);
458 if ((flags
& TM_SUBSECOND
) && (format
- 2) != oformat
)
469 p
= tm_info
.format
[TM_TIME
];
471 case 'u': /* weekday number [1(Monday)-7] */
472 if (!(i
= tm
->tm_wday
))
474 cp
= number(cp
, ep
, (long)i
, 0, width
, pad
);
476 case 'U': /* week number, Sunday as first day */
477 cp
= number(cp
, ep
, (long)tmweek(tm
, 0, -1, -1), 2, width
, pad
);
479 case 'V': /* ISO week number */
480 cp
= number(cp
, ep
, (long)tmweek(tm
, 2, -1, -1), 2, width
, pad
);
482 case 'W': /* week number, Monday as first day */
483 cp
= number(cp
, ep
, (long)tmweek(tm
, 1, -1, -1), 2, width
, pad
);
485 case 'w': /* weekday number [0(Sunday)-6] */
486 cp
= number(cp
, ep
, (long)tm
->tm_wday
, 0, width
, pad
);
489 p
= tm_info
.format
[TM_DATE
];
492 p
= tm_info
.format
[TM_TIME
];
494 case 'y': /* year in the form yy */
495 cp
= number(cp
, ep
, (long)(tm
->tm_year
% 100), 2, width
, pad
);
497 case 'Y': /* year in the form ccyy */
498 cp
= number(cp
, ep
, (long)(1900 + tm
->tm_year
), 4, width
, pad
);
500 case 'z': /* time zone west offset */
503 if ((zp
= tmzone(arg
, &f
, 0, 0)) && !*f
&& tm
->tm_zone
!= zp
)
504 tm
= tmxtm(tm
, tmxtime(tm
, tm
->tm_zone
->west
+ (tm
->tm_isdst
? tm
->tm_zone
->dst
: 0)), zp
);
508 cp
= tmpoff(cp
, ep
- cp
, "", (flags
& TM_UTC
) ? 0 : tm
->tm_zone
->west
+ (tm
->tm_isdst
? tm
->tm_zone
->dst
: 0), 24 * 60);
510 case 'Z': /* time zone */
513 if ((zp
= tmzone(arg
, &f
, 0, 0)) && !*f
&& tm
->tm_zone
!= zp
)
514 tm
= tmxtm(tm
, tmxtime(tm
, tm
->tm_zone
->west
+ (tm
->tm_isdst
? tm
->tm_zone
->dst
: 0)), zp
);
517 p
= (flags
& TM_UTC
) ? tm_info
.format
[TM_UT
] : tm
->tm_isdst
&& tm
->tm_zone
->daylight
? tm
->tm_zone
->daylight
: tm
->tm_zone
->standard
;
519 case '+': /* old %+flag */
520 case '!': /* old %!flag */
523 case '=': /* old %=[=][+-]flag */
524 for (arg
= argbuf
; *format
== '=' || *format
== '-' || *format
== '+' || *format
== '!'; format
++)
525 if (arg
< &argbuf
[sizeof(argbuf
) - 2])
527 if (*arg
++ = *format
)
540 p
= tm_info
.format
[n
];
542 while (cp
< ep
&& (*cp
= *p
++))
580 * right, the global state stinks
581 * but we respect its locale-like status
590 tm
= tmxtm(tm
, t
, (flags
& TM_UTC
) ? &tm_data
.zone
[2] : tm
->tm_zone
);
599 tm
= tmxtm(tm
, t
, (flags
& TM_UTC
) ? &tm_data
.zone
[2] : tm
->tm_zone
);
608 if (sp
< &stack
[elementsof(stack
)])
610 sp
->format
= (char*)format
;
612 sp
->delimiter
= delimiter
;
618 tm_info
.flags
= flags
;