1/* Convert between RCS time format and Posix and/or C formats.  */
2
3/* Copyright 1992, 1993, 1994, 1995 Paul Eggert
4   Distributed under license by the Free Software Foundation, Inc.
5
6This file is part of RCS.
7
8RCS is free software; you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation; either version 2, or (at your option)
11any later version.
12
13RCS is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with RCS; see the file COPYING.
20If not, write to the Free Software Foundation,
2159 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22
23Report problems and direct all questions to:
24
25    rcs-bugs@cs.purdue.edu
26
27*/
28
29#include "rcsbase.h"
30#include "partime.h"
31#include "maketime.h"
32
33libId(rcstimeId, "$FreeBSD$")
34
35static long zone_offset; /* seconds east of UTC, or TM_LOCAL_ZONE */
36static int use_zone_offset; /* if zero, use UTC without zone indication */
37
38/*
39* Convert Unix time to RCS format.
40* For compatibility with older versions of RCS,
41* dates from 1900 through 1999 are stored without the leading "19".
42*/
43	void
44time2date(unixtime,date)
45	time_t unixtime;
46	char date[datesize];
47{
48	register struct tm const *tm = time2tm(unixtime, RCSversion<VERSION(5));
49	VOID sprintf(date,
50#		if has_printf_dot
51			"%.2d.%.2d.%.2d.%.2d.%.2d.%.2d",
52#		else
53			"%02d.%02d.%02d.%02d.%02d.%02d",
54#		endif
55		tm->tm_year  +  ((unsigned)tm->tm_year < 100 ? 0 : 1900),
56		tm->tm_mon+1, tm->tm_mday,
57		tm->tm_hour, tm->tm_min, tm->tm_sec
58	);
59}
60
61/* Like str2time, except die if an error was found.  */
62static time_t str2time_checked P((char const*,time_t,long));
63	static time_t
64str2time_checked(source, default_time, default_zone)
65	char const *source;
66	time_t default_time;
67	long default_zone;
68{
69	time_t t = str2time(source, default_time, default_zone);
70	if (t == -1)
71		faterror("unknown date/time: %s", source);
72	return t;
73}
74
75/*
76* Parse a free-format date in SOURCE, convert it
77* into RCS internal format, and store the result into TARGET.
78*/
79	void
80str2date(source, target)
81	char const *source;
82	char target[datesize];
83{
84	time2date(
85		str2time_checked(source, now(),
86			use_zone_offset ? zone_offset
87			: RCSversion<VERSION(5) ? TM_LOCAL_ZONE
88			: 0
89		),
90		target
91	);
92}
93
94/* Convert an RCS internal format date to time_t.  */
95	time_t
96date2time(source)
97	char const source[datesize];
98{
99	char s[datesize + zonelenmax];
100	return str2time_checked(date2str(source, s), (time_t)0, 0);
101}
102
103
104/* Set the time zone for date2str output.  */
105	void
106zone_set(s)
107	char const *s;
108{
109	if ((use_zone_offset = *s)) {
110		long zone;
111		char const *zonetail = parzone(s, &zone);
112		if (!zonetail || *zonetail)
113			error("%s: not a known time zone", s);
114		else
115			zone_offset = zone;
116	}
117}
118
119
120/*
121* Format a user-readable form of the RCS format DATE into the buffer DATEBUF.
122* Yield DATEBUF.
123*/
124	char const *
125date2str(date, datebuf)
126	char const date[datesize];
127	char datebuf[datesize + zonelenmax];
128{
129	register char const *p = date;
130
131	while (*p++ != '.')
132		continue;
133	if (!use_zone_offset)
134	    VOID sprintf(datebuf,
135		"19%.*s/%.2s/%.2s %.2s:%.2s:%s"
136			+ (date[2]=='.' && VERSION(5)<=RCSversion  ?  0  :  2),
137		(int)(p-date-1), date,
138		p, p+3, p+6, p+9, p+12
139	    );
140	else {
141	    struct tm t;
142	    struct tm const *z;
143	    int non_hour;
144	    long zone;
145	    char c;
146
147	    t.tm_year = atoi(date) - (date[2]=='.' ? 0 : 1900);
148	    t.tm_mon = atoi(p) - 1;
149	    t.tm_mday = atoi(p+3);
150	    t.tm_hour = atoi(p+6);
151	    t.tm_min = atoi(p+9);
152	    t.tm_sec = atoi(p+12);
153	    t.tm_wday = -1;
154	    zone = zone_offset;
155	    if (zone == TM_LOCAL_ZONE) {
156		time_t u = tm2time(&t, 0), d;
157		z = localtime(&u);
158		d = difftm(z, &t);
159		zone  =  (time_t)-1 < 0 || d < -d  ?  d  :  -(long)-d;
160	    } else {
161		adjzone(&t, zone);
162		z = &t;
163	    }
164	    c = '+';
165	    if (zone < 0) {
166		zone = -zone;
167		c = '-';
168	    }
169	    VOID sprintf(datebuf,
170#		if has_printf_dot
171		    "%.2d-%.2d-%.2d %.2d:%.2d:%.2d%c%.2d",
172#		else
173		    "%02d-%02d-%02d %02d:%02d:%02d%c%02d",
174#		endif
175		z->tm_year + 1900,
176		z->tm_mon + 1, z->tm_mday, z->tm_hour, z->tm_min, z->tm_sec,
177		c, (int) (zone / (60*60))
178	    );
179	    if ((non_hour = zone % (60*60))) {
180#		if has_printf_dot
181		    static char const fmt[] = ":%.2d";
182#		else
183		    static char const fmt[] = ":%02d";
184#		endif
185		VOID sprintf(datebuf + strlen(datebuf), fmt, non_hour / 60);
186		if ((non_hour %= 60))
187		    VOID sprintf(datebuf + strlen(datebuf), fmt, non_hour);
188	    }
189	}
190	return datebuf;
191}
192