summaryrefslogtreecommitdiffstats
path: root/winsup/cygwin/tzcode/localtime_wrapper.c
blob: 4e784480b0f4dbdff6adb3d12b31db98f11628f8 (plain)
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
/* localtime.cc: Wrapper of NetBSD tzcode support for Cygwin. See README file.

This file is part of Cygwin.

This software is a copyrighted work licensed under the terms of the
Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
details. */

#include "winsup.h"
#include "perprocess.h"
#include "tz_posixrules.h"
#include <cygwin/version.h>
#include <stdlib.h>
#include <sys/_tz_structs.h>

static NO_COPY SRWLOCK tzset_guard = SRWLOCK_INIT;

// Convert these NetBSD rwlock ops into SRWLocks
#define rwlock_wrlock(X) AcquireSRWLockExclusive(&tzset_guard)
#define rwlock_unlock(X) ReleaseSRWLockExclusive(&tzset_guard)

// Set these NetBSD-related option #defines appropriately for Cygwin
//#define STD_INSPIRED	// early-include private.h below does this
#define lint
#define HAVE_POSIX_DECLS 0
#define USG_COMPAT 1
#define NO_ERROR_IN_DST_GAP
#define state __state

// Turn a specific known kind of const parameter into non-const
#define __UNCONST(X) ((char *) (X))

// Turn off these NetBSD audit-related definitions
#define __aconst
#define _DIAGASSERT(X)

// Supply this Cygwin-specific function in advance of its use in localtime.c
static char *
tzgetwintzi (char *wildabbr, char *outbuf)
{
    TIME_ZONE_INFORMATION tzi;
    char *cp, *dst;
    wchar_t *wsrc;
    div_t d;

    GetTimeZoneInformation (&tzi);
    dst = cp = outbuf;
    for (wsrc = tzi.StandardName; *wsrc; wsrc++)
	if (*wsrc >= L'A' && *wsrc <= L'Z')
	    *dst++ = *wsrc;
    if ((dst - cp) < 3)
      {
	/* In non-english Windows, converted tz.StandardName
	   may not contain a valid standard timezone name. */
	strcpy (cp, wildabbr);
	cp += strlen (wildabbr);
      }
    else
	cp = dst;
    d = div (tzi.Bias + tzi.StandardBias, 60);
    __small_sprintf (cp, "%d", d.quot);
    if (d.rem)
	__small_sprintf (cp = strchr (cp, 0), ":%d", abs (d.rem));
    if (tzi.StandardDate.wMonth)
      {
	cp = strchr (cp, 0);
	dst = cp;
	for (wsrc = tzi.DaylightName; *wsrc; wsrc++)
	    if (*wsrc >= L'A' && *wsrc <= L'Z')
		*dst++ = *wsrc;
	if ((dst - cp) < 3)
	  {
	    /* In non-english Windows, converted tz.DaylightName
	       may not contain a valid daylight timezone name. */
	    strcpy (cp, wildabbr);
	    cp += strlen (wildabbr);
	  }
	else
	    cp = dst;
	d = div (tzi.Bias + tzi.DaylightBias, 60);
	__small_sprintf (cp, "%d", d.quot);
	if (d.rem)
	    __small_sprintf (cp = strchr (cp, 0), ":%d", abs (d.rem));
	cp = strchr (cp, 0);
	__small_sprintf (cp = strchr (cp, 0), ",M%d.%d.%d/%d",
			 tzi.DaylightDate.wMonth,
			 tzi.DaylightDate.wDay,
			 tzi.DaylightDate.wDayOfWeek,
			 tzi.DaylightDate.wHour);
	if (tzi.DaylightDate.wMinute || tzi.DaylightDate.wSecond)
	    __small_sprintf (cp = strchr (cp, 0), ":%d",
			     tzi.DaylightDate.wMinute);
	if (tzi.DaylightDate.wSecond)
	    __small_sprintf (cp = strchr (cp, 0), ":%d",
			     tzi.DaylightDate.wSecond);
	cp = strchr (cp, 0);
	__small_sprintf (cp = strchr (cp, 0), ",M%d.%d.%d/%d",
			 tzi.StandardDate.wMonth,
			 tzi.StandardDate.wDay,
			 tzi.StandardDate.wDayOfWeek,
			 tzi.StandardDate.wHour);
	if (tzi.StandardDate.wMinute || tzi.StandardDate.wSecond)
	    __small_sprintf (cp = strchr (cp, 0), ":%d",
			     tzi.StandardDate.wMinute);
	if (tzi.StandardDate.wSecond)
	    __small_sprintf (cp = strchr (cp, 0), ":%d",
			     tzi.StandardDate.wSecond);
      }
    /* __small_printf ("TZ deduced as `%s'\n", outbuf); */
    return outbuf;
}

// Pull these in early to catch any small issues before the real test
#include "private.h"
#include "tzfile.h"

/* Some NetBSD differences were too difficult to work around..
   so #include a patched copy of localtime.c rather than the NetBSD original.
   Here is a list of the patches...
   (1) fix an erroneous decl of tzdirslash size (flagged by g++)
   (2) add conditional call to Cygwin's tzgetwintzi() from tzsetlcl()
   (3) add Cygwin's historical "posixrules" support to tzloadbody()
*/
#include "localtime.patched.c"

// Don't forget these Cygwin-specific additions from this point to EOF
EXPORT_ALIAS (tzset_unlocked, _tzset_unlocked)

long
__cygwin_gettzoffset (const struct tm *tmp)
{
#ifdef TM_GMTOFF
    if (CYGWIN_VERSION_CHECK_FOR_EXTRA_TM_MEMBERS)
	return tmp->TM_GMTOFF;
#endif /* defined TM_GMTOFF */
    __tzinfo_type *tz = __gettzinfo ();
    /* The sign of this is exactly opposite the envvar TZ.  We
       could directly use the global _timezone for tm_isdst==0,
       but have to use __tzrule for daylight savings.  */
    long offset = -tz->__tzrule[tmp->tm_isdst > 0].offset;
    return offset;
}

const char *
__cygwin_gettzname (const struct tm *tmp)
{
#ifdef TM_ZONE
    if (CYGWIN_VERSION_CHECK_FOR_EXTRA_TM_MEMBERS)
	return tmp->TM_ZONE;
#endif
    return _tzname[tmp->tm_isdst > 0];
}