ASN1_TIME_SET(3ossl)
β¨ ASN1_TIME_SET(3ossl) β¨
okay so this is the OpenSSL page for the ASN.1 time functions, bestie β ASN1_TIME_SET(3ossl) in the OpenSSL manual. it's a LOT but we're gonna get through it together, i promise π
πΈ NAME πΈ
ASN1_TIME_set, ASN1_UTCTIME_set, ASN1_GENERALIZEDTIME_set, ASN1_TIME_adj, ASN1_UTCTIME_adj, ASN1_GENERALIZEDTIME_adj, ASN1_TIME_check, ASN1_UTCTIME_check, ASN1_GENERALIZEDTIME_check, ASN1_TIME_set_string, ASN1_UTCTIME_set_string, ASN1_GENERALIZEDTIME_set_string, ASN1_TIME_set_string_X509, ASN1_TIME_normalize, ASN1_TIME_to_tm, ASN1_TIME_print, ASN1_TIME_print_ex, ASN1_UTCTIME_print, ASN1_GENERALIZEDTIME_print, ASN1_TIME_diff, ASN1_TIME_cmp_time_t, ASN1_UTCTIME_cmp_time_t, ASN1_TIME_compare, ASN1_TIME_to_generalizedtime, ASN1_TIME_dup, ASN1_UTCTIME_dup, ASN1_GENERALIZEDTIME_dup β all of these are the ASN.1 Time functions. yes. all of them. that's the whole squad π«Ά
π SYNOPSIS π
here's the whole lineup, character for character, no cap:
ASN1_TIME *ASN1_TIME_set(ASN1_TIME *s, time_t t);
ASN1_UTCTIME *ASN1_UTCTIME_set(ASN1_UTCTIME *s, time_t t);
ASN1_GENERALIZEDTIME *ASN1_GENERALIZEDTIME_set(ASN1_GENERALIZEDTIME *s,
time_t t);
ASN1_TIME *ASN1_TIME_adj(ASN1_TIME *s, time_t t, int offset_day,
long offset_sec);
ASN1_UTCTIME *ASN1_UTCTIME_adj(ASN1_UTCTIME *s, time_t t,
int offset_day, long offset_sec);
ASN1_GENERALIZEDTIME *ASN1_GENERALIZEDTIME_adj(ASN1_GENERALIZEDTIME *s,
time_t t, int offset_day,
long offset_sec);
int ASN1_TIME_set_string(ASN1_TIME *s, const char *str);
int ASN1_TIME_set_string_X509(ASN1_TIME *s, const char *str);
int ASN1_UTCTIME_set_string(ASN1_UTCTIME *s, const char *str);
int ASN1_GENERALIZEDTIME_set_string(ASN1_GENERALIZEDTIME *s,
const char *str);
int ASN1_TIME_normalize(ASN1_TIME *s);
int ASN1_TIME_check(const ASN1_TIME *t);
int ASN1_UTCTIME_check(const ASN1_UTCTIME *t);
int ASN1_GENERALIZEDTIME_check(const ASN1_GENERALIZEDTIME *t);
int ASN1_TIME_print(BIO *b, const ASN1_TIME *s);
int ASN1_TIME_print_ex(BIO *bp, const ASN1_TIME *tm, unsigned long flags);
int ASN1_UTCTIME_print(BIO *b, const ASN1_UTCTIME *s);
int ASN1_GENERALIZEDTIME_print(BIO *b, const ASN1_GENERALIZEDTIME *s);
int ASN1_TIME_to_tm(const ASN1_TIME *s, struct tm *tm);
int ASN1_TIME_diff(int *pday, int *psec, const ASN1_TIME *from,
const ASN1_TIME *to);
int ASN1_TIME_cmp_time_t(const ASN1_TIME *s, time_t t);
int ASN1_UTCTIME_cmp_time_t(const ASN1_UTCTIME *s, time_t t);
int ASN1_TIME_compare(const ASN1_TIME *a, const ASN1_TIME *b);
ASN1_GENERALIZEDTIME *ASN1_TIME_to_generalizedtime(ASN1_TIME *t,
ASN1_GENERALIZEDTIME **out);
ASN1_TIME *ASN1_TIME_dup(const ASN1_TIME *t);
ASN1_UTCTIME *ASN1_UTCTIME_dup(const ASN1_UTCTIME *t);
ASN1_GENERALIZEDTIME *ASN1_GENERALIZEDTIME_dup(const ASN1_GENERALIZEDTIME *t);
like, yes, it's giving phone book π but every single one is important, ok??
π DESCRIPTION π
setting times from a time_t β°
so ASN1_TIME_set(), ASN1_UTCTIME_set() and ASN1_GENERALIZEDTIME_set() set the structure s to the time that the time_t value t represents. and if s is NULL? iconic behavior β a brand new time structure gets allocated and returned to you. π
offsets, aka the glow-up π¦
ASN1_TIME_adj(), ASN1_UTCTIME_adj() and ASN1_GENERALIZEDTIME_adj() set the time structure s to the time that is offset_day and offset_sec after the time_t value t. both offset_day and offset_sec can be negative if you wanna go before t (time travel is real). also offset_sec is allowed to be bigger than the number of seconds in a day, no judgment. and same deal as before β if s is NULL, a new structure is allocated and returned. β¨
setting times from a string π€
ASN1_TIME_set_string(), ASN1_UTCTIME_set_string() and ASN1_GENERALIZEDTIME_set_string() set the time structure s to the time represented by the string str, which must be in the appropriate ASN.1 time format β think YYMMDDHHMMSSZ or YYYYMMDDHHMMSSZ. and if s is NULL, the function just does a format check on str and nothing else. the string str gets copied into s.
the X509 one, she's strict π
ASN1_TIME_set_string_X509() sets the ASN1_TIME structure s to the time represented by str, which must be in the appropriate time format that RFC 5280 requires. translation: it only allows YYMMDDHHMMSSZ and YYYYMMDDHHMMSSZ β leap second is rejected, sorry babe β and every other ASN.1 time format is simply not allowed. if s is NULL, it only performs a format check on str.
normalize, for consistent vibes π
ASN1_TIME_normalize() converts an ASN1_GENERALIZEDTIME or ASN1_UTCTIME into a time value that can be used in a certificate. run it after the ASN1_TIME_set_string() functions and before the ASN1_TIME_print() functions so you get consistent (i.e. GMT) results. it's the "fix your hair before the photo" step, fr fr.
syntax checks π
ASN1_TIME_check(), ASN1_UTCTIME_check() and ASN1_GENERALIZEDTIME_check() check the syntax of the time structure s. that's it. that's the whole job.
printing it out π¨οΈ
ASN1_TIME_print(), ASN1_UTCTIME_print() and ASN1_GENERALIZEDTIME_print() print the time structure s to BIO b in human readable format. the format is MMM DD HH:MM:SS[.s*] YYYY GMT β for example "Feb 3 00:55:52 2015 GMT" β and no, it does not include a newline. if the time structure has an invalid format it prints out "Bad time value" and returns an error. and the output for generalized time may include a fractional part following the second. π
ASN1_TIME_print_ex() gives you flags to specify the output format of the datetime β either ASN1_DTFLGS_RFC822 or ASN1_DTFLGS_ISO8601. pick your aesthetic π
converting to a struct tm π°οΈ
ASN1_TIME_to_tm() converts the time s to the standard tm structure. if s is NULL, then the current time is what gets converted. output time is GMT. the tm_sec, tm_min, tm_hour, tm_mday, tm_wday, tm_yday, tm_mon and tm_year fields of the tm structure get set to proper values, while all the other fields get set to 0. if tm is NULL, this function just performs a format check on s only. and heads up β if s is in Generalized format with fractional seconds, e.g. YYYYMMDDHHMMSS.SSSZ, those fractional seconds will be lost while converting s to the tm structure. π gone. poof.
diffing two times ββ
ASN1_TIME_diff() sets *pday and *psec to the time difference between from and to.
- if
tois later thanfrom, then one or both (depending on the time difference) of*pdayand*psecwill be positive. - if
tois earlier thanfrom, then one or both of*pdayand*psecwill be negative. - if
toandfromare the same time, both*pdayand*psecwill be zero.
and if both *pday and *psec are nonzero they will always have the same sign. also *psec will always be less than the number of seconds in a day. if from or to is NULL, the current time is used instead.
comparing π―ββοΈ
ASN1_TIME_cmp_time_t() and ASN1_UTCTIME_cmp_time_t() compare the two times represented by the time structure s and the time_t t.
ASN1_TIME_compare() compares the two times represented by the time structures a and b.
to generalized time π§
ASN1_TIME_to_generalizedtime() converts an ASN1_TIME to an ASN1_GENERALIZEDTIME, regardless of year. if either out or *out are NULL, then a new object is allocated and you must free it after use. don't forget. i'm serious. free it. π«Ά
dupes π
ASN1_TIME_dup(), ASN1_UTCTIME_dup() and ASN1_GENERALIZEDTIME_dup() duplicate the time structure t and return the duplicated result correspondingly. matching set, slay.
β NOTES β
the ASN1_TIME structure corresponds to the ASN.1 structure Time defined in RFC5280 et al. and the time setting functions obey the rules outlined in RFC5280: if the date can be represented by UTCTime it's used, else GeneralizedTime is used. polite and rule-abiding β¨
the ASN1_TIME, ASN1_UTCTIME and ASN1_GENERALIZEDTIME structures are represented as an ASN1_STRING internally and can be freed up using ASN1_STRING_free().
the ASN1_TIME structure can represent years from 0000 to 9999, but no attempt is made to correct ancient calendar changes (for example from Julian to Gregorian calendars). she's not a historian, bestie π
ASN1_UTCTIME is limited to a year range of 1950 through 2049. that's the whole window. π
ok so important warning time: some applications add offset times directly to atime_tvalue and pass the results toASN1_TIME_set()(or equivalent). this can cause problems because thetime_tvalue can overflow on some systems, resulting in unexpected results π new applications should useASN1_TIME_adj()instead and pass the offset value in theoffset_secandoffset_dayparameters instead of directly manipulating atime_tvalue. do it the right way, please, i'm begging π
ASN1_TIME_adj() may change the type from ASN1_GENERALIZEDTIME to ASN1_UTCTIME, or vice versa, based on the resulting year. but ASN1_GENERALIZEDTIME_adj() and ASN1_UTCTIME_adj() will not modify the type of the return structure. they stay in their lane.
it is recommended that functions starting with ASN1_TIME be used instead of those starting with ASN1_UTCTIME or ASN1_GENERALIZEDTIME. the ones starting with ASN1_UTCTIME and ASN1_GENERALIZEDTIME act only on that specific time format, whereas the ones starting with ASN1_TIME will operate on either format. so like, flexible queen behavior π
if you're familiar with RFC822, note this: when specifying the flag ASN1_DTFLGS_RFC822 the year will be formatted as documented above, i.e., using 4 digits, not 2 as specified in RFC822. plot twist π
π BUGS π
ASN1_TIME_print(), ASN1_UTCTIME_print() and ASN1_GENERALIZEDTIME_print() do not print out the timezone: it either prints out "GMT" or nothing. but tbh all certificates complying with RFC5280 et al use GMT anyway, so it's fineee.
ASN1_TIME_print(), ASN1_TIME_print_ex(), ASN1_UTCTIME_print() and ASN1_GENERALIZEDTIME_print() do not distinguish if they fail because of an I/O error or an invalid time format. you just get "it didn't work" and have to guess π
use the ASN1_TIME_normalize() function to normalize the time value before printing to get GMT results. that's the fix, bestie πΈ
π₯ RETURN VALUES π₯
ASN1_TIME_set(), ASN1_UTCTIME_set(), ASN1_GENERALIZEDTIME_set(), ASN1_TIME_adj(), ASN1_UTCTIME_adj() and ASN1_GENERALIZEDTIME_set() return a pointer to a time structure, or NULL if an error occurred.
ASN1_TIME_set_string(), ASN1_UTCTIME_set_string(), ASN1_GENERALIZEDTIME_set_string() and ASN1_TIME_set_string_X509() return 1 if the time value is successfully set, and 0 otherwise.
ASN1_TIME_normalize() returns 1 on success, and 0 on error.
ASN1_TIME_check(), ASN1_UTCTIME_check and ASN1_GENERALIZEDTIME_check() return 1 if the structure is syntactically correct, and 0 otherwise.
ASN1_TIME_print(), ASN1_UTCTIME_print() and ASN1_GENERALIZEDTIME_print() return 1 if the time is successfully printed out, and 0 if an I/O error occurred an error occurred (I/O error or invalid time format).
ASN1_TIME_to_tm() returns 1 if the time is successfully parsed, and 0 if an error occurred (invalid time format).
ASN1_TIME_diff() returns 1 for success and 0 for failure. it can fail if the passed-in time structure has invalid syntax, for example.
ASN1_TIME_cmp_time_t() and ASN1_UTCTIME_cmp_time_t() return -1 if s is before t, 0 if s equals t, or 1 if s is after t. and -2 is returned on error.
ASN1_TIME_compare() returns -1 if a is before b, 0 if a equals b, or 1 if a is after b. and -2 is returned on error.
ASN1_TIME_to_generalizedtime() returns a pointer to the appropriate time structure on success, or NULL if an error occurred.
ASN1_TIME_dup(), ASN1_UTCTIME_dup() and ASN1_GENERALIZEDTIME_dup() return a pointer to a time structure, or NULL if an error occurred.
ok that's the first half, bestie β and yes there's more coming, so don't go anywhere π«Άβ¨
πΈ EXAMPLES πΈ
okay bestie, real talk β this is where it gets fun. here's how you actually use this stuff π
π¦ set a time one hour in the future and print it
literally just: grab the current time, add 60 * 60 seconds, print it, clean up. no cap, that's it.
#include <time.h>
#include <openssl/asn1.h>
ASN1_TIME *tm;
time_t t;
BIO *b;
t = time(NULL);
tm = ASN1_TIME_adj(NULL, t, 0, 60 * 60);
b = BIO_new_fp(stdout, BIO_NOCLOSE);
ASN1_TIME_print(b, tm);
ASN1_STRING_free(tm);
BIO_free(b);
the way ASN1_TIME_adj(NULL, t, 0, 60 * 60) passes NULL for the first arg so a fresh ASN1_TIME gets allocated for you? iconic. and note the 0 days / 60 * 60 seconds split β day offset first, seconds second, always π₯
π§ is this time sooner or later than now?
passing NULL as the "from" time means "compare against the current time," and it fills in day and sec for you:
int day, sec;
if (!ASN1_TIME_diff(&day, &sec, NULL, to))
/* Invalid time format */
if (day > 0 || sec > 0)
printf("Later\n");
else if (day < 0 || sec < 0)
printf("Sooner\n");
else
printf("Same\n");
β οΈ okay this part matters: ifASN1_TIME_diff()returns 0, the time format was invalid β do not go readingdayandseclike they mean something, because they don't. check that return value, bestie, i'm begging π
also lowkey obsessed with how positive means later, negative means sooner, and both zero means same. simple. clean. slay.
π HISTORY π
a lil timeline for you, because knowing when stuff dropped is main character energy π
ASN1_TIME_to_tm()β added in OpenSSL 1.1.1 β¨ASN1_TIME_set_string_X509()β added in OpenSSL 1.1.1 β¨ASN1_TIME_normalize()β added in OpenSSL 1.1.1 β¨ASN1_TIME_cmp_time_t()β added in OpenSSL 1.1.1 β¨ASN1_TIME_compare()β added in OpenSSL 1.1.1 β¨
so yeah, they all landed together in 1.1.1. a whole era. we love a coordinated release drop π«Ά
π COPYRIGHT π
Copyright 2015-2025 The OpenSSL Project Authors. All Rights Reserved.
Licensed under the Apache License 2.0 (the "License"). You may not use this file except in compliance with the License. You can obtain a copy in the file LICENSE in the source distribution or at <https://www.openssl.org/source/license.html>.
π fr fr, read the license before you go shipping things β "except in compliance with the License" is doing real work in that sentence π
and that's the whole page, bestie!! π this was ASN1_TIME_SET(3ossl), version 3.5.0, dated 2026-09-25 β go forth and handle your timestamps responsibly, you absolute icon ππ
π¦ where this page comes from
- opensuse-leap 16.0: openssl-3-doc 3.5.0-160000.11.1 Β· Apache-2.0
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms