mktime函数性能分析

mktime函数性能分析

1月 02, 2019 in Linux环境高级编程

mktime函数性能分析

mktime是一个将break-down时间(struct tm)转化为日历时间(time_t)的转换函数。它的转换与struct tm中的 tm_wday、tm_yday无关,当进行转换时,mktime会通过struct tm的其他成员重新矫正该值。若struct tm中的成员是非法的的,mktime将会自动校正,如2018-12-32 00:00:00,矫正后为2019-01-01 00:00:00。若给定struct tm不能转换为日历时间,则mktime返回-1。—-man mktime

1. 背景

背景:最近工作中遇到一个奇怪的问题,在将原先在32位机器上编译的程序放在64位机器上重新编译之后,然后放到IDC机器运行,发现性能降了100倍左右。在经过性能分析和查阅相关资料后发现,是由于mktime使用不当导致。

 

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

 

#include <iostream>

#include <string>

#include <sys/time.h>

#include <unistd.h>

using namespace std;

#define PrintTime(id) struct timeval __now1105##id;\

gettimeofday(&__now1105##id, 0);

#define PrintTimeDone(id) struct timeval __now21105##id; \

gettimeofday(&__now21105##id, 0); \

printf("timer_%s spend time:%d us\n",#id,(__now21105##id.tv_sec-__now1105##id.tv_sec)* 1000000 + (__now21105##id.tv_usec-__now1105##id.tv_usec));

static void get_time(const std::string& time_str)

{

struct tm temp_tm;

strptime(time_str.c_str(), "%Y-%m-%d %H:%M:%S", &temp_tm);

PrintTime(mktime);

time_t temp = mktime(&temp_tm);

PrintTimeDone(mktime);

}

int main()

{

for (int i = 0; i < 10; i++)

get_time("2018-12-27 00:00:00");

return 0;

}

32位机器(i686)编译运行结果:

 

timer_mktime spend time:51 us

timer_mktime spend time:4 us

timer_mktime spend time:2 us

timer_mktime spend time:3 us

timer_mktime spend time:3 us

timer_mktime spend time:3 us

timer_mktime spend time:3 us

timer_mktime spend time:2 us

timer_mktime spend time:2 us

timer_mktime spend time:2 us

64位机器(x86_64 )编译运行结果:

 

timer_mktime spend time:181 us

timer_mktime spend time:156 us

timer_mktime spend time:138 us

timer_mktime spend time:138 us

timer_mktime spend time:137 us

timer_mktime spend time:145 us

timer_mktime spend time:143 us

timer_mktime spend time:138 us

timer_mktime spend time:138 us

timer_mktime spend time:145 us

造成上述问题的原因究竟是什么呢?

2. 源码分析mktime性能

2.1 mktime源码:

mktime.c

 

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

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

 

/* Convert *TP to a time_t value. */

time_t

mktime (struct tm *tp)

{

#ifdef _LIBC

/* POSIX.1 8.1.1 requires that whenever mktime() is called, the

time zone names contained in the external variable 'tzname' shall

be set as if the tzset() function had been called. */

__tzset ();

#endif

return __mktime_internal (tp, __localtime_r, &localtime_offset);

}

/* Convert *TP to a time_t value, inverting

the monotonic and mostly-unit-linear conversion function CONVERT.

Use *OFFSET to keep track of a guess at the offset of the result,

compared to what the result would be for UTC without leap seconds.

If *OFFSET's guess is correct, only one CONVERT call is needed.

This function is external because it is used also by timegm.c. */

time_t

__mktime_internal (struct tm *tp,

struct tm *(*convert) (const time_t *, struct tm *),

time_t *offset)

{

time_t t, gt, t0, t1, t2;

struct tm tm;

/* The maximum number of probes (calls to CONVERT) should be enough

to handle any combinations of time zone rule changes, solar time,

leap seconds, and oscillations around a spring-forward gap.

POSIX.1 prohibits leap seconds, but some hosts have them anyway. */

int remaining_probes = 6;

/* Time requested. Copy it in case CONVERT modifies *TP; this can

occur if TP is localtime's returned value and CONVERT is localtime. */

int sec = tp->tm_sec;

int min = tp->tm_min;

int hour = tp->tm_hour;

int mday = tp->tm_mday;

int mon = tp->tm_mon;

int year_requested = tp->tm_year;

int isdst = tp->tm_isdst;

/* 1 if the previous probe was DST. */

int dst2;

/* Ensure that mon is in range, and set year accordingly. */

int mon_remainder = mon % 12;

int negative_mon_remainder = mon_remainder < 0;

int mon_years = mon / 12 - negative_mon_remainder;

long_int lyear_requested = year_requested;

long_int year = lyear_requested + mon_years;

/* The other values need not be in range:

the remaining code handles minor overflows correctly,

assuming int and time_t arithmetic wraps around.

Major overflows are caught at the end. */

/* Calculate day of year from year, month, and day of month.

The result need not be in range. */

int mon_yday = ((__mon_yday[leapyear (year)]

[mon_remainder + 12 * negative_mon_remainder])

- 1);

long_int lmday = mday;

long_int yday = mon_yday + lmday;

time_t guessed_offset = *offset;

int sec_requested = sec;

if (LEAP_SECONDS_POSSIBLE)

{

/* Handle out-of-range seconds specially,

since ydhms_tm_diff assumes every minute has 60 seconds. */

if (sec < 0)

sec = 0;

if (59 < sec)

sec = 59;

}

/* Invert CONVERT by probing. First assume the same offset as last

time. */

t0 = ydhms_diff (year, yday, hour, min, sec,

EPOCH_YEAR - TM_YEAR_BASE, 0, 0, 0, - guessed_offset);

if (TIME_T_MAX / INT_MAX / 366 / 24 / 60 / 60 < 3)

{

/* time_t isn't large enough to rule out overflows, so check

for major overflows. A gross check suffices, since if t0

has overflowed, it is off by a multiple of TIME_T_MAX -

TIME_T_MIN + 1. So ignore any component of the difference

that is bounded by a small value. */

/* Approximate log base 2 of the number of time units per

biennium. A biennium is 2 years; use this unit instead of

years to avoid integer overflow. For example, 2 average

Gregorian years are 2 * 365.2425 * 24 * 60 * 60 seconds,

which is 63113904 seconds, and rint (log2 (63113904)) is

26. */

int ALOG2_SECONDS_PER_BIENNIUM = 26;

int ALOG2_MINUTES_PER_BIENNIUM = 20;

int ALOG2_HOURS_PER_BIENNIUM = 14;

int ALOG2_DAYS_PER_BIENNIUM = 10;

int LOG2_YEARS_PER_BIENNIUM = 1;

int approx_requested_biennia =

(SHR (year_requested, LOG2_YEARS_PER_BIENNIUM)

- SHR (EPOCH_YEAR - TM_YEAR_BASE, LOG2_YEARS_PER_BIENNIUM)

+ SHR (mday, ALOG2_DAYS_PER_BIENNIUM)

+ SHR (hour, ALOG2_HOURS_PER_BIENNIUM)

+ SHR (min, ALOG2_MINUTES_PER_BIENNIUM)

+ (LEAP_SECONDS_POSSIBLE

? 0

: SHR (sec, ALOG2_SECONDS_PER_BIENNIUM)));

int approx_biennia = SHR (t0, ALOG2_SECONDS_PER_BIENNIUM);

int diff = approx_biennia - approx_requested_biennia;

int approx_abs_diff = diff < 0 ? -1 - diff : diff;

/* IRIX 4.0.5 cc miscalculates TIME_T_MIN / 3: it erroneously

gives a positive value of 715827882. Setting a variable

first then doing math on it seems to work.

(ghazi@caip.rutgers.edu) */

time_t time_t_max = TIME_T_MAX;

time_t time_t_min = TIME_T_MIN;

time_t overflow_threshold =

(time_t_max / 3 - time_t_min / 3) >> ALOG2_SECONDS_PER_BIENNIUM;

if (overflow_threshold < approx_abs_diff)

{

/* Overflow occurred. Try repairing it; this might work if

the time zone offset is enough to undo the overflow. */

time_t repaired_t0 = -1 - t0;

approx_biennia = SHR (repaired_t0, ALOG2_SECONDS_PER_BIENNIUM);

diff = approx_biennia - approx_requested_biennia;

approx_abs_diff = diff < 0 ? -1 - diff : diff;

if (overflow_threshold < approx_abs_diff)

return -1;

guessed_offset += repaired_t0 - t0;

t0 = repaired_t0;

}

}

/* Repeatedly use the error to improve the guess. */

for (t = t1 = t2 = t0, dst2 = 0;

(gt = guess_time_tm (year, yday, hour, min, sec, &t,

ranged_convert (convert, &t, &tm)),

t != gt);

t1 = t2, t2 = t, t = gt, dst2 = tm.tm_isdst != 0)

if (t == t1 && t != t2

&& (tm.tm_isdst < 0

|| (isdst < 0

? dst2 <= (tm.tm_isdst != 0)

: (isdst != 0) != (tm.tm_isdst != 0))))

/* We can't possibly find a match, as we are oscillating

between two values. The requested time probably falls

within a spring-forward gap of size GT - T. Follow the common

practice in this case, which is to return a time that is GT - T

away from the requested time, preferring a time whose

tm_isdst differs from the requested value. (If no tm_isdst

was requested and only one of the two values has a nonzero

tm_isdst, prefer that value.) In practice, this is more

useful than returning -1. */

goto offset_found;

else if (--remaining_probes == 0)

return -1;

/* We have a match. Check whether tm.tm_isdst has the requested

value, if any. */

if (isdst_differ (isdst, tm.tm_isdst))

{

/* tm.tm_isdst has the wrong value. Look for a neighboring

time with the right value, and use its UTC offset.

Heuristic: probe the adjacent timestamps in both directions,

looking for the desired isdst. This should work for all real

time zone histories in the tz database. */

/* Distance between probes when looking for a DST boundary. In

tzdata2003a, the shortest period of DST is 601200 seconds

(e.g., America/Recife starting 2000-10-08 01:00), and the

shortest period of non-DST surrounded by DST is 694800

seconds (Africa/Tunis starting 1943-04-17 01:00). Use the

minimum of these two values, so we don't miss these short

periods when probing. */

int stride = 601200;

/* The longest period of DST in tzdata2003a is 536454000 seconds

(e.g., America/Jujuy starting 1946-10-01 01:00). The longest

period of non-DST is much longer, but it makes no real sense

to search for more than a year of non-DST, so use the DST

max. */

int duration_max = 536454000;

/* Search in both directions, so the maximum distance is half

the duration; add the stride to avoid off-by-1 problems. */

int delta_bound = duration_max / 2 + stride;

int delta, direction;

for (delta = stride; delta < delta_bound; delta += stride)

for (direction = -1; direction <= 1; direction += 2)

if (time_t_int_add_ok (t, delta * direction))

{

time_t ot = t + delta * direction;

struct tm otm;

ranged_convert (convert, &ot, &otm);

if (! isdst_differ (isdst, otm.tm_isdst))

{

/* We found the desired tm_isdst.

Extrapolate back to the desired time. */

t = guess_time_tm (year, yday, hour, min, sec, &ot, &otm);

ranged_convert (convert, &t, &tm);

goto offset_found;

}

}

}

offset_found:

*offset = guessed_offset + t - t0;

if (LEAP_SECONDS_POSSIBLE && sec_requested != tm.tm_sec)

{

/* Adjust time to reflect the tm_sec requested, not the normalized value.

Also, repair any damage from a false match due to a leap second. */

int sec_adjustment = (sec == 0 && tm.tm_sec == 60) - sec;

if (! time_t_int_add_ok (t, sec_requested))

return -1;

t1 = t + sec_requested;

if (! time_t_int_add_ok (t1, sec_adjustment))

return -1;

t2 = t1 + sec_adjustment;

if (! convert (&t2, &tm))

return -1;

t = t2;

}

*tp = tm;

return t;

}

 

tzset.c

 

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

 

void

__tzset (void)

{

__libc_lock_lock (tzset_lock);

tzset_internal (1, 1);

if (!__use_tzfile)

{

/* Set `tzname'. */

__tzname[0] = (char *) tz_rules[0].name;

__tzname[1] = (char *) tz_rules[1].name;

}

__libc_lock_unlock (tzset_lock);

}

weak_alias (__tzset, tzset)

/* Interpret the TZ envariable. */

static void

internal_function

tzset_internal (int always, int explicit)

{

static int is_initialized;

const char *tz;

if (is_initialized && !always)

return;

is_initialized = 1;

/* Examine the TZ environment variable. */

tz = getenv ("TZ");

if (tz == NULL && !explicit)

/* Use the site-wide default. This is a file name which means we

would not see changes to the file if we compare only the file

name for change. We want to notice file changes if tzset() has

been called explicitly. Leave TZ as NULL in this case. */

tz = TZDEFAULT;

if (tz && *tz == '\0')

/* User specified the empty string; use UTC explicitly. */

tz = "Universal";

/* A leading colon means "implementation defined syntax".

We ignore the colon and always use the same algorithm:

try a data file, and if none exists parse the 1003.1 syntax. */

if (tz && *tz == ':')

++tz;

/* Check whether the value changed since the last run. */

if (old_tz != NULL && tz != NULL && strcmp (tz, old_tz) == 0)

/* No change, simply return. */

return;

if (tz == NULL)

/* No user specification; use the site-wide default. */

tz = TZDEFAULT;

tz_rules[0].name = NULL;

tz_rules[1].name = NULL;

/* Save the value of `tz'. */

free (old_tz);

old_tz = tz ? __strdup (tz) : NULL;

/* Try to read a data file. */

__tzfile_read (tz, 0, NULL);

if (__use_tzfile)

return;

/* No data file found. Default to UTC if nothing specified. */

if (tz == NULL || *tz == '\0'

|| (TZDEFAULT != NULL && strcmp (tz, TZDEFAULT) == 0))

{

memset (tz_rules, '\0', sizeof tz_rules);

tz_rules[0].name = tz_rules[1].name = "UTC";

if (J0 != 0)

tz_rules[0].type = tz_rules[1].type = J0;

tz_rules[0].change = tz_rules[1].change = (time_t) -1;

update_vars ();

return;

}

__tzset_parse_tz (tz);

}

 

2.2 源码分析结论

从mktime的源码实现中可以看出,mktime的大致执行流程如下:

  • 首先通过调用__tzset()对时区进行设置
    • 若TZ环境变量为NULL,尝试调用__tzfile_read读取文件中的时区信息
    • 只需要首次设置,若改变或为NULL重新设置
  • 然后调用__mktime_internal进行时间转换
    • 检查系统的tm_isdst(是否为夏令时)和传入的struct tm的tm_isdst,若两个不一致(isdst_differ),则进行矫正tm_isdst

从上源码可以得出结论:影响mktime性能的两个因素主要包括两方面,一是TZ设置是否为NULL,二是传入的struct tm参数的tm_isdst成员与系统的是否一致。

3. 实验分析mktime性能

3.1 TZ对mktime的性能影响

3.1.1 实验程序

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

 

void test_TZ_effect(int count)

{

struct tm temp_tm;

temp_tm.tm_isdst = 0;

std::string time_str = "2019-01-02 00:00:00";

strptime(time_str.c_str(), "%Y-%m-%d %H:%M:%S", &temp_tm);

setenv("TZ", "Asia/Shanghai", 0);

PrintTime(TZ_SET);

for (int i = 0; i < count; i++)

{

mktime(&temp_tm);

}

PrintTimeDone(TZ_SET);

unsetenv("TZ");

PrintTime(TZ_UNSET);

for (int i = 0; i < count; i++)

{

mktime(&temp_tm);

}

PrintTimeDone(TZ_UNSET);

}

3.1.2 实验结果:

 

./a.out 10000

32位机器实验结果:

timer_TZ_SET spend time:3945 us

timer_TZ_UNSET spend time:14332 us

64位机器实验结果:

timer_TZ_SET spend time:2566 us

timer_TZ_UNSET spend time:17459 us

3.1.3 实验结果分析

从源码分析可以知道,当TZ为NULL时,将会尝试读取文件,故不设置TZ时性能较低,约为设置的1/6

3.2 tm_isdst对mktime性能影响

3.2.1 实验程序

 

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

 

void test_TZ_effect(int count)

{

struct tm temp_tm;

std::string time_str = "2019-01-02 00:00:00";

strptime(time_str.c_str(), "%Y-%m-%d %H:%M:%S", &temp_tm);

setenv("TZ", "Asia/Shanghai", 0);

PrintTime(DST_INNER_0);

for (int i = 0; i < count; i++)

{

temp_tm.tm_isdst = 0;

mktime(&temp_tm);

}

PrintTimeDone(DST_INNER_0);

PrintTime(DST_INNER_1);

for (int i = 0; i < count; i++)

{

temp_tm.tm_isdst = 1;

mktime(&temp_tm);

}

PrintTimeDone(DST_INNER_1);

PrintTime(DST_INNER_NAG1);

for (int i = 0; i < count; i++)

{

temp_tm.tm_isdst = -1;

mktime(&temp_tm);

}

PrintTimeDone(DST_INNER_NAG1);

/************************************/

PrintTime(DST_OUTTER_0);

temp_tm.tm_isdst = 0;

for (int i = 0; i < count; i++)

{

mktime(&temp_tm);

}

PrintTimeDone(DST_OUTTER_0);

PrintTime(DST_OUTTER_1);

temp_tm.tm_isdst = 1;

for (int i = 0; i < count; i++)

{

mktime(&temp_tm);

}

PrintTimeDone(DST_OUTTER_1);

PrintTime(DST_OUTTER_NAG1);

temp_tm.tm_isdst = -1;

for (int i = 0; i < count; i++)

{

mktime(&temp_tm);

}

PrintTimeDone(DST_OUTTER_NAG1);

}

3.2.2 实验结果

 

$ ./a.out 10000

32位实验结果

imer_DST_INNER_0 spend time:3957 us

timer_DST_INNER_1 spend time:1986811 us

timer_DST_INNER_NAG1 spend time:3871 us

timer_DST_OUTTER_0 spend time:3889 us

timer_DST_OUTTER_1 spend time:4042 us

timer_DST_OUTTER_NAG1 spend time:3958 us

64位实验结果

timer_DST_INNER_0 spend time:2622 us

timer_DST_INNER_1 spend time:1408451 us

timer_DST_INNER_NAG1 spend time:2588 us

timer_DST_OUTTER_0 spend time:2534 us

timer_DST_OUTTER_1 spend time:2698 us

timer_DST_OUTTER_NAG1 spend time:2621 us

3.2.3 实验结果分析

从上面可以看出,当tm_isdst与系统夏令时设置不一致的时候,性能为一致时的1/500左右。为什么将tm_isdst设置放在外面的时候性能又高了很多呢?从源码可以看出,当mktime对isdst进行矫正后,将正确的isdst存到了传入的结构体中,所以第二次进来时,isdst与系统一致,不需要矫正。

特别注意:当tm_isdst初始化为-1的时候,mktime设置是否为夏令时时间时失手编译器的设计影响,所以传入isdst=-1可能会得到错误的结果,即非夏令时按照夏令时计算或者夏令时按照非夏令时计算。

4. 背景中出现的问题以及mktime使用注意事项

4.1 背景中问题原因

通过实验程序以及原程序片段分析,运行过程中,在32位机器上的tm_isdst默认值为0,在64位机器上tm_isdst的默认值为1(与系统和内存环境有关,可能出现不同结果,我的机器上上述问题),导致在32位机器上运行时间较短,而且第一次运行时需要设置TZ环境变量,故第一次运行时间为
51us,后面运行时间较短。而64为机器一致运行时间为150us左右。

4.2 mktime使用注意事项

  • 使用前建议设置TZ环境变量,在大量调用操作过程中,有助于将该部分性能提高到5~6倍.
  • mktime函数中传入的tm的tm_isdst参数必须设置为0或1,若设置为-1可能会得到错误的结果,或者使用默认值导致性能大大降低,国内一般设置为0,因为中国不采用夏令时。

5. 参考链接:

https://github.molgen.mpg.de/git-mirror/glibc/blob/20003c49884422da7ffbc459cdeee768a6fee07b/time/mktime.c
https://github.molgen.mpg.de/git-mirror/glibc/blob/20003c49884422da7ffbc459cdeee768a6fee07b/time/tzset.c
https://blog.csdn.net/aquester/article/details/54669264

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
static __time64_t __cdecl _make__time64_t ( struct tm *tb, int ultflag ) { __time64_t tmptm1, tmptm2, tmptm3; struct tm tbtemp; long dstbias = 0; long timezone = 0; _VALIDATE_RETURN( ( tb != NULL ), EINVAL, ( ( __time64_t )( -1 ) ) ) /* * First, make sure tm_year is reasonably close to being in range. */ if ( ((tmptm1 = tb->tm_year) < _BASE_YEAR - 1) || (tmptm1 > _MAX_YEAR64 + 1) ) goto err_mktime; /* * Adjust month value so it is in the range 0 - 11. This is because * we don't know how many days are in months 12, 13, 14, etc. */ if ( (tb->tm_mon < 0) || (tb->tm_mon > 11) ) { tmptm1 += (tb->tm_mon / 12); if ( (tb->tm_mon %= 12) < 0 ) { tb->tm_mon += 12; tmptm1--; } /* * Make sure year count is still in range. */ if ( (tmptm1 < _BASE_YEAR - 1) || (tmptm1 > _MAX_YEAR64 + 1) ) goto err_mktime; } /***** HERE: tmptm1 holds number of elapsed years *****/ /* * Calculate days elapsed minus one, in the given year, to the given * month. Check for leap year and adjust if necessary. */ tmptm2 = _days[tb->tm_mon]; if ( _IS_LEAP_YEAR(tmptm1) && (tb->tm_mon > 1) ) tmptm2++; /* * Calculate elapsed days since base date (midnight, 1/1/70, UTC) * * * 365 days for each elapsed year since 1970, plus one more day for * each elapsed leap year. no danger of overflow because of the range * check (above) on tmptm1. */ tmptm3 = (tmptm1 - _BASE_YEAR) * 365 + _ELAPSED_LEAP_YEARS(tmptm1); /* * elapsed days to current month (still no possible overflow) */ tmptm3 += tmptm2; /* * elapsed days to current date. */ tmptm1 = tmptm3 + (tmptm2 = (__time64_t)(tb->tm_mday)); /***** HERE: tmptm1 holds number of elapsed days *****/ /* * Calculate elapsed hours since base date */ tmptm2 = tmptm1 * 24; tmptm1 = tmptm2 + (tmptm3 = (__time64_t)tb->tm_hour); /***** HERE: tmptm1 holds number of elapsed hours *****/ /* * Calculate elapsed minutes since base date */ tmptm2 = tmptm1 * 60; tmptm1 = tmptm2 + (tmptm3 = (__time64_t)tb->tm_min); /***** HERE: tmptm1 holds number of elapsed minutes *****/ /* * Calculate elapsed seconds since base date */ tmptm2 = tmptm1 * 60; tmptm1 = tmptm2 + (tmptm3 = (__time64_t)tb->tm_sec); /***** HERE: tmptm1 holds number of elapsed seconds *****/ if ( ultflag ) { /* * Adjust for timezone. No need to check for overflow since * localtime() will check its arg value */ __tzset(); _ERRCHECK(_get_dstbias(&dstbias;)); _ERRCHECK(_get_timezone(&timezone;)); tmptm1 += timezone; /* * Convert this second count back into a time block structure. * If localtime returns NULL, return an error. */ if ( _localtime64_s(&tbtemp;, &tmptm1;) != 0 ) goto err_mktime; /* * Now must compensate for DST. The ANSI rules are to use the * passed-in tm_isdst flag if it is non-negative. Otherwise, * compute if DST applies. Recall that tbtemp has the time without * DST compensation, but has set tm_isdst correctly. */ if ( (tb->tm_isdst > 0) || ((tb->tm_isdst < 0) && (tbtemp.tm_isdst > 0)) ) { tmptm1 += dstbias; if ( _localtime64_s(&tbtemp;, &tmptm1;) != 0 ) goto err_mktime; } } else { if ( _gmtime64_s(&tbtemp;, &tmptm1;) != 0) goto err_mktime; } /***** HERE: tmptm1 holds number of elapsed seconds, adjusted *****/ /***** for local time if requested *****/ *tb = tbtemp; return tmptm1; err_mktime: /* * All errors come to here */ errno = EINVAL; return (__time64_t)(-1); }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值