blob: 72bf9b7079209ae8d5bf7e8e486ff6d7d87404a9 [file] [log] [blame]
Radek Krejcid0d19522015-09-02 13:49:25 +02001/**
2 * \file time.c
3 * \author Radek Krejci <rkrejci@cesnet.cz>
4 * \brief libnetconf2 - time handling functions
5 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * https://opensource.org/licenses/BSD-3-Clause
Radek Krejcid0d19522015-09-02 13:49:25 +020013 */
14
15#define _GNU_SOURCE
16#include <ctype.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <unistd.h>
21
Michal Vasko316c9632020-04-15 11:06:57 +020022#include "compat.h"
Radek Krejcid0d19522015-09-02 13:49:25 +020023#include "libnetconf.h"
Radek Krejcid0d19522015-09-02 13:49:25 +020024
25API time_t
Michal Vasko231e4b22015-12-10 10:10:59 +010026nc_datetime2time(const char *datetime)
Radek Krejcid0d19522015-09-02 13:49:25 +020027{
28 struct tm time;
Michal Vasko231e4b22015-12-10 10:10:59 +010029 char *dt;
Radek Krejcid0d19522015-09-02 13:49:25 +020030 int i;
31 long int shift, shift_m;
32 time_t retval;
33
Michal Vasko7f1c78b2016-01-19 09:52:14 +010034 if (!datetime) {
Michal Vasko45e53ae2016-04-07 11:46:03 +020035 ERRARG("datetime");
Michal Vasko7f1c78b2016-01-19 09:52:14 +010036 return -1;
Radek Krejcid0d19522015-09-02 13:49:25 +020037 }
38
Michal Vasko7f1c78b2016-01-19 09:52:14 +010039 dt = strdup(datetime);
Michal Vasko4eb3c312016-03-01 14:09:37 +010040 if (!dt) {
41 ERRMEM;
42 return -1;
43 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +010044
Radek Krejcid0d19522015-09-02 13:49:25 +020045 if (strlen(dt) < 20 || dt[4] != '-' || dt[7] != '-' || dt[13] != ':' || dt[16] != ':') {
46 ERR("Wrong date time format not compliant to RFC 3339.");
47 free(dt);
48 return (-1);
49 }
50
51 memset(&time, 0, sizeof(struct tm));
52 time.tm_year = atoi(&dt[0]) - 1900;
53 time.tm_mon = atoi(&dt[5]) - 1;
54 time.tm_mday = atoi(&dt[8]);
55 time.tm_hour = atoi(&dt[11]);
56 time.tm_min = atoi(&dt[14]);
57 time.tm_sec = atoi(&dt[17]);
58
59 retval = timegm(&time);
60
61 /* apply offset */
62 i = 19;
63 if (dt[i] == '.') { /* we have fractions to skip */
64 for (i++; isdigit(dt[i]); i++)
65 ;
66 }
67 if (dt[i] == 'Z' || dt[i] == 'z') {
68 /* zero shift */
69 shift = 0;
70 } else if (dt[i + 3] != ':') {
71 /* wrong format */
72 ERR("Wrong date time shift format not compliant to RFC 3339.");
73 free(dt);
74 return (-1);
75 } else {
76 shift = strtol(&dt[i], NULL, 10);
77 shift = shift * 60 * 60; /* convert from hours to seconds */
78 shift_m = strtol(&dt[i + 4], NULL, 10) * 60; /* includes conversion from minutes to seconds */
79 /* correct sign */
80 if (shift < 0) {
81 shift_m *= -1;
82 }
83 /* connect hours and minutes of the shift */
84 shift = shift + shift_m;
85 }
86 /* we have to shift to the opposite way to correct the time */
87 retval -= shift;
88
89 free(dt);
Michal Vasko7f1c78b2016-01-19 09:52:14 +010090 return retval;
Radek Krejcid0d19522015-09-02 13:49:25 +020091}
92
Michal Vasko231e4b22015-12-10 10:10:59 +010093API char *
Michal Vasko8c1bfab2016-05-25 11:17:02 +020094nc_time2datetime(time_t time, const char *tz, char *buf)
Radek Krejcid0d19522015-09-02 13:49:25 +020095{
Michal Vasko231e4b22015-12-10 10:10:59 +010096 char *date = NULL;
97 char *zoneshift = NULL;
Radek Krejcid0d19522015-09-02 13:49:25 +020098 int zonediff, zonediff_h, zonediff_m;
99 struct tm tm, *tm_ret;
100 char *tz_origin;
101
102 if (tz) {
Radek Krejcia507d1a2016-02-26 15:26:44 +0100103 tz_origin = getenv("TZ");
104 if (tz_origin) {
105 tz_origin = strdup(tz_origin);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100106 if (!tz_origin) {
107 ERRMEM;
108 return NULL;
109 }
Radek Krejcia507d1a2016-02-26 15:26:44 +0100110 }
Radek Krejcid0d19522015-09-02 13:49:25 +0200111 setenv("TZ", tz, 1);
Radek Krejcibd16d452016-05-31 16:00:32 +0200112 tzset(); /* apply timezone change */
Radek Krejcid0d19522015-09-02 13:49:25 +0200113 tm_ret = localtime_r(&time, &tm);
Radek Krejcia507d1a2016-02-26 15:26:44 +0100114 if (tz_origin) {
115 setenv("TZ", tz_origin, 1);
116 free(tz_origin);
117 } else {
118 unsetenv("TZ");
119 }
Radek Krejcibd16d452016-05-31 16:00:32 +0200120 tzset(); /* apply timezone change */
Radek Krejcid0d19522015-09-02 13:49:25 +0200121
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100122 if (!tm_ret) {
123 return NULL;
Radek Krejcid0d19522015-09-02 13:49:25 +0200124 }
125 } else {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100126 if (!gmtime_r(&time, &tm)) {
127 return NULL;
Radek Krejcid0d19522015-09-02 13:49:25 +0200128 }
129 }
130
Radek Krejcifcf561b2016-05-31 16:02:37 +0200131 /* years cannot be negative */
132 if (tm.tm_year < -1900) {
133 ERRARG("time");
134 return NULL;
135 }
136
137 if (tm.tm_gmtoff == 0) {
138 /* time is Zulu (UTC) */
139 if (asprintf(&zoneshift, "Z") == -1) {
140 ERRMEM;
141 return NULL;
142 }
Radek Krejcid0d19522015-09-02 13:49:25 +0200143 } else {
Radek Krejcifcf561b2016-05-31 16:02:37 +0200144 zonediff = tm.tm_gmtoff;
145 zonediff_h = zonediff / 60 / 60;
146 zonediff_m = zonediff / 60 % 60;
Michal Vasko55fc5ef2016-06-03 12:07:27 +0200147 if (asprintf(&zoneshift, "%+03d:%02d", zonediff_h, zonediff_m) == -1) {
Radek Krejcifcf561b2016-05-31 16:02:37 +0200148 ERRMEM;
149 return NULL;
Radek Krejcid0d19522015-09-02 13:49:25 +0200150 }
151 }
Michal Vasko8c1bfab2016-05-25 11:17:02 +0200152
153 if (buf) {
154 sprintf(buf, "%04d-%02d-%02dT%02d:%02d:%02d%s",
155 tm.tm_year + 1900,
156 tm.tm_mon + 1,
157 tm.tm_mday,
158 tm.tm_hour,
159 tm.tm_min,
160 tm.tm_sec,
Radek Krejcifcf561b2016-05-31 16:02:37 +0200161 zoneshift);
Michal Vasko8c1bfab2016-05-25 11:17:02 +0200162 } else if (asprintf(&date, "%04d-%02d-%02dT%02d:%02d:%02d%s",
163 tm.tm_year + 1900,
164 tm.tm_mon + 1,
165 tm.tm_mday,
166 tm.tm_hour,
167 tm.tm_min,
168 tm.tm_sec,
Radek Krejcifcf561b2016-05-31 16:02:37 +0200169 zoneshift) == -1) {
Radek Krejcid0d19522015-09-02 13:49:25 +0200170 free(zoneshift);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100171 ERRMEM;
172 return NULL;
Radek Krejcid0d19522015-09-02 13:49:25 +0200173 }
174 free(zoneshift);
175
Michal Vasko8c1bfab2016-05-25 11:17:02 +0200176 return (buf ? buf : date);
Radek Krejcid0d19522015-09-02 13:49:25 +0200177}