[LEAPSECS] BBC radio Crowd Science

Brooks Harris brooks at edlmax.com
Tue Jan 31 13:29:40 EST 2017


On 2017-01-30 04:39 PM, Tom Van Baak wrote:
>>> 2017-01-01T00:00:36.5 - 36 s = 2016-12-31T23:59:60.5
>>     What kind of arithmetic is that?
> Hi Michael,
>
> First, there's no problem with this, right?  (Thanks to Steve for catching typo)
>
> 2017-01-01T00:00:35.5 TAI = 2016-12-31T23:59:59.5 UTC
> 2017-01-01T00:00:36.5 TAI = 2016-12-31T23:59:60.5 UTC
> 2017-01-01T00:00:37.5 TAI = 2017-01-01T00:00:00.5 UTC
>
> Now, we want to use "UTC = TAI + (UTC - TAI)" notation. So which is correct:
>
> 2017-01-01T00:00:35.5 TAI - 36 s = 2016-12-31T23:59:59.5 UTC
> 2017-01-01T00:00:36.5 TAI - 36 s = 2016-12-31T23:59:60.5 UTC  ??
> 2017-01-01T00:00:37.5 TAI - 37 s = 2017-01-01T00:00:00.5 UTC
>
> or
>
> 2017-01-01T00:00:35.5 TAI - 36 s = 2016-12-31T23:59:59.5 UTC
> 2017-01-01T00:00:36.5 TAI - 37 s = 2016-12-31T23:59:60.5 UTC  ??
> 2017-01-01T00:00:37.5 TAI - 37 s = 2017-01-01T00:00:00.5 UTC
>
> Neither one is particularly clear to me. Of course in real code it all works because you special case the leap second label discontinuity and make it work. In a sense you replace normal sexagesimal arithmetic with 59-gesimal or 61-gesimal arithmetic for that one minute. But, yeah, I can see that it complicates prose and equations regarding UTC-TAI offsets.
>
>
>
I think its the first one >>
2017-01-01T00:00:36.5 TAI - 36 s = 2016-12-31T23:59:60.5 UTC  ??
2017-01-01T00:00:37.5 TAI - 37 s = 2017-01-01T00:00:00.5 UTC

The Leap Second accrues to the TAI-UTC value immediately after the Leap 
Second.

As Steve Summit said earlier (with his 2015 example)

Positive leap second:

     TAI           UTC           TAI-UTC
     00:00:34.0    23:59:59.0    35
     00:00:34.5    23:59:59.5    35
     00:00:35.0    23:59:60.0    35
     00:00:35.5    23:59:60.5    35
     00:00:36.0    00:00:00.0    36
     00:00:36.5    00:00:00.5    36

Here, in Steve's example, I think the notation is appropriate. The 
YMDhms columns are titled "TAI" and "UTC". Too often in the literature, 
and sometimes in standards specifications, we see a "date" but no 
explanation of what timescale the YMDhms representation is referring to. 
Usually, it means UTC, which I take to mean "Gregorian calendar 
algorithm with Leap Second compensation applied". But sometimes a YMDhms 
form is used to express TAI, in which case its what I'd call "pure" 
Gregorian, that is, with no Leap Second compensation.

To extend the 2016/2017 example more completely:

Two Leap Second metadata variables are required to support positive Leap 
Seconds:
1) TAI-UTC value
2) Is Leap Second - flag marking the actual Leap Second

TAI seconds - seconds-since-TAI1958
YMDhms (TAI) - YMDhms encoding by "pure Gregorian" of seconds-since-TAI1958
TAI-UTC - TAI-UTC value (initial 10s calibration at UTC1972 plus Leap 
Seconds)
UTC seconds - (seconds-since-TAI1958 - TAI-UTC)
LS - Is Leap Second (0 or 1)
YMDhms (UTC) - YMDhms encoding of (seconds-since-TAI1958 - TAI-UTC) by 
"Leap Second compensated Gregorian"

TAI seconds YMDhms (TAI)          TAI-UTC UTC seconds  LS  YMDhms (UTC)
1861920035.0 = 2017-01-01T00:00:35.0 - 36  = 1861919999.0 0 = 
2016-12-31T23:59:59.0
1861920035.5 = 2017-01-01T00:00:35.5 - 36  = 1861919999.5 0 = 
2016-12-31T23:59:59.5
1861920036.0 = 2017-01-01T00:00:36.0 - 36  = 1861920000.0 1 = 
2016-12-31T23:59:60.0
1861920036.5 = 2017-01-01T00:00:36.5 - 36  = 1861920000.5 1 = 
2016-12-31T23:59:60.5
1861920037.0 = 2017-01-01T00:00:37.0 - 37  = 1861920000.0 0 = 
2017-00-00T00:00:00.0
1861920037.5 = 2017-01-01T00:00:37.5 - 37  = 1861920000.5 0 = 
2017-00-00T00:00:00.5
1861920038.0 = 2017-01-01T00:00:38.0 - 37  = 1861920001.0 0 = 
2017-00-00T00:00:01.0

Attached - TAIToUTCDemoConsole - a rudimentary c program using POSIX 
gmtime() (a pure, strict gmtime() with 1 second resolution) demonstrates 
one way to make the calculations. Its output:

2016-2017 Leap Second
TAI seconds  YMDhms (TAI)       TAI-UTC UTC seconds LS  YMDhms (UTC)
1861920035 = 2017-01-01 00:00:35 - 36 = 1861919999  0 = 2016-12-31 23:59:59
1861920036 = 2017-01-01 00:00:36 - 36 = 1861920000  1 = 2016-12-31 23:59:60
1861920037 = 2017-01-01 00:00:37 - 37 = 1861920000  0 = 2017-01-01 00:00:00
1861920038 = 2017-01-01 00:00:38 - 37 = 1861920001  0 = 2017-01-01 00:00:01

-Brooks
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://pairlist6.pair.net/pipermail/leapsecs/attachments/20170131/fb000700/attachment.html>
-------------- next part --------------
//TAIToUTCDemoConsole.h

#ifndef TAIToUTCDemoConsole_h_included
#define TAIToUTCDemoConsole_h_included

/**************************
TAI seconds to YMDhms (UTC)

Demonstrates methods for calculating YMDhms UTC from TAI seconds
using classic POSIX gmtime()
(strict time_t and gmtime() operate on integral seconds)

Two Leap Second metadata variables are required
to support positive Leap Seconds:
1) TAI-UTC value
2) Is Leap Second - flag marking the actual Leap Second

Negative Leap Seconds are not supported

Brooks Harris 2017-01-31
**************************/

#include <stdio.h>
#include <time.h>
#include <memory.h> // memset


////////////////////////
// MSVC 6.0 does not support "ll" printf() directive or long long data type.
// MSVC 6.0 printf() requires "I64" to forant __it64 types
// this is a stupid hack/work-around
//
// The MSVC version replaces any "ll" with "I64"
// and calls vprintf() with modifief formatMod
//
// The Linux verion makes no changes and calls vprintf() 
//
// USE: If a printf() format is to include a 64-bit display
// printf64() with "%lld" or "%llu" instead of standard printf() 

int __cdecl printf64(
        const char *format,
        ...
        );
////////////////////////

#ifdef _MSC_VER
// MSVC //////////////

#include <stdarg.h> // for va_start
#include <string.h> // strcpy()

typedef unsigned __int64 uint64_t;
typedef __int64 int64_t;

int __cdecl printf64(
        const char *format,
        ...
        )
{
// replace any "ll" with "I64"
// and vprintf() with formatMod

char formatMod[64];
memset(formatMod, 0, sizeof(formatMod));
char* pFormatChar = (char*)format;
char *pstrstrll = NULL;
int  i1stllPos = 0;
int iMax = strlen(format);
int iCnt = 0;
int iCntMod = 0;
while(1)
{

if(iCnt > iMax)
	break;

pstrstrll = strstr( pFormatChar, "ll" );
if(pstrstrll)
	i1stllPos = pstrstrll - format;

if(i1stllPos && iCnt == i1stllPos)
	{
	strcat(formatMod, "I64");
	iCntMod += 3;
	pFormatChar += 2;
	iCnt += 2;
	i1stllPos = 0;
	}
else
	{
	formatMod[iCntMod] = *pFormatChar;
	iCntMod++;
	pFormatChar++;
	iCnt++;
	}
}

va_list arglist;
va_start(arglist, format);
return vprintf(formatMod, arglist);
}

// MSVC //////////////
#else // #ifdef _MSC_VER
// LINUX /////////////

#include <inttypes.h>

int __cdecl printf64(
        const char *format,
        ...
        )
{
// Linux //////////////
// no format changes
// call vprintf() with format
va_list arglist;
va_start(arglist, format);
return vprintf(format, arglist);
// Linux //////////////
}

///////////////////////
#endif // #ifdef _MSC_VER

#define STOP_CONSOLE_ON_COMPLETION_IF_YOU_WANT
#ifdef STOP_CONSOLE_ON_COMPLETION_IF_YOU_WANT
#include <conio.h>
#endif // STOP_CONSOLE_ON_COMPLETION_IF_YOU_WANT

#endif // #ifndef TAIToUTCDemoConsole_h_included

-------------- next part --------------
// TAIToUTCDemoConsole.cpp

/**************************
TAI seconds to YMDhms (UTC)

Demonstrates methods for calculating YMDhms UTC from TAI seconds
using classic POSIX gmtime()
(strict time_t and gmtime() operate on integral seconds)

Two Leap Second metadata variables are required
to support positive Leap Seconds:
1) TAI-UTC value
2) Is Leap Second - flag marking the actual Leap Second

Negative Leap Seconds are not supported

Brooks Harris 2017-01-31
**************************/

////////////////////////////////////////////////////////
////////////////////////////////////////////////////////

#include "TAIToUTCDemoConsole.h"

int TAISecondsToYMDhmsUTC(int64_t i64SecondsSinceTAI1958,
																				int iTAI_UTC, 
																				bool bIsLeapSecond)
{

// define offset TAI1958 to Posix 1970 epoch
#define OFFSET_TAI1958_To_UTC1970_378691210 378691210

time_t time_tUTC = 0;
tm* ptmUTC = NULL;

// UTC seconds = TAI seconds - TAI-UTC
int64_t i64SecondsUTC = i64SecondsSinceTAI1958 - iTAI_UTC;

// to Posix 1970 epoch
time_tUTC = (time_t)i64SecondsUTC - OFFSET_TAI1958_To_UTC1970_378691210 + 10;

/////////////////////////
// calculate YMDhms (UTC), 
// Gregorian with Leap Second compensation
if(bIsLeapSecond)
	time_tUTC--; // decrement to yield "YYYY-MM-DD 23:59:59"

// calculate YMDhms UTC
ptmUTC = gmtime(&time_tUTC);

if(bIsLeapSecond)
	ptmUTC->tm_sec = 60;// overwrite tm_sec 59 with 60
											// this is entirely illegal for strict POSIX 
											// but strftime() will format it OK
char sTmYMDhmsUTC[128];
sTmYMDhmsUTC[0] = 0;
strftime( sTmYMDhmsUTC, sizeof(sTmYMDhmsUTC),
"%Y-%m-%d %H:%M:%S", ptmUTC);
/////////////////////////

/////////////////////////
// calculate YMDhms (TAI)
// 'pure' Gregorian with no compensation

// TAI seconds to Posix 1970 epoch 
time_t time_tTAI = (time_t)i64SecondsSinceTAI1958 - 
																		OFFSET_TAI1958_To_UTC1970_378691210 +	10;
tm* ptmTAI = NULL;
ptmTAI = gmtime(&time_tTAI);

char sTmYMDhmsTAI[128];
sTmYMDhmsTAI[0] = 0;
strftime( sTmYMDhmsTAI, sizeof(sTmYMDhmsTAI),
"%Y-%m-%d %H:%M:%S", ptmTAI);
/////////////////////////

printf64("%010lld = %s - %d = %010lld  %d = %s\n", 
i64SecondsSinceTAI1958, 
sTmYMDhmsTAI, 
iTAI_UTC,
i64SecondsUTC,
bIsLeapSecond ? 1 : 0,
sTmYMDhmsUTC);

return 0;
}

int main(int argc, char* argv[])
{

int64_t i64SecondsSinceTAI1958 = 0; // 1958-01-01T00:00:00 (TAI)
int iTAI_UTC = 0;										// not set
bool bIsLeapSecond = false;					// Is Leap Second

printf("2016-2017 Leap Second\n");

printf("TAI seconds  YMDhms (TAI)       TAI-UTC UTC seconds LS  YMDhms (UTC)\n");

// begin at 2017-01-01T00:00:35 (TAI)
// 2016-12-31T23:59:59 (UTC)
i64SecondsSinceTAI1958 = 1861920035; // 2017-01-01T00:00:35 (TAI)
iTAI_UTC = 36;					// no increment
bIsLeapSecond = false;	// NOT Leap Second
TAISecondsToYMDhmsUTC(i64SecondsSinceTAI1958, iTAI_UTC,	bIsLeapSecond);

// the Leap Second at 2016-12-31T23:59:60 (UTC)
i64SecondsSinceTAI1958++;
iTAI_UTC = 36;					// no increment
bIsLeapSecond = true;		// IS Leap Second !!!!
TAISecondsToYMDhmsUTC(i64SecondsSinceTAI1958, iTAI_UTC,	bIsLeapSecond);

// 2017-01-01T00:00:00 (UTC)
i64SecondsSinceTAI1958++;
iTAI_UTC = 37;					// increment TAI-UTC !!!!
bIsLeapSecond = false;	// NOT Leap Second
TAISecondsToYMDhmsUTC(i64SecondsSinceTAI1958, iTAI_UTC,	bIsLeapSecond);

// 2017-01-01T00:00:01 (UTC)
i64SecondsSinceTAI1958++;
iTAI_UTC = 37;					// no increment
bIsLeapSecond = false;	// NOT Leap Second
TAISecondsToYMDhmsUTC(i64SecondsSinceTAI1958, iTAI_UTC,	bIsLeapSecond);

/////////////////////
#define STOP_CONSOLE_ON_COMPLETION_IF_YOU_WANT
#ifdef STOP_CONSOLE_ON_COMPLETION_IF_YOU_WANT
_getch();
#endif // STOP_CONSOLE_ON_COMPLETION_IF_YOU_WANT

return 0;
}




More information about the LEAPSECS mailing list