4 * Author: Lukas Zeller (luz@synthesis.ch)
6 * conversion from/to linear time scale.
8 * Copyright (c) 2002-2009 by Synthesis AG (www.synthesis.ch)
10 * 2002-04-14 : luz : created from pascal source (plani.ch)
13 #include "prefix_file.h"
14 #include "sync_include.h"
15 #include "lineartime.h"
16 #include "timezones.h"
18 #if defined(SYSYNC_TOOL)
19 #include "syncappbase.h" // for CONSOLEPRINTF
20 #include "vtimezone.h" // for CONSOLEPRINTF
25 // Support for SySync Diagnostic Tool
28 // convert between different time formats and zones
29 int timeConv(int argc, const char *argv[])
33 CONSOLEPRINTF((" time <input zone> <output zone> [<input in ISO8601 or lineartime>]"));
34 CONSOLEPRINTF((" Convert between time zone representations:"));
35 CONSOLEPRINTF((" special input zone names: "));
36 CONSOLEPRINTF((" - \"now\" : input is current system time (no <input> required)"));
37 CONSOLEPRINTF((" - \"floating\" : floating time (when no zone from ISO8601 input"));
38 CONSOLEPRINTF((" - \"vtimezone\" : output zone as VTIMEZONE"));
43 if (argc<2 || argc>3) {
44 CONSOLEPRINTF(("2 or 3 arguments required"));
48 string inzone,outzone,s,z;
49 // internal representation
52 timecontext_t incontext,outcontext;
57 // check special "now" case
58 if (strucmp(inzone.c_str(),"now")==0) {
59 // input time is current time in system zone
60 incontext = TCTX_SYSTEM;
61 intime = getSystemNowAs(incontext,&zones);
64 // get input context from name
65 if (!TimeZoneNameToContext(inzone.c_str(),incontext,&zones))
66 incontext=TCTX_UNKNOWN;
67 // input time from 3rd argument
69 CONSOLEPRINTF(("input time required as 3rd argument"));
72 // try to parse as ISO8601
74 uInt16 n=ISO8601StrToTimestamp(argv[2],intime,inpctx);
75 if (n==0 || argv[2][n]!=0) {
76 // no ISO, read as lineartime
78 n=StrToLongLong(argv[2],intime);
80 CONSOLEPRINTF(("input time must be either ISO8601 or decimal lineartime units"));
84 if (!TCTX_IS_UNKNOWN(inpctx))
88 if (strucmp(outzone.c_str(),"vtimezone")==0) {
89 // show input time zone as VTIMEZONE and DAYLIGHT (for current date)
90 intime = getSystemNowAs(incontext,&zones);
91 internalToVTIMEZONE(incontext,z,&zones);
92 CONSOLEPRINTF(("Input time zone represented as VTIMEZONE :\n\nBEGIN:VTIMEZONE\n%sEND:VTIMEZONE\n",z.c_str()));
93 timecontext_t stdoffs;
94 ContextToTzDaylight(incontext,intime,z,stdoffs,&zones);
95 s.erase(); ContextToISO8601StrAppend(s, stdoffs, true);
96 CONSOLEPRINTF(("Input time zone represented as TZ/DAYLIGHT :\n\nTZ:%s\nDAYLIGHT:%s\n",s.c_str(),z.c_str()));
98 else if (!TimeZoneNameToContext(outzone.c_str(),outcontext,&zones))
99 outcontext=TCTX_UNKNOWN;
103 TimestampToISO8601Str(s, intime, incontext, true, true);
104 TimeZoneContextToName(incontext, z, &zones);
105 TzResolveToOffset(incontext, minOffs, intime, false, &zones);
106 CONSOLEPRINTF(("Input : %-25s (%+03hd:%02hd - '%s')", s.c_str(), minOffs/MinsPerHour, abs(minOffs)%MinsPerHour, z.c_str()));
107 // - convert to output
108 if (!TzConvertTimestamp(intime,incontext,outcontext,&zones)) {
109 CONSOLEPRINTF(("input zone cannot be converted to output zone"));
114 TimestampToISO8601Str(s, intime, outcontext, true, true);
115 TimeZoneContextToName(outcontext, z, &zones);
116 TzResolveToOffset(outcontext, minOffs, intime, false, &zones);
117 CONSOLEPRINTF(("Output : %-25s (%+03hd:%02hd - '%s')", s.c_str(), minOffs/MinsPerHour, abs(minOffs)%MinsPerHour, z.c_str()));
122 #endif // SYSYNC_TOOL
127 #ifndef PLATFORM_LROUND
128 // use generic implementation of lround
129 static sInt32 lround(double x) {
136 #ifndef PLATFORM_TRUNC
137 // use generic implementation of trunc
138 static double trunc(double x) {
146 #ifndef PLATFORM_DATE2LINEARDATE
148 // helper: Returns the biggest integer smaller than x
149 static sInt32 lfloor(double x) {
150 #if ( defined __MACH__ && defined __GNUC__ ) || defined _MSC_VER // XCode or Visual Studio
152 return lround( x-0.5 );
154 return (sInt32)floor( x );
158 // convert date to linear date (generic version using our internal scale)
159 /* procedure calcEphTime(year:integer;month,day:byte;hour:single;var julDat:double);
160 { Berechnet Julianisches Datum aus Weltzeit } */
161 lineardate_t date2lineardate(sInt16 aYear, sInt16 aMonth, sInt16 aDay)
163 // use custom algorithm that goes back to year -4712...
165 /* const MinYear= -4712; */
166 const sInt32 MinYear = -4712;
168 /* var a,b:integer; */
171 /* if aMonth<3 then begin year:=year-1; aMonth:=aMonth+12; end; */
172 if (aMonth<3) { aYear--; aMonth+=12; }
173 /* if (aYear<1582) or
174 ((aYear=1582) and (aMonth<10)) or
175 ((aYear=1582) and (aMonth=10) and (aDay<15)) */
178 (aYear==1582 && aMonth<10) ||
179 (aYear==1582 && aMonth==10 && aDay<15)
182 /* then b:=0 { julianisch } */
187 /* else begin a:=floor(aYear/100); b:=2-a+floor(a/4) end; { gregorianisch } */
191 // now calc julian date
192 /*JulDat:=floor(365.25*(aYear-minYear)+1E-6)+round(30.6*(aMonth-3))+aDay+b+58.5;
193 JulDat:=JulDat+hour/24; */
195 (lfloor(365.25*(aYear-MinYear)+1E-6)+lround(30.6*(aMonth-3))+aDay+b+59)
196 - linearDateOriginOffset // apply offset used for this target platform
200 #endif // PLATFORM_DATE2LINEARDATE
203 // convert date to linear time
204 lineartime_t date2lineartime(sInt16 aYear, sInt16 aMonth, sInt16 aDay)
206 return date2lineardate(aYear,aMonth,aDay) * linearDateToTimeFactor;
210 // convert time to linear time
211 lineartime_t time2lineartime(sInt16 aHour, sInt16 aMinute, sInt16 aSecond, sInt16 aMS)
214 ((((lineartime_t)aHour)*60 +
215 (lineartime_t)aMinute)*60 +
216 (lineartime_t)aSecond)*secondToLinearTimeFactor;
217 if (secondToLinearTimeFactor==1000)
223 // convert lineardate to weekday
224 // 0=sunday, 1=monday ... 6=saturday
225 sInt16 lineardate2weekday(lineardate_t aLinearDate)
227 return (aLinearDate+linearDateOriginWeekday) % 7;
228 } // lineardate2weekday
231 // convert lineartime to weekday
232 // 0=sunday, 1=monday ... 6=saturday
233 sInt16 lineartime2weekday(lineartime_t aLinearTime)
235 // juldat seems to be sunday-based :-)
236 return lineardate2weekday(aLinearTime / linearDateToTimeFactor);
237 } // lineardate2weekday
240 // get number of days in a month
241 sInt16 getMonthDays(lineardate_t aLinearDate)
246 // get year and month of given date
247 lineardate2date(aLinearDate,&y,&m,&d);
248 // get first of this month
249 ld = date2lineardate(y,m,1);
250 // calculate next month
252 if (m>12) { m=1; y++; }
253 // return difference between 1st of current and 1st of next month = number of days in month
254 return date2lineardate(y,m,1) - ld;
259 #ifndef PLATFORM_LINEARDATE2DATE
261 // convert lineardate to year/month/day
263 procedure calcDat(zeitZone,julDat:double;var year:integer;var month,day,hour,min:byte;var sec:single);
264 { Berechnet das Kalenderdatum und Weltzeit aus Julianischem Datum }
266 void lineardate2date(lineardate_t aLinearDate,sInt16 *aYearP, sInt16 *aMonthP, sInt16 *aDayP)
271 var JD0,JD,C,E:double;
278 // apply offset correction
279 aLinearDate+=linearDateOriginOffset;
281 // no time, no "correction" for date change at noon
282 /* JD:=julDat+zeitZone/24;
283 JD0:=sInt32(Jd+0.5); */
285 if JD0<2299161 then begin
290 B:=trunc((JD0-1867216.25)/36524.25);
291 C:=Jd0+(B-trunc(B/4))+1525.0;
294 if (aLinearDate<2299161) {
299 B=(sInt32)(trunc((aLinearDate-1867216.25)/36524.25));
300 C=aLinearDate+(B-trunc((double)B/4))+1525.0;
303 D:=trunc((C-122.1)/365.25);
304 E:=365.0*D+trunc(D/4);
305 F:=trunc((C-E)/30.6001);
306 day:=trunc(C-E+0.5)-trunc(30.6001*F);
307 month:=F-1-12*trunc(F/14);
308 year:=D-4715-trunc((7+month)/10);
310 D=(sInt32)(trunc((C-122.1)/365.25));
311 E=365.0*D+trunc((double)D/4);
312 F=(sInt32)(trunc((C-E)/30.6001));
314 sInt16 month = (sInt16)(F-1-12*trunc((double)F/14));
315 if (aDayP) *aDayP=(sInt16)(trunc(C-E+0.5)-trunc(30.6001*F));
316 if (aMonthP) *aMonthP=month;
317 if (aYearP) *aYearP=(sInt16)(D-4715-trunc((double)(7+month)/10.0));
329 #endif // PLATFORM_LINEARDATE2DATE
332 // convert lineartime to year/month/day
333 void lineartime2date(lineartime_t aLinearTime, sInt16 *aYearP, sInt16 *aMonthP, sInt16 *aDayP)
335 lineardate2date(lineartime2dateonly(aLinearTime), aYearP, aMonthP, aDayP);
339 // convert lineartime to h,m,s,ms
340 void lineartime2time(lineartime_t aLinearTime, sInt16 *aHourP, sInt16 *aMinP, sInt16 *aSecP, sInt16 *aMSP)
343 // negative time, create wrap around to make sure time remains positive
344 aLinearTime = lineartime2timeonly(aLinearTime);
346 if (secondToLinearTimeFactor==1) {
351 // we have sub-seconds
352 if (aMSP) *aMSP = aLinearTime % secondToLinearTimeFactor;
353 aLinearTime /= secondToLinearTimeFactor;
355 if (aSecP) *aSecP = aLinearTime % 60;
357 if (aMinP) *aMinP = aLinearTime % 60;
359 if (aHourP) *aHourP = aLinearTime % 24; // to make sure we don't convert date part
363 // convert seconds to linear time
364 lineartime_t seconds2lineartime(sInt32 aSeconds)
366 return aSeconds*secondToLinearTimeFactor;
367 } // seconds2lineartime
370 // convert linear time to seconds
371 sInt32 lineartime2seconds(lineartime_t aLinearTime)
373 return aLinearTime/secondToLinearTimeFactor;
374 } // lineartime2seconds
377 // get time-only part of a linear time
378 lineartime_t lineartime2timeonly(lineartime_t aLinearTime)
380 //return aLinearTime % linearDateToTimeFactor;
381 return aLinearTime-lineartime2dateonlyTime(aLinearTime);
382 } // lineartime2timeonly
385 // get date-only part of a linear time
386 lineardate_t lineartime2dateonly(lineartime_t aLinearTime)
388 return aLinearTime/linearDateToTimeFactor - (aLinearTime<0 ? 1 : 0);
389 } // lineartime2dateonly
392 // get date-only part of a linear time, in lineartime_t units
393 lineartime_t lineartime2dateonlyTime(lineartime_t aLinearTime)
395 lineartime_t ts = lineartime2dateonly(aLinearTime);
396 ts *= linearDateToTimeFactor;
398 } // lineartime2dateonlyTime
401 } // namespace sysync