ASN1_TIME_SET(3)
π ASN1_TIME_SET(3) β ASN.1 Time functions π
okay bestie, welcome to the ASN.1 Time manual. It's giving βlots of tiny time functions,β but we're gonna get through it together β¨
π NAME π
This whole friend group is: ASN1_TIME_set, ASN1_UTCTIME_set, ASN1_GENERALIZEDTIME_set, ASN1_TIME_adj, ASN1_UTCTIME_adj, ASN1_GENERALIZEDTIME_adj, ASN1_TIME_set_string, ASN1_TIME_set_string_X509, ASN1_UTCTIME_set_string, ASN1_GENERALIZEDTIME_set_string, ASN1_TIME_normalize, ASN1_TIME_check, ASN1_UTCTIME_check, ASN1_GENERALIZEDTIME_check, ASN1_TIME_print, ASN1_UTCTIME_print, ASN1_GENERALIZEDTIME_print, ASN1_TIME_to_tm, ASN1_TIME_diff, ASN1_TIME_cmp_time_t, ASN1_UTCTIME_cmp_time_t, ASN1_TIME_compare, ASN1_TIME_to_generalizedtime, OPENSSL_gmtime, OPENSSL_timegm, OPENSSL_posix_to_tm, OPENSSL_tm_to_posix β collectively the ASN.1 Time functions.
β¨ SYNOPSIS β¨
Library energy: libcrypto. Include vibe: <openssl/asn1.h>, plus <openssl/crypto.h> and <openssl/posix_time.h> for the OPENSSL squad later on. Signatures, verbatim, no cap:
library βlibcryptoβ #include <openssl/asn1.h>
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_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 *s, const ASN1_TIME *t);
ASN1_GENERALIZEDTIME *
ASN1_TIME_to_generalizedtime(const ASN1_TIME *t,
ASN1_GENERALIZEDTIME **out);
#include <openssl/crypto.h>
struct tm *
OPENSSL_gmtime(const time_t *time, struct tm *out_tm);
#include <openssl/posix_time.h>
int
OPENSSL_timegm(const struct tm *tm, time_t *out_time);
int
OPENSSL_posix_to_tm(int64_t time, struct tm *out_tm);
int
OPENSSL_tm_to_posix(struct tm *t_tm, int64_t *out);
π DESCRIPTION π
okay so an ASN1_TIME object is a shallow wrapper around a string containing an ASN.1 Time value in the restricted format that's valid in X.509 certificates. It's literally just a string wrapper doing its little certification thing.
An ASN1_TIME object is either:
- an
ASN1_UTCTIMEobject containing a string of the formatYYMMDDHHMMSSZ, which is valid for the years1950to2049, or - an
ASN1_GENERALIZEDTIMEobject containing a string of the formatYYYYMMDDHHMMSSZ, which is valid for the years0000to1949and2050to9999.
In both cases, the mandatory suffix 'Z' represents the GMT time zone, periodt. LibreSSL by design does not support the full syntax of ASN.1 times. In particular, it neither supports fractional seconds nor any other time zone. So don't come looking for fancy timezone offsets here, bestie β it's GMT or nothing π
The functions ASN1_TIME_set(), ASN1_UTCTIME_set(), and ASN1_GENERALIZEDTIME_set() set the time object s to the time represented by the time_t value t. If s is NULL, a new time object is allocated and returned.
The functions ASN1_TIME_adj(), ASN1_UTCTIME_adj(), and ASN1_GENERALIZEDTIME_adj() set the time object s to the time represented by the time offset_day and offset_sec after the time_t value t. The values of offset_day or offset_sec can be negative to set a time before t. The offset_sec value can also exceed the number of seconds in a day. If s is NULL, a new time object is allocated and returned.
ASN1_TIME_adj() may change the type from ASN1_GENERALIZEDTIME to ASN1_UTCTIME or vice versa depending on the resulting year. The functions ASN1_UTCTIME_adj() and ASN1_GENERALIZEDTIME_adj() do not modify the type of the return object. so like, ASN1_TIME_adj() is flexible, but the other two are committed to their format π
The functions ASN1_TIME_set_string(), ASN1_TIME_set_string_X509(), ASN1_UTCTIME_set_string(), and ASN1_GENERALIZEDTIME_set_string() set the time object s to the time string str, which must be in appropriate ASN.1 time format: YYMMDDHHMMSSZ for ASN1_UTCTIME, YYYYMMDDHHMMSSZ for ASN1_GENERALIZEDTIME, or either of the two for ASN1_TIME. The string str is copied into s. If s is NULL, these functions only perform a format check on str.
In LibreSSL, ASN1_TIME_set_string() and ASN1_TIME_set_string_X509() behave identically and always set the time object to a valid value to use in an X.509 certificate. ASN1_GENERALIZEDTIME_set_string() may encode a time string that is not valid in an X.509 certificate. yes, that's a little messy, but that's the lore π«Ά
The function ASN1_TIME_normalize() converts an ASN1_GENERALIZEDTIME into a time value that can be used in a certificate by changing it to an ASN1_UTCTIME if possible. It has no effect on an ASN1_UTCTIME.
The functions ASN1_TIME_check(), ASN1_UTCTIME_check(), and ASN1_GENERALIZEDTIME_check() check the syntax of the time string contained in the object s.
The functions ASN1_TIME_print(), ASN1_UTCTIME_print(), and ASN1_GENERALIZEDTIME_print() print out the time s to BIO b in human readable format. It will be of the format MMM DD HH:MM:SS YYYY [GMT], for example "Feb 3 00:55:52 2015 GMT". It does not include a newline. If the time string has an invalid format, it prints out "Bad time value" and returns an error. iconic error message, honestly π
The function ASN1_TIME_to_tm() converts the time s to the standard tm structure. If s is NULL, then the current time is converted. The output time is always in the GMT time zone. The tm_sec, tm_min, tm_hour, tm_mday, tm_mon, and tm_year fields of the tm structure are set to the proper values, whereas all other fields are set to 0. If tm is NULL, this function performs a format check on s only.
The function ASN1_TIME_diff() sets *pday and *psec to the time difference between from and to. If to represents a time later than from, then one or both (depending on the time difference) of *pday and *psec will be positive. If to represents a time earlier than from, then one or both of *pday and *psec will be negative. If to and from represent the same time, then *pday and *psec will both be zero. If both *pday and *psec are nonzero, they will always have the same sign. The value of *psec will always be less than the number of seconds in a day. If from or to is NULL, the current time is used.
The functions ASN1_TIME_cmp_time_t(), ASN1_UTCTIME_cmp_time_t(), and ASN1_TIME_compare() compare the two times represented by s and t.
The function ASN1_TIME_to_generalizedtime() converts the ASN1_TIME t to an ASN1_GENERALIZEDTIME, regardless of year. If either out or *out is NULL, then a new object is allocated and must be freed after use.
The ASN1_TIME, ASN1_UTCTIME, and ASN1_GENERALIZEDTIME objects are represented as ASN1_STRING objects internally and can be freed using ASN1_STRING_free(3).
It is recommended that ASN1_TIME functions be used instead of ASN1_UTCTIME or ASN1_GENERALIZEDTIME functions because the ASN1_UTCTIME and ASN1_GENERALIZEDTIME functions act only on that specific time format, while the ASN1_TIME functions operate on either format. so yeah, use the flexible one unless you have a very specific reason not to, bestie β¨
OPENSSL_gmtime() converts a time_t value in time to a struct tm in out_tm and also returns the struct passed in on success.
OPENSSL_timegm() converts a time structure in UTC time in tm to a time_t value in out_time.
OPENSSL_posix_to_tm() converts an int64_t POSIX time value in time, which must be in the range of year 0 to 9999, to a broken out time value in tm.
OPENSSL_tm_to_posix() converts a time value between the years 0 and 9999 in tm to a POSIX time value in out.
π RETURN VALUES π
okay, return value check, because bestie we love knowing what we're getting back:
ASN1_TIME_set(),ASN1_UTCTIME_set(),ASN1_GENERALIZEDTIME_set(),ASN1_TIME_adj(),ASN1_UTCTIME_adj(),ASN1_GENERALIZEDTIME_adj(), andASN1_TIME_to_generalizedtime()return a pointer to a time object orNULLif an error occurred.ASN1_TIME_set_string(),ASN1_TIME_set_string_X509(),ASN1_UTCTIME_set_string(), andASN1_GENERALIZEDTIME_set_string()return1if the time value is successfully set or0otherwise.ASN1_TIME_normalize()returns1on success or0on error.ASN1_TIME_check(),ASN1_UTCTIME_check(), andASN1_GENERALIZEDTIME_check()return1if the time string contained in the object is syntactically correct or0otherwise.ASN1_TIME_print(),ASN1_UTCTIME_print(), andASN1_GENERALIZEDTIME_print()return1if the time is successfully printed or0if an error occurred (I/O error or invalid time format).ASN1_TIME_to_tm()returns1if the time is successfully parsed or0if an error occurred, usually due to an invalid time format.ASN1_TIME_diff()returns1for success or0for failure. It can for example fail if a time string passed in has invalid syntax.ASN1_TIME_cmp_time_t(),ASN1_UTCTIME_cmp_time_t(), andASN1_TIME_compare()return-1ifsis earlier thant,0if both are equal,1ifsis later thant, or-2on error.OPENSSL_timegm()returns1for success or0for failure. It can fail if the time is not representable in atime_t, or falls outside the range allowed in RFC 5280 times.OPENSSL_gmtime()returnsout_tmon success orNULLfor failure. It can fail if the time is not representable in astruct tm, or falls outside the range allowed in RFC 5280 times.OPENSSL_posix_to_tm()andOPENSSL_tm_to_posix()return1for success or0on failure. It is a failure if the year is less than0or more than9999.
π§ EXAMPLES π§
Set a time object to one hour after the current time and print it out:
#include <time.h>
#include <openssl/asn1.h>
ASN1_TIME *asn1_time;
time_t t;
BIO *b;
t = time(NULL);
asn1_time = ASN1_TIME_adj(NULL, t, 0, 60 * 60);
b = BIO_new_fp(stdout, BIO_NOCLOSE);
if (asn1_time != NULL) {
ASN1_TIME_print(b, asn1_time);
BIO_printf(b, "\n");
} else {
BIO_printf(b, "Time out of range or un-representable\n");
}
ASN1_STRING_free(asn1_time);
BIO_free(b);
π SEE ALSO π
ASN1_TIME_new(3), X509_cmp_time(3)
β STANDARDS β
The usage of the ASN.1 Time, UTCTime, and GeneralizedTime data types in X.509 certificates is specified in RFC 5280, Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile, section 4.1.2.5 (TBS Certificate Validity).
π°οΈ HISTORY (the lore, bestie) π°οΈ
Okay so this page has so much history and honestly the way these functions have been around since before we were born is lowkey iconic. Let's go chronologically because that's the only way this makes sense π«Ά
- SSLeay era, the origin story β¨ β
ASN1_UTCTIME_check()andASN1_UTCTIME_print()first appeared in SSLeay 0.5.1. ThenASN1_UTCTIME_set()showed up in SSLeay 0.6.0. AndASN1_UTCTIME_set_string()landed in SSLeay 0.9.0. All of these functions have been available since OpenBSD 2.4. Ancient. Vintage. We stan a legacy. - The OpenSSL 0.9.2b glow-up π β
ASN1_TIME_set(),ASN1_GENERALIZEDTIME_set(),ASN1_GENERALIZEDTIME_set_string(),ASN1_GENERALIZEDTIME_check(),ASN1_TIME_print(), andASN1_GENERALIZEDTIME_print()all first appeared in OpenSSL 0.9.2b, and have been available since OpenBSD 2.6. ASN1_UTCTIME_cmp_time_t()enters the chat π β first appeared in OpenSSL 0.9.6, available since OpenBSD 2.9.ASN1_TIME_check()andASN1_TIME_to_generalizedtime()said hi π β first appeared in OpenSSL 0.9.7, available since OpenBSD 3.2.- The 1.0.0 squad π₯ β
ASN1_TIME_adj(),ASN1_UTCTIME_adj(),ASN1_GENERALIZEDTIME_adj(), andASN1_TIME_set_string()first appeared in OpenSSL 1.0.0, and have been available since OpenBSD 4.9. ASN1_TIME_diff()arrives πΈ β first appeared in OpenSSL 1.0.2, available since OpenBSD 7.1.- The 1.1.1 besties π β
ASN1_TIME_set_string_X509(),ASN1_TIME_normalize(),ASN1_TIME_to_tm(),ASN1_TIME_cmp_time_t(), andASN1_TIME_compare()first appeared in OpenSSL 1.1.1 and have been available since OpenBSD 7.2. - The time-conversion crew π¦ β
OPENSSL_gmtime()first appeared in OpenSSL 0.9.7. MeanwhileOPENSSL_timegm(),OPENSSL_posix_to_tm(), andOPENSSL_tm_to_posix()first appeared in BoringSSL. All of these functions have been available since OpenBSD 7.5.
π¨ CAVEATS (bestie pls read this one) π¨
Okay so listen, this is the part where I need you to focus because it's giving danger and not the fun kind π
Some applications add offset times directly to a time_t value and then pass the results to ASN1_TIME_set() (or equivalent). This can cause problems, because the time_t value can overflow on some systems, resulting in unexpected results. Not cute. Not slay. Genuinely a vibe killer π
New applications should useASN1_TIME_adj()instead, and pass the offset value in the offset_sec and offset_day parameters instead of directly manipulating atime_tvalue. Do it this way. Please. For me. π
And that's the page, bestie β you made it to the end, I'm so proud of you π₯Ήπ This one's from Debian, dated June 8, 2025, and it lives at ASN1_TIME_SET(3) in the man pages. Go forth and format your timestamps responsibly. Mwah πβ¨
π¦ where this page comes from
- alpine 3.24: libressl-doc 4.3.1-r0 Β· custom
- arch rolling: libressl 4.4.0-1 Β· ISC AND custom:OpenSSL
- Copyright (c) 2022 Ingo Schwarze <schwarze@openbsd.org>
- Copyright (c) 2022 Bob Beck <beck@openbsd.org>
- Copyright (c) 2015, 2017 The OpenSSL Project. All rights reserved
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms