Coverage Report

Created: 2026-10-28 06:40

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/r-source/src/main/datetime.c
Line
Count
Source
1
/*
2
 *  R : A Computer Language for Statistical Data Analysis
3
 *  Copyright (C) 2000-2026  The R Core Team.
4
 *
5
 *  This program is free software; you can redistribute it and/or modify
6
 *  it under the terms of the GNU General Public License as published by
7
 *  the Free Software Foundation; either version 2 of the License, or
8
 *  (at your option) any later version.
9
 *
10
 *  This program is distributed in the hope that it will be useful,
11
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 *  GNU General Public License for more details.
14
 *
15
 *  You should have received a copy of the GNU General Public License
16
 *  along with this program; if not, a copy is available at
17
 *  https://www.R-project.org/Licenses/
18
 *
19
 *
20
 *      Interfaces to POSIX date-time conversion functions.
21
 */
22
23
/*
24
    These use POSIX functions which are also part of C99 so are almost
25
    universally available, albeit with more room for implementation
26
    variations.
27
28
    A particular problem is the setting of the timezone TZ on
29
    Unix/Linux.  POSIX appears to require it, yet older Linux systems
30
    do not set it and do not give the correct results/crash strftime
31
    if it is not set (or even if it is: see the workaround below).  We
32
    use unsetenv() to work around this: that is a BSD (and POSIX 2001)
33
    construct but seems to be available on the affected platforms.
34
35
    The system date-time entry points we use (or substitute) are
36
37
    gmtime_r (or gmtime)
38
    localtime_r (or localtime)
39
    mktime
40
    strftime (in do_formatPOSIXlt)
41
    tzname
42
    tzset
43
44
    PATH 2) also uses R_timegm and R_tzsetwall (a version of tzset
45
    which ignores TZ and loads the current timezone).
46
47
    Via Rstrptime.h we use the system strftime or wcsftime to get the
48
    language-specific names.
49
*/
50
51
/*
52
  R class "POSIXlt" is a list of 9 components, with two optional ones
53
  (but always added as from R 4.3.0).
54
55
  Objects of this class are most often created from character inputs
56
  via strptime() (called by as.POSIXlt.character) or from "POSIXct"
57
  objects. In the first case they may or may not have an associated
58
  time zone: in the second they must.
59
60
  On a system with tm_gmtoff (all current platforms) the components
61
  zone and gmtoff are included, but gmtoff may be NA and usually will
62
  be unless the value was supplied by strptime(, "%z") or by
63
  conversion from "POSIXct" or "Date".
64
65
  There will usually be a "tzone" attribute, of length 1 if only the
66
  name is known or is "UTC', of length 3 including the abbreviations
67
  for all other timezones. (If the timezone does not use DST, the
68
  second abbreviation may be empty or may repeat the first, depending
69
  on the platform., but it will always be present.)  However, if the
70
  call to strptime() does not specify 'tz', this attribute is omitted.
71
72
  Names for the date-times are optional (and rarely supplied):
73
  they are attached to the 'year' element' and used by strptime().
74
*/
75
76
#ifdef HAVE_CONFIG_H
77
# include <config.h>
78
#endif
79
80
// to get tm_zone, tm_gmtoff defined in glibc.
81
// some other header, e.g. math.h, might define the macro so do this first
82
#if defined HAVE_FEATURES_H
83
# include <features.h>
84
# ifdef __GNUC_PREREQ
85
#  if __GNUC_PREREQ(2,20) && !defined(_DEFAULT_SOURCE_)
86
#   define _DEFAULT_SOURCE 1
87
#  endif
88
# endif
89
#endif
90
#if defined(HAVE_GLIBC2) && !defined(_DEFAULT_SOURCE_) && !defined(_BSD_SOURCE)
91
# define _BSD_SOURCE 1
92
#endif
93
94
/*
95
  glibc (with these macros), macOS and internal tzcode all have tm_zone
96
  and tm_gmtoff fields in struct tm.
97
98
  musl has __tm_zone and __tm_gmtoff, which it re-defines without __
99
  if _BSD_SOURCE or _GNU_SOURCE is defined.  But with no macro to
100
  detect musl.  However, they were redefined on the tested Alpine
101
  Linux system which did define _GNU_SOURCE.
102
*/
103
104
#include <time.h>
105
#include <errno.h> // mktime or substitute may set errno.
106
#include <Rmath.h> // for imin2()
107
108
/*
109
110
There are two implementation paths here.
111
Inspectable from R,  sessionInfo()$tzcode_type  is either
112
  1) "system (<libc>)"  (with '<libc>' = 'glibc' usually)  or
113
  2) "internal"
114
115
1) Use the system functions for mktime, gmtime[_r], localtime[_r], strftime.
116
   Use the system time_t, struct tm and time-zone tables.
117
118
   This can be use on glibc, macOS and Solaris (and probably FreeBSD),
119
   but all except 64-bit glibc have issues we can try to work around.
120
   It could in principle be used on Windows but the issues there are
121
   too severe (no support for before 1970) to work around.
122
123
   The system facilities are used for 1902-2037 and outside those
124
   limits where there is a 64-bit time_t and the conversions work
125
   (some OSes have only 32-bit time-zone tables and macOS 13 only
126
   works from 1900).  Otherwise there is code below to extrapolate
127
   from 1902-2037.
128
129
   Other known issues are with strftime (macOS only supports offsets
130
   in multiple of half-hours), not having tzdata tables (possible on
131
   Alpine and now fatal when configuring) and odd issues reading the
132
   time-zone tables, especially for 1939-1945.
133
134
2) USE_INTERNAL_MKTIME : Use substitutes from src/extra/tzone for
135
   mktime, gmtime_r, localtime_r, strftime with a R_ prefix.  The
136
   system strftime is used for locale-dependent names in R_strptime
137
   and R_strftime.  This uses the time-zone tables shipped with R and
138
   installed into R_HOME/share/zoneinfo , with facilities to switch to
139
   others using environment variable TZDIR.
140
141
   Our own versions of time_t (64-bit) and struct tm (including the
142
   BSD-style fields tm_zone and tm_gmtoff) are used.
143
144
   PATH 2) was added for R 3.1.0 (2014-04), is the only one
145
   supported on Windows and is the current default on macOS.
146
147
*/
148
149
// ---------------------------
150
#ifdef USE_INTERNAL_MKTIME
151
// PATH 2)
152
# include "datetime.h"
153
// configure might have checked the system versions, so we override.
154
# undef HAVE_LOCALTIME_R
155
# define HAVE_LOCALTIME_R 1
156
# undef HAVE_TM_ZONE
157
# define HAVE_TM_ZONE 1
158
# undef HAVE_TM_GMTOFF
159
# define HAVE_TM_GMTOFF 1
160
// latterly these are set by configure, but not on Windows
161
# undef MKTIME_SETS_ERRNO
162
# define MKTIME_SETS_ERRNO
163
// these should only be used in PATH 1), but we set them for completeness.
164
# undef HAVE_WORKING_MKTIME_AFTER_2037
165
# define HAVE_WORKING_MKTIME_AFTER_2037 1
166
# undef HAVE_WORKING_MKTIME_BEFORE_1902
167
# define HAVE_WORKING_MKTIME_BEFORE_1902 1
168
# undef HAVE_WORKING_MKTIME_BEFORE_1970
169
# define HAVE_WORKING_MKTIME_BEFORE_1970 1
170
#else // PATH 1)
171
172
typedef struct tm stm;
173
4.36k
#define R_tzname tzname
174
extern char *tzname[2];
175
176
#endif
177
// ---------------------------
178
179
180
#include <stdlib.h> /* for setenv or putenv */
181
#define R_USE_SIGNALS 1
182
#include <Defn.h>
183
#include <Internal.h>
184
185
#ifndef USE_INTERNAL_MKTIME
186
/* PATH 1 */
187
static bool warn1902 = false;
188
#endif
189
190
/* Substitute based on glibc code. */
191
#include "Rstrptime.h"
192
/* --> Def.  R_strptime()  etc */
193
194
static const int month_days[12] =
195
  {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
196
197
// Careful : days_in_year is for base-0 years, days_in_month for base-1900.
198
420k
#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
199
390k
#define days_in_year(year) (isleap(year) ? 366 : 365)
200
25.1k
#define days_in_month(mon, yr) ((mon == 1 && isleap(1900+yr)) ? 29 : month_days[mon])
201
202
/*
203
  Adjust a struct tm to be a valid scalar date-time.
204
  Return 0 if valid, -1 if invalid and uncorrectable, or a positive
205
  integer approximating the number of corrections done.
206
207
  Used in both paths in mktime0, in localtime0 in PATH 1) and in
208
  do_formatPOSIXlt, do_strptime, do_POSIXlt2D, do_balancePOSIXlt.
209
*/
210
static int validate_tm (stm *tm)
211
9.14k
{
212
9.14k
    int tmp, res = 0;
213
214
9.14k
    if (tm->tm_sec < 0 || tm->tm_sec > 60) { /* 61 POSIX, 60 draft ISO C */
215
1.54k
  res++;
216
1.54k
  int tmp = tm->tm_sec/60;
217
1.54k
  tm->tm_sec -= 60 * tmp; tm->tm_min += tmp;
218
1.54k
  if(tm->tm_sec < 0) {tm->tm_sec += 60; tm->tm_min--;}
219
1.54k
    }
220
221
9.14k
    if (tm->tm_min < 0 || tm->tm_min > 59) {
222
1.52k
  res++;
223
1.52k
  int tmp = tm->tm_min/60;
224
1.52k
  tm->tm_min -= 60 * tmp; tm->tm_hour += tmp;
225
1.52k
  if(tm->tm_min < 0) {tm->tm_min += 60; tm->tm_hour--;}
226
1.52k
    }
227
228
9.14k
    if(tm->tm_hour == 24 && tm->tm_min == 0 && tm->tm_sec == 0) { /* 24:00:00 */
229
82
  tm->tm_hour = 0; tm->tm_mday++;
230
82
  if(tm->tm_mon >= 0 && tm->tm_mon <= 11) {
231
82
      if(tm->tm_mday > days_in_month(tm->tm_mon, tm->tm_year)) {
232
24
    tm->tm_mon++; tm->tm_mday = 1;
233
24
    if(tm->tm_mon == 12) {
234
8
        tm->tm_year++; tm->tm_mon = 0;
235
8
    }
236
24
      }
237
82
  }
238
82
    }
239
9.06k
    else if (tm->tm_hour < 0 || tm->tm_hour > 23) {
240
1.57k
  res++;
241
1.57k
  tmp = tm->tm_hour/24;
242
1.57k
  tm->tm_hour -= 24 * tmp; tm->tm_mday += tmp;
243
1.57k
  if(tm->tm_hour < 0) {tm->tm_hour += 24; tm->tm_mday--;}
244
1.57k
    }
245
246
    /* defer fixing mday until we know the year */
247
9.14k
    if (tm->tm_mon < 0 || tm->tm_mon > 11) {
248
726
  res++;
249
726
  tmp = tm->tm_mon/12;
250
726
  tm->tm_mon -= 12 * tmp; tm->tm_year += tmp;
251
726
  if(tm->tm_mon < 0) {tm->tm_mon += 12; tm->tm_year--;}
252
726
    }
253
254
    /* A limit on the loops of about 3000x round.
255
       We could spin backwards or forwards in multiples of 400 years.
256
     */
257
9.14k
    if(tm->tm_mday < -1000000 || tm->tm_mday > 1000000) return -1;
258
259
9.06k
    if(abs(tm->tm_mday) > 366) {
260
1.00k
  res++;
261
  /* first spin back until January */
262
7.50k
  while(tm->tm_mon > 0) {
263
6.50k
      --tm->tm_mon;
264
6.50k
      tm->tm_mday += days_in_month(tm->tm_mon, tm->tm_year);
265
6.50k
  }
266
  /* then spin on/back by years */
267
23.9k
  while(tm->tm_mday < 1) {
268
22.9k
      --tm->tm_year;
269
22.9k
      tm->tm_mday += 365 + (isleap(1900+tm->tm_year)? 1 : 0);
270
22.9k
  }
271
1.18k
  while(tm->tm_mday >
272
1.18k
        (tmp = 365 + (isleap(1900+tm->tm_year)? 1 : 0))) {
273
177
      tm->tm_mday -= tmp; tm->tm_year++;
274
177
  }
275
1.00k
    }
276
277
10.4k
    while(tm->tm_mday < 1) {
278
1.39k
  res++;
279
1.39k
  if(--tm->tm_mon < 0) {tm->tm_mon += 12; tm->tm_year--;}
280
1.39k
  tm->tm_mday += days_in_month(tm->tm_mon, tm->tm_year);
281
1.39k
    }
282
283
15.9k
    while(tm->tm_mday > (tmp = days_in_month(tm->tm_mon, tm->tm_year))) {
284
6.85k
  res++;
285
6.85k
  if(++tm->tm_mon > 11) {tm->tm_mon -= 12; tm->tm_year++;}
286
6.85k
  tm->tm_mday -= tmp;
287
6.85k
    }
288
9.06k
    return res;
289
9.14k
} // validate_tm
290
291
/*
292
  glibc and internal strftime are subject to integer overflow when
293
  tm->tm_year + 1900 does not fit into an integer
294
*/
295
static int likely_strftime_overflow (stm *tm)
296
1.10k
{
297
1.10k
  double year = 1900.0 + tm->tm_year;
298
299
1.10k
#if SIZEOF_INT <= 4
300
1.10k
  return (year > INT_MAX || year < INT_MIN);
301
#else
302
  /* err on the safe side to avoid surprise due to imprecise floating point
303
     representation of the limits */
304
  return !(year < INT_MAX && year > INT_MIN);
305
#endif
306
1.10k
}
307
308
/*
309
   days_in_year is the same for year mod 400.
310
   We could avoid loops altogether by computing how many leap years
311
   there are between 1900 + tm->tm_year and 1900.
312
313
   This will fix up tm->tm_yday and tm->tm_wday.
314
315
   Used in timegm00 (possibly) and guess_offset in PATH 1),
316
   POSIXlt2D and do_balancePOSIXlt
317
*/
318
static double mkdate00 (stm *tm)
319
4.71k
{
320
4.71k
    if(tm->tm_mday == NA_INTEGER || tm->tm_year == NA_INTEGER
321
4.71k
       || tm->tm_mon == NA_INTEGER) {
322
0
  tm->tm_yday = tm->tm_wday = NA_INTEGER;
323
0
  return NA_REAL;
324
0
    }
325
326
4.71k
    int day = tm->tm_mday - 1, /* checked for NA above */
327
4.71k
  year0 = tm->tm_year;
328
4.71k
    double excess = 0.0;
329
4.71k
    if (year0 >= 400) {
330
199
  excess = (int)(year0/400) - 1;
331
199
  year0 -= (int)(excess * 400);
332
4.52k
    } else if (year0 < 0) {
333
1.04k
  excess = -1 - (int)(-year0/400);
334
1.04k
  year0 -= (int)(excess * 400);
335
1.04k
    }
336
4.71k
    year0 += 1900;
337
338
35.9k
    for(int i = 0; i < tm->tm_mon; i++) day += month_days[i];
339
4.71k
    if (tm->tm_mon > 1 && isleap(year0)) day++;
340
4.71k
    tm->tm_yday = day;
341
342
4.71k
    if (year0 > 1970) {
343
378k
  for (int year = 1970; year < year0; year++)
344
374k
      day += days_in_year(year);
345
4.43k
    } else if (year0 < 1970) {
346
5.84k
  for (int year = 1969; year >= year0; year--)
347
5.60k
      day -= days_in_year(year);
348
245
    }
349
350
    /* weekday: Epoch day was a Thursday */
351
4.71k
    if ((tm->tm_wday = ((day % 7) + 4) % 7) < 0) tm->tm_wday += 7;
352
353
4.71k
    return (day + excess * 146097);
354
4.71k
}
355
356
357
/* if d is negative and non-integer then t will be off by one second
358
   since we really need floor(). But floor() is slow, so we just
359
   fix t instead as needed. */
360
#define LOCALTIME_MK_(t_)     \
361
898
        time_t t_ = (time_t) d;     \
362
898
  if (d < 0. && d != (double) t_) t_--
363
364
365
#ifdef USE_INTERNAL_MKTIME
366
/*
367
   PATH 2), internal tzcode
368
369
   Interface to mktime or timegm, version in each PATH. This is PATH 2).
370
   Called from do_asPOSIXct and do_strptime.
371
*/
372
static double mktime0 (stm *tm, const int local)
373
{
374
    if(validate_tm(tm) < 0) {
375
#ifdef EOVERFLOW
376
  errno = EOVERFLOW;
377
#else
378
  errno = 79;
379
#endif
380
  return -1.;
381
    }
382
    return (double)(local ? R_mktime(tm) : R_timegm(tm));
383
}
384
385
/*
386
   Interface to localtime_r or gmtime_r.
387
   Version in each PATH.  This is PATH 2).
388
   Used in do_asPOSIXlt and do_strptime.
389
*/
390
static stm * localtime0(const double *tp, const int local, stm *ltm)
391
{
392
    double d = *tp;
393
    LOCALTIME_MK_(t);
394
    return local ? R_localtime_r(&t, ltm) : R_gmtime_r(&t, ltm);
395
}
396
397
#else
398
//--------------------------------------------------------- long clause ----
399
// PATH 1), using system functions.
400
/*
401
   Substitute for timegm (which is non-POSIX) -- with no checking.  Also,
402
   returns double and needs to be wider than a 32-bit time_t.  So we
403
   could use timegm if it exists _and_ time_t is 64-bit _and_ it supports
404
   a full range of dates (macOS's does not).
405
406
   Used in guess_offset, mktime0 in PATH 1).
407
*/
408
static double timegm00 (stm *tm)
409
3.74k
{
410
    // NA handling may no longer be needed, but left in for safety
411
    // (it caused UBSAN errors).
412
3.74k
    double day = mkdate00(tm); // handles NA inputs
413
3.74k
    if (day == NA_REAL) return NA_REAL;
414
3.74k
    return tm->tm_sec + (tm->tm_min * 60) + (tm->tm_hour * 3600)
415
3.74k
  + day * 86400.0;
416
3.74k
}
417
418
// no known examples recently
419
#ifndef HAVE_POSIX_LEAPSECONDS
420
static int n_leapseconds = 27; // 2017-01, sync with .leap.seconds in R (!)
421
static const time_t leapseconds[] = // dput(unclass(.leap.seconds)) :
422
{  78796800, 94694400,126230400,157766400,189302400,220924800,252460800,
423
  283996800,315532800,362793600,394329600,425865600,489024000,567993600,
424
  631152000,662688000,709948800,741484800,773020800,820454400,867715200,
425
   915148800,1136073600,1230768000,1341100800,1435708800,1483228800};
426
#endif
427
428
static double guess_offset (stm *tm)
429
0
{
430
0
    double offset, offset1, offset2;
431
0
    int i, wday, year, oldmonth, oldisdst, oldmday;
432
0
    stm oldtm;
433
    /*
434
       Adjust as best we can for timezones: if isdst is unknown, use
435
       the smaller offset at same day in Jan or July of a valid year.
436
       We don't know the timezone rules, but if we choose a year with
437
       July 1 on the same day of the week we will likely get guess
438
       right (since they are usually on Sunday mornings not in Jan/Feb).
439
440
       Update for R 2.7.0: no one had DST before 1916, so just use the offset
441
       in 1902, if available.
442
    */
443
444
0
    memcpy(&oldtm, tm, sizeof(stm));
445
0
    if(tm->tm_year < 2) { /* no DST */
446
0
  tm->tm_year = 2;
447
0
  mktime(tm);
448
0
  offset1 = (double) mktime(tm) - timegm00(tm);
449
0
  memcpy(tm, &oldtm, sizeof(stm));
450
0
  tm->tm_isdst = 0;
451
0
  return offset1;
452
0
    }
453
0
    oldmonth = tm->tm_mon;
454
0
    oldmday = tm->tm_mday;
455
    /* We know there was no DST prior to 1916 */
456
0
    oldisdst = (tm->tm_year < 16) ? 0 : tm->tm_isdst;
457
458
    /* so now look for a suitable year */
459
0
    tm->tm_mon = 6;
460
0
    tm->tm_mday = 1;
461
0
    tm->tm_isdst = -1;
462
0
    mkdate00(tm); // fixes tm_wday and tm_yday
463
0
    wday = tm->tm_wday;
464
0
    if (oldtm.tm_year > 137) { /* in the unknown future */
465
0
  for(i = 130; i < 137; i++) { /* These cover all the possibilities */
466
0
      tm->tm_year = i;
467
0
      mktime(tm);
468
0
      if(tm->tm_wday == wday) break;
469
0
  }
470
0
    } else { /* a benighted OS with date before 1970 */
471
  /* We could not use 1970 because of the Windows bug with
472
     1970-01-01 east of GMT. */
473
0
  for(i = 71; i < 82; i++) { /* These cover all the possibilities */
474
0
      tm->tm_year = i;
475
0
      mktime(tm);
476
0
      if(tm->tm_wday == wday) break;
477
0
  }
478
0
    }
479
0
    year = i;
480
481
    /* Now look up offset in January */
482
0
    tm->tm_mday = oldmday;
483
0
    tm->tm_mon = 0;
484
0
    tm->tm_year = year;
485
0
    tm->tm_isdst = -1;
486
0
    offset1 = (double) mktime(tm) - timegm00(tm);
487
    /* and in July */
488
0
    tm->tm_year = year;
489
0
    tm->tm_mon = 6;
490
0
    tm->tm_isdst = -1;
491
0
    offset2 = (double) mktime(tm) - timegm00(tm);
492
0
    if(oldisdst > 0) {
493
0
  offset = (offset1 > offset2) ? offset2 : offset1;
494
0
    } else {
495
0
  offset = (offset1 > offset2) ? offset1 : offset2;
496
0
    }
497
    /* now try to guess dst if unknown */
498
0
    tm->tm_mon = oldmonth;
499
0
    tm->tm_isdst = -1;
500
0
    if(oldisdst < 0) {
501
0
  offset1 = (double) mktime(tm) - timegm00(tm);
502
0
  oldisdst = (offset1 < offset) ? 1:0;
503
0
  if(oldisdst) offset = offset1;
504
0
    }
505
    /* restore all as mktime might alter it */
506
0
    memcpy(tm, &oldtm, sizeof(stm));
507
    /* and then set isdst */
508
0
    tm->tm_isdst = oldisdst;
509
0
    return offset;
510
0
}
511
512
/*
513
   Interface to mktime or timegm00, version in each PATH.
514
   Called from do_asPOSIXct and do_strptime.
515
516
   This is the version for PATH 1)
517
*/
518
static double mktime0 (stm *tm, const int local)
519
4.33k
{
520
4.33k
    double res;
521
4.33k
    bool OK;
522
523
4.33k
    if(validate_tm(tm) < 0) {
524
41
#ifdef EOVERFLOW
525
41
  errno = EOVERFLOW;
526
#else
527
  errno = 79;
528
#endif
529
41
  return -1.;
530
41
    }
531
4.29k
    if(!local) return timegm00(tm);
532
533
/*
534
   Platforms with a 32-bit time_t will fail before 1901-12-13 and after 2038-01-19.
535
   macOS 10.9 gave -1 for dates prior to 1902 and ignored DST after 2037
536
   macOS 13 gives -1 for dates prior to 1900
537
   Windows UCRT (and in 2004) gives -1 for dates prior to 1970
538
   https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_16
539
   states 'the relationship is undefined' prior to 1970.
540
   glibc from 2.2.5 until late 2004 also gave -1 for such dates.
541
*/
542
555
    if(sizeof(time_t) == 8) {
543
555
  OK = true;
544
#ifndef HAVE_WORKING_MKTIME_AFTER_2037
545
  OK = OK && tm->tm_year < 138;
546
#endif
547
#ifndef HAVE_WORKING_MKTIME_BEFORE_1902
548
  OK = OK && tm->tm_year >= 02;
549
  if (tm->tm_year < 02) {
550
      if(!warn1902)
551
    warning(_("datetimes before 1902 may not be accurate: warns once per session"));
552
      warn1902 = true;
553
  }
554
#endif
555
#ifndef HAVE_WORKING_MKTIME_BEFORE_1970
556
  OK = OK && tm->tm_year >= 70;
557
#endif
558
555
    } else {  // 32-bit time_t
559
0
  OK = tm->tm_year < 138 && tm->tm_year >= 02;
560
0
  if (tm->tm_year < 02) {
561
0
      if(!warn1902)
562
0
    warning(_("datetimes before 1902 may not be accurate: warns once per session"));
563
0
      warn1902 = true;
564
0
  }
565
#ifndef HAVE_WORKING_MKTIME_BEFORE_1970
566
  OK = OK && tm->tm_year >= 70;
567
#endif
568
0
    }
569
555
    if(OK) {
570
555
  res = (double) mktime(tm);
571
555
  if (res == -1.) return res;
572
#ifndef HAVE_POSIX_LEAPSECONDS
573
  for(int i = 0; i < n_leapseconds; i++)
574
      if(res > leapseconds[i]) res -= 1.0;
575
#endif
576
553
  return res;
577
/* watch the side effect here: both calls alter their arg */
578
555
    } else {
579
0
  double offset = guess_offset(tm);
580
0
#ifdef HAVE_TM_GMTOFF
581
0
  tm->tm_gmtoff = (long)offset;
582
0
#endif
583
0
  return offset + timegm00(tm);
584
0
    }
585
555
}
586
587
/*
588
   Interface to localtime[_r] or gmtime[_r] or internal substitute.
589
   Version in each PATH: this is PATH 1)
590
   Used in do_asPOSIXlt and do_strptime.
591
*/
592
static stm * localtime0(const double *tp, const int local, stm *ltm)
593
898
{
594
898
    double d = *tp;
595
596
898
    bool OK = true;;
597
/* as mktime is broken, do not trust localtime */
598
898
    if (sizeof(time_t) == 8) {
599
898
  OK = true;
600
#ifndef HAVE_WORKING_MKTIME_AFTER_2037
601
  OK = OK && d < 2147483647.0;
602
#endif
603
#ifndef HAVE_WORKING_MKTIME_BEFORE_1902
604
  if (d <= -2147483647.0) {
605
      if(!warn1902)
606
    warning(_("datetimes before 1902 may not be accurate: warns once per session"));
607
      warn1902 = true;
608
      OK = false;
609
  }
610
  OK = OK && d > -2147483647.0;
611
#endif
612
#ifndef HAVE_WORKING_MKTIME_BEFORE_1970
613
  OK = OK && d >= 0.0;
614
#endif
615
898
    } else { // 32-bit time_t
616
0
  if (d <= -2147483647.0) {
617
0
      if(!warn1902)
618
0
    warning(_("datetimes before 1902 may not be accurate: warns once per session"));
619
0
      warn1902 = true;
620
0
      OK = false;
621
0
  }
622
0
  OK = OK && d < 2147483647.0;
623
#ifndef HAVE_WORKING_MKTIME_BEFORE_1970
624
  OK = OK && d >= 0.0;
625
#endif
626
0
    }
627
898
    if(OK) {
628
898
  LOCALTIME_MK_(t);
629
#ifndef HAVE_POSIX_LEAPSECONDS
630
  for(int y = 0; y < n_leapseconds; y++) if(t > leapseconds[y] + y - 1) t++;
631
#endif
632
  // Recent Linux and macOS have localtime_r
633
898
#ifdef HAVE_LOCALTIME_R
634
898
  return local ? localtime_r(&t, ltm) : gmtime_r(&t, ltm);
635
#else
636
  return local ? localtime(&t) : gmtime(&t);
637
#endif
638
898
    } // end of OK
639
640
    // internal substitute code.
641
642
0
    double dday = floor(d/86400.0);
643
//    static stm ltm0, *res = &ltm0;
644
0
    stm *res = ltm; // be like localtime_r
645
    // This cannot exceed (2^31-1) years in either direction from 1970
646
0
    if (fabs(dday) > 784368402400) { //bail out
647
0
  res->tm_year = NA_INTEGER;
648
0
  res->tm_mon = NA_INTEGER;
649
0
  res->tm_mday = NA_INTEGER;
650
0
  res->tm_yday = NA_INTEGER;
651
0
  res->tm_wday = NA_INTEGER;
652
0
  res->tm_hour = NA_INTEGER;
653
0
  res->tm_min = NA_INTEGER;
654
0
  res->tm_sec = NA_INTEGER;
655
0
  res->tm_isdst = -1;
656
0
  return res;
657
0
    }
658
0
    int left = (int) (d - dday * 86400.0 + 1e-6); // allow for fractional secs
659
660
0
    memset(res, 0, sizeof(stm));
661
    /* hour, min, and sec */
662
0
    res->tm_hour = left / 3600;
663
0
    left %= 3600;
664
0
    res->tm_min = left / 60;
665
0
    res->tm_sec = left % 60;
666
667
    /* weekday: 1970-01-01 was a Thursday */
668
0
    int tmp = (int)(dday - 7 * floor(dday/7)); // day % 7
669
0
    if ((res->tm_wday = ((tmp + 4) % 7)) < 0) res->tm_wday += 7;
670
671
    /* year & day within year */
672
    /* every 400 years is exactly 146097 days long and the
673
       pattern is repeated */
674
0
    double rounds = floor(floor(dday) / 146097.0);
675
0
    dday -= 146097.0 * rounds;
676
0
    int y = (int)(1970 + rounds * 400);
677
0
    if (dday >= 0)
678
0
  for ( ; dday >= (tmp = days_in_year(y)); dday -= tmp, y++);
679
0
    else
680
0
  for ( ; dday < 0; --y, dday += days_in_year(y) );
681
682
0
    y = res->tm_year = y - 1900;
683
0
    int day = (int) dday;
684
0
    res->tm_yday = day;
685
686
    /* month within year */
687
0
    int mon;
688
0
    for (mon = 0;
689
0
   day >= (tmp = days_in_month(mon, y)); // days_in_month is 1900-based
690
0
   day -= tmp, mon++);
691
0
    res->tm_mon = mon;
692
0
    res->tm_mday = day + 1;
693
694
0
    if(local) {
695
  /*  daylight saving time is unknown */
696
0
  res->tm_isdst = -1;
697
  /* Try to fix up time zone differences: cf PR#15480 */
698
0
  int sdiff = (int) guess_offset(res);
699
700
  /* New strategy in 4.3.0: convert as if in UTC, then guess
701
     offset and redo. */
702
0
  d -= sdiff;
703
0
  dday = floor(d/86400.0);
704
0
  left = (int) (d - dday * 86400.0 + 1e-6);
705
0
  res->tm_hour = left / 3600;
706
0
  left %= 3600;
707
0
  res->tm_min = left / 60;
708
0
  res->tm_sec = left % 60;
709
0
  tmp = (int)(dday - 7 * floor(dday/7)); // day % 7
710
0
  if ((res->tm_wday = ((tmp + 4) % 7)) < 0) res->tm_wday += 7;
711
0
  rounds = floor(floor(dday) / 146097.0);
712
0
  dday -= 146097.0 * rounds;
713
0
  int y = (int)(1970 + rounds * 400);
714
0
  if (dday >= 0)
715
0
      for ( ; dday >= (tmp = days_in_year(y)); dday -= tmp, y++);
716
0
  else
717
0
      for ( ; dday < 0; --y, dday += days_in_year(y) );
718
719
0
  y = res->tm_year = y - 1900;
720
0
  int day = (int) dday;
721
0
  res->tm_yday = day;
722
723
  /* month within year */
724
0
  int mon;
725
0
  for (mon = 0;
726
0
       day >= (tmp = days_in_month(mon, y));
727
0
       day -= tmp, mon++);
728
0
  res->tm_mon = mon;
729
0
  res->tm_mday = day + 1;
730
0
  validate_tm(res);
731
0
#ifdef HAVE_TM_GMTOFF
732
0
  res->tm_gmtoff = -sdiff;
733
0
#endif
734
  // No DST before 1916
735
0
  if(res->tm_year < 16) res->tm_isdst = 0;
736
0
  return res;
737
0
    } else {
738
0
  res->tm_isdst = 0; /* no dst in UTC */
739
0
  return res;
740
0
    }
741
0
} /* localtime0() */
742
#endif // end of PATH 1) ---------------------------------------------
743
744
/* Some functions below need to set environment variable TZ and then
745
   (attempt to) set the time-zone accordingly (via set_tz, tzset).
746
   On exit, they attempt to restore the previous TZ and re-set the time-zone
747
   (via reset_tz, tzset). Functions set_tz, reset_tz and the internal tzcode
748
   tzset() may issue warnings, which may be turned into errors and cause a long
749
   jump (PR#17966), before or after setting the time-zone (to the required one,
750
   or as a fallback to UTC).
751
752
   struct tzset_info ... holds information for resetting the time zone
753
                         during local and non-local returns
754
755
   prepare_reset_tz() .. initializes tzset_info and sets up context
756
   set_tz()           .. local setting of tz
757
   reset_tz()         .. local re-setting, also invoked non-locally
758
759
   prepare_dummy_reset_tz()
760
                      .. initializes tzset_info for local invocation
761
                         so that it does nothing (could go away
762
                         after some refactoring)
763
*/
764
765
typedef struct tzset_info {
766
    char oldtz[1001]; /* previous value of TZ variable */
767
    bool hadtz; /* TZ variable existed previously */
768
    bool settz; /* TZ variable was set by us */
769
    RCNTXT cntxt;
770
    bool end_context_on_reset;
771
      /* should endcontext() be called from reset_tz()? */
772
} tzset_info;
773
774
static void reset_tz(tzset_info *si);
775
776
static void cend_reset_tz(void *data)
777
494
{
778
494
    tzset_info *si = (tzset_info *)data;
779
494
    si->end_context_on_reset = false;
780
494
    reset_tz(si);
781
494
}
782
783
static void prepare_reset_tz(tzset_info *si)
784
16.9k
{
785
16.9k
    si->settz = false;
786
    /* set up a context which will reset tz if there is an error */
787
16.9k
    begincontext(&si->cntxt, CTXT_CCODE, R_NilValue, R_BaseEnv, R_BaseEnv,
788
16.9k
                 R_NilValue, R_NilValue);
789
16.9k
    si->cntxt.cend = &cend_reset_tz;
790
16.9k
    si->cntxt.cenddata = si;
791
16.9k
    si->end_context_on_reset = true;
792
16.9k
}
793
794
static void prepare_dummy_reset_tz(tzset_info *si)
795
387
{
796
387
    si->settz = false;
797
387
    si->end_context_on_reset = false;
798
387
}
799
800
static bool set_tz(const char *tz, tzset_info *si)
801
2.50k
{
802
2.50k
    si->settz = false;
803
804
2.50k
    char *p = getenv("TZ");
805
2.50k
    if(p) {
806
0
  if (strlen(p) > 1000)
807
0
      error("time zone specification is too long");
808
0
  strcpy(si->oldtz, p);
809
0
  si->hadtz = true;
810
0
    } else
811
2.50k
  si->hadtz = false;
812
    /* To force tzset() call to refresh tzname, first call tzset() while TZ=UTC.
813
       If the desired setting is actually TZ=UTC and tzname is not refreshed,
814
       it is OK, because UTC has only one possible time zone abbreviation.
815
    */
816
2.50k
    int status = -1; // code = {set,put}env();
817
2.50k
#ifdef HAVE_SETENV
818
2.50k
    status = setenv("TZ", "UTC", 1);
819
#elif defined(HAVE_PUTENV)
820
    {
821
  static char e[] = "TZ=UTC";
822
  status = putenv(e);
823
    }
824
#endif
825
2.50k
    if(!status) tzset();
826
2.50k
    if(!tz[0]) {
827
76
  if(status) {
828
0
      tzset();
829
0
      return si->settz;
830
76
  } else if(si->hadtz) {
831
0
      tz = si->oldtz;
832
76
  } else {
833
76
#ifdef HAVE_UNSETENV
834
76
      if(unsetenv("TZ")) warning(_("problem with unsetting timezone"));
835
#elif defined(HAVE_PUTENV_UNSET)
836
      static char e[] = "TZ";
837
      if(putenv(e)) warning(_("problem with unsetting timezone"));
838
#elif defined(HAVE_PUTENV_UNSET2)
839
      static char e[] = "TZ=";
840
      if(putenv(e)) warning(_("problem with unsetting timezone"));
841
#endif
842
76
      tzset();
843
76
      return si->settz;
844
76
  }
845
76
    }
846
2.43k
#ifdef HAVE_SETENV
847
2.43k
    if(setenv("TZ", tz, 1)) warning(_("problem with setting timezone"));
848
2.43k
    else si->settz = true;
849
#elif defined(HAVE_PUTENV)
850
    {
851
  /* This could be dynamic, but setenv is strongly preferred
852
     (but not available on Windows)
853
     "A program should not alter or free the string"
854
  */
855
  static char buff[1010];
856
  if (strlen(tz) > 1000)
857
      error("time zone specification is too long");
858
  strcpy(buff, "TZ="); strcat(buff, tz);
859
  if(putenv(buff)) warning(_("problem with setting timezone"));
860
  else si->settz = true;
861
    }
862
#else
863
    warning(_("cannot set timezones on this system"));
864
#endif
865
2.43k
    tzset();
866
2.43k
    return si->settz;
867
2.50k
}
868
869
static void reset_tz(tzset_info *si)
870
17.3k
{
871
17.3k
    if (si->end_context_on_reset) {
872
16.4k
  endcontext(&si->cntxt);
873
16.4k
  si->end_context_on_reset = false; /* guard against double reset */
874
16.4k
    }
875
17.3k
    if (!si->settz)
876
14.9k
  return;
877
878
2.43k
    si->settz = false; /* better avoid recursive attempts */
879
2.43k
    if(si->hadtz) {
880
0
#ifdef HAVE_SETENV
881
0
  if(setenv("TZ", si->oldtz, 1))
882
0
      warning(_("problem with setting timezone"));
883
#elif defined(HAVE_PUTENV)
884
  {
885
      static char buff[1010];
886
      strcpy(buff, "TZ="); strcat(buff, si->oldtz); // could use strncat
887
      if(putenv(buff)) warning(_("problem with setting timezone"));
888
  }
889
#endif
890
2.43k
    } else {
891
2.43k
#ifdef HAVE_UNSETENV
892
  // FreeBSD variants used not to return a value, but POSIX requires this
893
2.43k
  if(unsetenv("TZ")) warning(_("problem with unsetting timezone"));
894
#elif defined(HAVE_PUTENV_UNSET)
895
  if(putenv("TZ")) warning(_("problem with unsetting timezone"));
896
#elif defined(HAVE_PUTENV_UNSET2)
897
  if(putenv("TZ=")) warning(_("problem with unsetting timezone"));
898
#endif
899
2.43k
    }
900
2.43k
    tzset();
901
2.43k
}
902
903
// called from do_strptime
904
static void glibc_fix(stm *tm, bool *invalid)
905
1.13k
{
906
    /* set mon and mday which glibc does not always set.
907
       Use current year/... if none has been specified.
908
909
       Specifying mon but not mday nor yday is invalid.
910
    */
911
1.13k
    time_t t = time(NULL);
912
1.13k
    stm *tm0;
913
1.13k
    int tmp;
914
#ifndef HAVE_POSIX_LEAPSECONDS
915
    t -= n_leapseconds;
916
#endif
917
    // Recent Linux and macOS have localtime_r
918
1.13k
#ifdef HAVE_LOCALTIME_R
919
1.13k
    stm tm2;
920
1.13k
    tm0 = localtime_r(&t, &tm2);
921
#else
922
    tm0 = localtime(&t);
923
#endif
924
1.13k
    if(tm->tm_year == NA_INTEGER) tm->tm_year = tm0->tm_year;
925
1.13k
    if(tm->tm_mon != NA_INTEGER && tm->tm_mday != NA_INTEGER) return;
926
    // at least one of the month and the day of the month is missing
927
1.10k
    if(tm->tm_yday != NA_INTEGER) {
928
  // since we have yday, let that take precedence over mon/mday
929
39
  int yday = tm->tm_yday, mon = 0;
930
39
  if (yday < 0 || yday >= days_in_year(1900 + tm->tm_year)) {
931
1
      *invalid = true;
932
1
      return;
933
1
  }
934
126
  while(yday >= (tmp = days_in_month(mon, tm->tm_year))) {
935
88
      yday -= tmp;
936
88
      mon++;
937
88
  }
938
38
  tm->tm_mon = mon;
939
38
  tm->tm_mday = yday + 1;
940
1.06k
    } else {
941
1.06k
  if(tm->tm_mday == NA_INTEGER) {
942
1.03k
      if(tm->tm_mon != NA_INTEGER) {
943
40
    *invalid = true;
944
40
    return;
945
998
      } else tm->tm_mday = tm0->tm_mday;
946
1.03k
  }
947
1.02k
  if(tm->tm_mon == NA_INTEGER) tm->tm_mon = tm0->tm_mon;
948
1.02k
    }
949
1.10k
}
950
951
// Used in do_asPOSIXlt do_strptime do_D2POSIXlt do_balancePOSIXlt
952
static void
953
makelt(stm *tm, SEXP ans, R_xlen_t i, bool valid, double frac_secs)
954
10.9k
{
955
10.9k
    if(valid) {
956
3.86k
  REAL(   VECTOR_ELT(ans, 0))[i] = tm->tm_sec + frac_secs;
957
3.86k
  INTEGER(VECTOR_ELT(ans, 1))[i] = tm->tm_min;
958
3.86k
  INTEGER(VECTOR_ELT(ans, 2))[i] = tm->tm_hour;
959
3.86k
  INTEGER(VECTOR_ELT(ans, 3))[i] = tm->tm_mday;
960
3.86k
  INTEGER(VECTOR_ELT(ans, 4))[i] = tm->tm_mon;
961
3.86k
  INTEGER(VECTOR_ELT(ans, 5))[i] = tm->tm_year;
962
3.86k
  INTEGER(VECTOR_ELT(ans, 6))[i] = tm->tm_wday;
963
3.86k
  INTEGER(VECTOR_ELT(ans, 7))[i] = tm->tm_yday;
964
3.86k
  INTEGER(VECTOR_ELT(ans, 8))[i] = tm->tm_isdst;
965
7.10k
    } else {
966
7.10k
  REAL(VECTOR_ELT(ans, 0))[i] = frac_secs;
967
56.8k
  for(int j = 1; j < 8; j++)
968
49.7k
      INTEGER(VECTOR_ELT(ans, j))[i] = NA_INTEGER;
969
7.10k
  INTEGER(VECTOR_ELT(ans, 8))[i] = -1; // isdst
970
7.10k
    }
971
10.9k
}
972
973
// Used in do_asPOSIXlt do_strptime
974
// Uses tz from enclosing function
975
#define SET_TZONE         \
976
    /* set now in case this gets changed by conversions. */ \
977
9.79k
    SEXP tzone;           \
978
9.79k
    if (isUTC) {         \
979
7.98k
  tzone = PROTECT(mkString(tz));     \
980
7.98k
    } else {           \
981
1.81k
  tzone = PROTECT(allocVector(STRSXP, 3)); \
982
1.81k
  SET_STRING_ELT(tzone, 0, mkChar(tz));    \
983
1.81k
  SET_STRING_ELT(tzone, 1, mkChar(R_tzname[0]));  \
984
1.81k
  SET_STRING_ELT(tzone, 2, mkChar(R_tzname[1]));  \
985
1.81k
    }
986
987
// Used in do_asPOSIXlt()  and  do_strptime() -- uses (stz, isUTC, tz) from enclosing function
988
#define GET_tz_n_CHECK              \
989
13.9k
   if(!isString(stz) || LENGTH(stz) != 1)       \
990
13.9k
  error(_("invalid '%s' value"), "tz");       \
991
13.9k
   const char *tz = CHAR(STRING_ELT(stz, 0));       \
992
13.9k
   bool new_tz = strlen(tz) == 0;  /* tz = "" */      \
993
13.9k
   if(new_tz) {               \
994
76
  /* do a direct look up here as this does not otherwise    \
995
76
     work on Windows */           \
996
76
  char *p = getenv("TZ");           \
997
76
  if(p) {               \
998
0
      stz = mkString(p); /* make a copy */      \
999
0
      tz = CHAR(STRING_ELT(stz, 0));       \
1000
0
      PROTECT(stz);            \
1001
0
  } else               \
1002
76
      new_tz = false;           \
1003
76
    }                  \
1004
13.9k
    /*                  \
1005
13.9k
       isUTC here means that the timezone has been set to UTC   \
1006
13.9k
       either  by default, for example as the system timezone   \
1007
13.9k
       or      via TZ="UTC",            \
1008
13.9k
       or      via a 'tz' argument.         \
1009
13.9k
                  \
1010
13.9k
       It controls setting TZ, the use of gmtime vs localtime, forcing  \
1011
13.9k
       isdst = 0 and how the "tzone" attribute is set.      \
1012
13.9k
    */                  \
1013
13.9k
    bool isUTC = (strcmp(tz, "GMT") == 0 || strcmp(tz, "UTC") == 0);  \
1014
13.9k
    /*                  \
1015
13.9k
      if !isUTC we need to set the tz, not set tm_isdst and use mktime  \
1016
13.9k
      not timegm (or an emulation).         \
1017
13.9k
    */                  \
1018
13.9k
    tzset_info tzsi;              \
1019
13.9k
    prepare_reset_tz(&tzsi);            \
1020
13.9k
                  \
1021
13.9k
    if(!isUTC) set_tz(tz, &tzsi)
1022
1023
1024
// Used in do_asPOSIXlt()  and  do_strptime() -- uses (stz, isUTC, tz) from enclosing function
1025
#define GET_tz_CHECK_n_TZONE            \
1026
9.79k
    GET_tz_n_CHECK;             \
1027
9.79k
    SET_TZONE
1028
1029
1030
// Used in do_asPOSIXlt do_strptime do_D2POSIXlt
1031
// Uses ans ansnames tzone tzsi from enclosing function
1032
#define END_MAKElt          \
1033
10.1k
    setAttrib(ans, R_NamesSymbol, ansnames);   \
1034
10.1k
    SEXP klass = PROTECT(allocVector(STRSXP, 2)); \
1035
10.1k
    SET_STRING_ELT(klass, 0, mkChar("POSIXlt")); \
1036
10.1k
    SET_STRING_ELT(klass, 1, mkChar("POSIXt"));    \
1037
10.1k
    classgets(ans, klass);       \
1038
10.1k
    if(isString(tzone)) setAttrib(ans, install("tzone"), tzone); \
1039
10.1k
    reset_tz(&tzsi);              \
1040
10.1k
    SEXP nm = getAttrib(x, R_NamesSymbol);       \
1041
10.1k
    if(nm != R_NilValue) setAttrib(VECTOR_ELT(ans, 5), R_NamesSymbol, nm); \
1042
10.1k
    MAYBE_INIT_balanced              \
1043
10.1k
    setAttrib(ans, lt_balancedSymbol, _balanced_)
1044
1045
1046
/*
1047
   A POSIXlt object may have 9, 10 or 11 components, but newly created
1048
  ones always have 11..  The optional ones are zone and gmtoff, and it
1049
  may have either or both.  The description does not specify the order
1050
  of the components.  The code assumes the first nine are secs
1051
  ... isdst.  and that the 10th and 11th are zone and gmtoff if
1052
  present.
1053
1054
  Object can have gmtoff without HAVE_TM_GMTOFF.
1055
1056
  Called from do_asPOSIXct do_formatPOSIXlt do_balancePOSIXlt
1057
*/
1058
1059
// used by valid_POSIX do_asPOSIXlt do_strptime do_D2POSIXlt do_balancePOSIXlt
1060
static const char ltnames[][11] =
1061
  // 0     1      2       3       4      5       6       7       8
1062
{ "sec", "min", "hour", "mday", "mon", "year", "wday", "yday", "isdst",
1063
  // 9       10
1064
  "zone",  "gmtoff"};
1065
1066
// validate components 1 ... nm
1067
84.2k
#define isNum(s) ((TYPEOF(s) == INTSXP) || (TYPEOF(s) == REALSXP))
1068
// NB: this can change its argument.
1069
static bool valid_POSIXlt(SEXP x, int nm)
1070
9.35k
{
1071
9.35k
    int n_comp = LENGTH(x); // >= 9, 11 for fresh objects
1072
9.35k
    int n_check = imin2(n_comp, nm);
1073
9.35k
    if(!isVectorList(x) || n_comp < 9)
1074
0
  error(_("a valid \"POSIXlt\" object is a list of at least 9 elements"));
1075
1076
9.35k
    SEXP nms = getAttrib(x, R_NamesSymbol);
1077
9.35k
    if(LENGTH(nms) < 9)
1078
0
  error(_("a valid \"POSIXlt\" object has names"));
1079
1080
    // Now check the names
1081
97.5k
    for (int i = 0; i < n_check ; i++) {
1082
88.2k
  const char *nm = CHAR(STRING_ELT(nms, i));
1083
88.2k
  if (strcmp(nm, ltnames[i]))
1084
0
      error(_("a valid \"POSIXlt\" object has element %d with name '%s' which should be '%s'"),
1085
0
      i+1, nm, ltnames[i]);
1086
88.2k
    }
1087
1088
    // And check the types and coerce if necessary
1089
89.6k
    for (int i = 0; i < imin2(9, nm) ; i++) {
1090
80.3k
  if(!isNum(VECTOR_ELT(x, i)))
1091
0
      error(_("a valid \"POSIXlt\" object has a numeric element %s"),
1092
0
    ltnames[i]);
1093
80.3k
    }
1094
9.35k
    SET_VECTOR_ELT(x, 0, coerceVector(VECTOR_ELT(x, 0), REALSXP));
1095
88.2k
    for(int i = 1; i < n_check; i++) {
1096
78.8k
  if (i == 9) continue; //skip zone
1097
74.9k
  SET_VECTOR_ELT(x, i, coerceVector(VECTOR_ELT(x, i), INTSXP));
1098
74.9k
    }
1099
1100
9.35k
    if(n_check >= 10) {
1101
3.94k
  if(!isString(VECTOR_ELT(x, 9)))
1102
0
      error(_("a valid \"POSIXlt\" object has a character element %s"),
1103
0
      ltnames[9]);
1104
3.94k
    }
1105
9.35k
    if(n_check >= 11) {
1106
3.94k
  if(!isNum(VECTOR_ELT(x, 10)))
1107
0
      error(_("a valid \"POSIXlt\" object has a numeric element %s"),
1108
0
      ltnames[10]);
1109
3.94k
    }
1110
1111
    // check the tzone attribute.
1112
9.35k
    SEXP tz = getAttrib(x, install("tzone"));
1113
9.35k
    if (!isNull(tz)) {
1114
9.35k
  if(!isString(tz))
1115
0
      error(_("invalid '%s'"), "attr(x, \"tzone\")");
1116
9.35k
  int l = LENGTH(tz);
1117
9.35k
  if(l != 1 && l != 3)
1118
0
      error(_("attr(x, \"tzone\") should have length 1 or 3"));
1119
9.35k
    }
1120
1121
9.35k
    return true;
1122
9.35k
}
1123
1124
1125
       /* --------- R interfaces --------- */
1126
1127
static SEXP  /* 'const' globals */
1128
    lt_balancedSymbol = NULL,
1129
    _balanced_ = NULL;
1130
1131
// called from do_asPOSIXlt, do_strptime, do_D2POSIXlt, do_balancePOSIXlt
1132
#define MAYBE_INIT_balanced /* initialize when first used */  \
1133
11.1k
    if(lt_balancedSymbol == NULL) {       \
1134
3
  lt_balancedSymbol = install("balanced");   \
1135
3
  _balanced_ = ScalarLogical(1);        \
1136
3
    }
1137
1138
// We assume time zone names/abbreviations are ASCII, as all known ones are.
1139
1140
// .Internal(as.POSIXlt(x, tz)) -- called only from  as.POSIXlt.POSIXct()
1141
attribute_hidden SEXP do_asPOSIXlt(SEXP call, SEXP op, SEXP args, SEXP env)
1142
2.01k
{
1143
2.01k
    checkArity(op, args);
1144
2.01k
    SEXP x = PROTECT(coerceVector(CAR(args), REALSXP));
1145
2.01k
    SEXP stz = CADR(args);
1146
2.01k
    GET_tz_CHECK_n_TZONE
1147
    /*------------------*/
1148
2.01k
    R_xlen_t n = XLENGTH(x);
1149
2.01k
    int nans = 11;
1150
2.01k
    SEXP ans = PROTECT(allocVector(VECSXP, nans));
1151
20.1k
    for(int i = 0; i < 9; i++)
1152
18.1k
  SET_VECTOR_ELT(ans, i, allocVector(i > 0 ? INTSXP : REALSXP, n));
1153
2.01k
    SET_VECTOR_ELT(ans,  9, allocVector(STRSXP, n)); // zone
1154
2.01k
    SET_VECTOR_ELT(ans, 10, allocVector(INTSXP, n)); // gmtoff
1155
1156
2.01k
    SEXP ansnames = PROTECT(allocVector(STRSXP, nans));
1157
24.2k
    for(int i = 0; i < nans; i++)
1158
22.2k
  SET_STRING_ELT(ansnames, i, mkChar(ltnames[i]));
1159
1160
4.03k
    for(R_xlen_t i = 0; i < n; i++) {
1161
2.01k
  stm dummy, *ptm = &dummy;
1162
2.01k
  double d = REAL(x)[i];
1163
2.01k
  bool valid;
1164
2.01k
  if(R_FINITE(d)) {
1165
877
      ptm = localtime0(&d, !isUTC, &dummy);
1166
      /*
1167
         In theory localtime/gmtime always return a valid struct
1168
         tm pointer, but Windows uses NULL for error conditions
1169
         (like negative times). Not that we use this for
1170
         Windows, but other OSes might also get it wrong.
1171
      */
1172
877
      valid = (ptm != NULL);
1173
1.14k
  } else {
1174
1.14k
      valid = false;
1175
1.14k
  }
1176
2.01k
  makelt(ptm, ans, i, valid, valid ? d - floor(d) : d);
1177
2.01k
  if (isUTC) {
1178
797
      SET_STRING_ELT(VECTOR_ELT(ans, 9), i, mkChar(tz));
1179
797
      INTEGER(VECTOR_ELT(ans, 10))[i] = 0;
1180
1.22k
  } else {
1181
1.22k
      const char *p = "";
1182
      // or ptm->tm_zone (but not specified by POSIX)
1183
1.22k
      if(valid && ptm->tm_isdst >= 0)
1184
526
    p = R_tzname[ptm->tm_isdst];
1185
1.22k
      SET_STRING_ELT(VECTOR_ELT(ans, 9), i, mkChar(p));
1186
1.22k
#ifdef HAVE_TM_GMTOFF
1187
1.22k
      INTEGER(VECTOR_ELT(ans, 10))[i] =
1188
1.22k
    valid ? (int)ptm->tm_gmtoff : NA_INTEGER;
1189
#else
1190
      INTEGER(VECTOR_ELT(ans, 10))[i] = NA_INTEGER;
1191
#endif
1192
1.22k
  }
1193
2.01k
    }
1194
2.01k
    END_MAKElt;
1195
2.01k
    UNPROTECT(5 + (int)new_tz);
1196
2.01k
    return ans;
1197
2.01k
} // asPOSIXlt
1198
1199
#define check_nlen(_i_)             \
1200
80.9k
  if(nlen[_i_] == 0)             \
1201
80.9k
    error(_("zero-length component [[%d]] in non-empty \"POSIXlt\" structure"), (_i_)+1)
1202
1203
// .Internal(as.POSIXct(x, tz)) -- called only from  as.POSIXct.POSIXlt()
1204
attribute_hidden SEXP do_asPOSIXct(SEXP call, SEXP op, SEXP args, SEXP env)
1205
4.12k
{
1206
4.12k
    checkArity(op, args);
1207
1208
4.12k
    SEXP x = PROTECT(duplicate(CAR(args))); // maybe coerced on next line
1209
4.12k
    valid_POSIXlt(x, 9);
1210
4.12k
    SEXP stz = CADR(args);
1211
4.12k
    GET_tz_n_CHECK;
1212
1213
4.12k
    R_xlen_t n = 0, nlen[9];
1214
28.8k
    for(int i = 0; i < 6; i++)
1215
24.7k
  if((nlen[i] = XLENGTH(VECTOR_ELT(x, i))) > n) n = nlen[i];
1216
4.12k
    if((nlen[8] = XLENGTH(VECTOR_ELT(x, 8))) > n) n = nlen[8];
1217
4.12k
    if(n > 0) {
1218
28.5k
  for(int i = 0; i < 6; i++)
1219
24.5k
      check_nlen(i);
1220
4.08k
  check_nlen(8);
1221
4.08k
    }
1222
1223
4.12k
    SEXP ans = PROTECT(allocVector(REALSXP, n));
1224
8.65k
    for(R_xlen_t i = 0; i < n; i++) {
1225
  // This codes assumes a fixed order of components.
1226
4.52k
  double secs = REAL(VECTOR_ELT(x, 0))[i%nlen[0]], fsecs = floor(secs);
1227
4.52k
  stm tm;
1228
  // avoid (int) NAN
1229
4.52k
  tm.tm_sec   = R_FINITE(secs) ? (int) fsecs: NA_INTEGER;
1230
4.52k
  tm.tm_min   = INTEGER(VECTOR_ELT(x, 1))[i%nlen[1]];
1231
4.52k
  tm.tm_hour  = INTEGER(VECTOR_ELT(x, 2))[i%nlen[2]];
1232
4.52k
  tm.tm_mday  = INTEGER(VECTOR_ELT(x, 3))[i%nlen[3]];
1233
4.52k
  tm.tm_mon   = INTEGER(VECTOR_ELT(x, 4))[i%nlen[4]];
1234
4.52k
  tm.tm_year  = INTEGER(VECTOR_ELT(x, 5))[i%nlen[5]];
1235
  /* mktime ignores tm.tm_wday and tm.tm_yday */
1236
4.52k
  tm.tm_isdst = isUTC ? 0 : INTEGER(VECTOR_ELT(x, 8))[i%nlen[8]];
1237
4.52k
  if(!R_FINITE(secs))
1238
2.91k
      REAL(ans)[i] = secs;
1239
1.61k
  else if(tm.tm_min  == NA_INTEGER ||
1240
1.61k
    tm.tm_hour == NA_INTEGER || tm.tm_mday == NA_INTEGER ||
1241
1.61k
    tm.tm_mon  == NA_INTEGER || tm.tm_year == NA_INTEGER)
1242
0
      REAL(ans)[i] = NA_REAL;
1243
1.61k
  else {
1244
1.61k
      errno = 0;
1245
      // Interface to mktime or timegm00, PATH-specific
1246
1.61k
      double tmp = mktime0(&tm, !isUTC);
1247
/*
1248
    POSIX
1249
1250
    POSIX requires that on error, mktime() returns (time_t)-1 and sets errno,
1251
    but previous versions of the specification made setting of errno optional.
1252
    errno on its own is not a reliable indication of error (PR#18532).
1253
*/
1254
1.61k
      REAL(ans)[i] = ((tmp == -1.)
1255
4
#ifdef MKTIME_SETS_ERRNO
1256
1.61k
                      && errno
1257
#else
1258
          /* avoid silly gotcha at epoch minus one sec */
1259
          && (tm.tm_sec != 59)
1260
          && ((tm.tm_sec = 58), (mktime0(&tm, !isUTC) != -2.))
1261
#endif
1262
1.61k
          ) ?
1263
1.61k
        NA_REAL : tmp + (secs - fsecs);
1264
1.61k
  }
1265
4.52k
    }
1266
1267
    // set names() and class() :
1268
4.12k
    SEXP nm = getAttrib(VECTOR_ELT(x, 5), R_NamesSymbol);
1269
4.12k
    if (nm != R_NilValue) setAttrib(ans, R_NamesSymbol, nm);
1270
4.12k
    SEXP klass = PROTECT(allocVector(STRSXP, 2));
1271
4.12k
    SET_STRING_ELT(klass, 0, mkChar("POSIXct"));
1272
4.12k
    SET_STRING_ELT(klass, 1, mkChar("POSIXt"));
1273
4.12k
    classgets(ans, klass);
1274
1275
4.12k
    reset_tz(&tzsi);
1276
4.12k
    UNPROTECT(3 + (int)new_tz);
1277
4.12k
    return ans;
1278
4.12k
} // as.POSIXct()
1279
1280
// .Internal(format.POSIXlt(x, format, usetz)) aka strftime
1281
attribute_hidden SEXP do_formatPOSIXlt(SEXP call, SEXP op, SEXP args, SEXP env)
1282
3.05k
{
1283
3.05k
    checkArity(op, args);
1284
3.05k
    SEXP x = PROTECT(duplicate(CAR(args))); /* maybe coerced in next line */
1285
3.05k
    valid_POSIXlt(x, 11);
1286
1287
3.05k
    SEXP sformat;
1288
3.05k
    if(!isString((sformat = CADR(args))) || XLENGTH(sformat) == 0)
1289
0
  error(_("invalid '%s' argument"), "format");
1290
3.05k
    R_xlen_t m = XLENGTH(sformat);
1291
3.05k
    int UseTZ = asLogical(CADDR(args));
1292
3.05k
    if(UseTZ == NA_LOGICAL)
1293
0
  error(_("invalid '%s' argument"), "usetz");
1294
3.05k
    int digits = asInteger(CADDDR(args)); // checked for NA below
1295
3.05k
    SEXP tz = getAttrib(x, install("tzone"));
1296
3.05k
    if(!isNull(tz) && !isString(tz))
1297
0
  error(_("invalid '%s'"), "attr(x, \"tzone\")");
1298
1299
3.05k
    tzset_info tzsi;
1300
3.05k
    prepare_reset_tz(&tzsi);
1301
3.05k
    const char *tz1;
1302
3.05k
    if (!isNull(tz) && strlen(tz1 = CHAR(STRING_ELT(tz, 0)))) {
1303
  /* If the format includes %Z or %z
1304
     we need to try to set TZ accordingly */
1305
3.05k
  bool needTZ = false;
1306
6.04k
  for(R_xlen_t i = 0; i < m; i++) {
1307
3.05k
      const char *p = translateChar(STRING_ELT(sformat, i));
1308
3.05k
      if (strstr(p, "%Z") || strstr(p, "%z")) {needTZ = true; break;}
1309
3.05k
  }
1310
  /* strftime (per POSIX) calls settz(), so we need to set TZ, but
1311
     we would not have to call settz() directly (except for the
1312
     old OLD_Win32 code) */
1313
3.05k
  if(needTZ) set_tz(tz1, &tzsi);
1314
3.05k
    }
1315
1316
    /* workaround for glibc/FreeBSD/macOS strftime: they have
1317
       non-POSIX/C99 time zone components
1318
     */
1319
3.05k
    stm tm;
1320
3.05k
    memset(&tm, 0, sizeof(tm));
1321
1322
    /* find length of longest one */
1323
3.05k
    R_xlen_t n = 0, nlen[11];
1324
3.05k
    int nn = imin2(LENGTH(x), 11);
1325
36.6k
    for(int i = 0; i < nn; i++) {
1326
33.6k
  nlen[i] = XLENGTH(VECTOR_ELT(x, i));
1327
33.6k
  if(nlen[i] > n) n = nlen[i];
1328
33.6k
    }
1329
3.05k
    if(n > 0) {
1330
36.6k
  for(int i = 0; i < nn; i++)
1331
33.6k
    check_nlen(i);
1332
3.05k
    }
1333
3.05k
    R_xlen_t N = (n > 0) ? ((m > n) ? m : n) : 0;
1334
3.05k
    SEXP ans = PROTECT(allocVector(STRSXP, N));
1335
3.05k
    char tm_zone[20];
1336
3.05k
    bool have_zone =
1337
3.05k
#ifdef HAVE_TM_GMTOFF
1338
3.05k
  LENGTH(x) >= 11;// and components w/ length >= 1
1339
#else
1340
  LENGTH(x) >= 10;
1341
#endif
1342
    // in case it is needed
1343
3.05k
    int ns0 = -1;
1344
6.11k
    for(R_xlen_t i = 0; i < N; i++) {
1345
  // This codes assumes a fixed order of components.
1346
3.05k
  double secs = REAL(VECTOR_ELT(x, 0))[i%nlen[0]], fsecs = floor(secs);
1347
  // avoid (int) NAN
1348
3.05k
  if (R_FINITE(secs) && fsecs >= INT_MIN && fsecs <= INT_MAX)
1349
1.10k
      tm.tm_sec = (int) fsecs;
1350
1.94k
  else
1351
1.94k
      tm.tm_sec = NA_INTEGER;
1352
3.05k
  tm.tm_min   = INTEGER(VECTOR_ELT(x, 1))[i%nlen[1]];
1353
3.05k
  tm.tm_hour  = INTEGER(VECTOR_ELT(x, 2))[i%nlen[2]];
1354
3.05k
  tm.tm_mday  = INTEGER(VECTOR_ELT(x, 3))[i%nlen[3]];
1355
3.05k
  tm.tm_mon   = INTEGER(VECTOR_ELT(x, 4))[i%nlen[4]];
1356
3.05k
  tm.tm_year  = INTEGER(VECTOR_ELT(x, 5))[i%nlen[5]];
1357
3.05k
  tm.tm_wday  = INTEGER(VECTOR_ELT(x, 6))[i%nlen[6]];
1358
3.05k
  tm.tm_yday  = INTEGER(VECTOR_ELT(x, 7))[i%nlen[7]];
1359
3.05k
  tm.tm_isdst = INTEGER(VECTOR_ELT(x, 8))[i%nlen[8]];
1360
3.05k
  if(have_zone) {
1361
3.05k
      strncpy(tm_zone,
1362
3.05k
        CHAR(STRING_ELT(VECTOR_ELT(x, 9), i%nlen[9])),
1363
3.05k
        20 - 1);
1364
3.05k
      tm_zone[20 - 1] = '\0';
1365
3.05k
#ifdef HAVE_TM_ZONE
1366
3.05k
      tm.tm_zone = tm_zone;
1367
#else
1368
      /* This used to be
1369
         if(tm.tm_isdst >= 0) tzname[tm.tm_isdst] = tm_zone;
1370
         Modifying tzname causes memory corruption on Solaris. It
1371
         is not specified to have any effect and strftime is documented
1372
         to call settz().*/
1373
//      if(tm.tm_isdst >= 0 && strcmp(tzname[tm.tm_isdst], tm_zone))
1374
//    warning(_("Timezone specified in the object field cannot be used on this system."));
1375
#endif
1376
3.05k
  }
1377
3.05k
  if(!R_FINITE(secs)) {
1378
1.94k
      SET_STRING_ELT(ans, i,
1379
1.94k
         ISNA(secs)  ? NA_STRING :
1380
1.94k
         ISNAN(secs) ? mkChar("NaN") :
1381
65
         (secs > 0)  ? mkChar("Inf") : mkChar("-Inf"));
1382
1.94k
  } else if(tm.tm_min == NA_INTEGER || tm.tm_hour == NA_INTEGER || tm.tm_mday == NA_INTEGER ||
1383
1.10k
      tm.tm_mon == NA_INTEGER || tm.tm_year == NA_INTEGER) {
1384
0
      SET_STRING_ELT(ans, i, NA_STRING);
1385
1.10k
  } else if(validate_tm(&tm) < 0 || likely_strftime_overflow(&tm)) {
1386
0
      SET_STRING_ELT(ans, i, NA_STRING);
1387
1.10k
  } else {
1388
      /* We could translate to wchar_t and use wcsftime if we
1389
         have it.  But there is no R_wcsftime, nor support in
1390
         IANA's tcode.  It might be safe enough to translate to
1391
         UTF-8 and use strftime -- this is only looking to
1392
         replace short ASCII character sequences. */
1393
1.10k
      const char *q = translateChar(STRING_ELT(sformat, i%m));
1394
1.10k
      int nn = (int) strlen(q) + 50;
1395
1.10k
      char buf2[nn];
1396
1.10k
      const char *p;
1397
1.10k
      strcpy(buf2, q);
1398
1399
1.10k
      p = strstr(q, "%OS");
1400
1.10k
      if(p) {
1401
56
    int ns, nused = 4;
1402
56
    char *p2 = strstr(buf2, "%OS");
1403
56
    *p2 = '\0';
1404
56
    ns = *(p + 3) - '0';
1405
56
    if(ns < 0 || ns > 9) { /* not a digit */
1406
42
        if (ns0 == -1) {
1407
42
      ns0 = digits;
1408
42
      if(ns0 == NA_INTEGER) ns0 = 0;
1409
42
        }
1410
42
        ns = ns0;
1411
42
        nused = 3;
1412
42
    }
1413
56
    if(ns > 6) ns = 6;
1414
56
    if(ns > 0) {
1415
        /* truncate to avoid nuisances such as PR#14579 */
1416
12
        double s = tm.tm_sec + (secs - fsecs), t = Rexp10((double) ns);
1417
12
        s = ((int) (s*t))/t;
1418
12
        snprintf(p2, sizeof(buf2) - (p2 - buf2), "%0*.*f",
1419
12
           ns+3, ns, s);
1420
12
        strcat(buf2, p+nused);
1421
44
    } else {
1422
44
        strcat(p2, "%S");
1423
44
        strcat(buf2, p+nused);
1424
44
    }
1425
56
      }
1426
1427
1.10k
#ifdef HAVE_TM_GMTOFF
1428
1.10k
      if(have_zone) {  // so not in UTC
1429
    // Got coerced if necessary above
1430
1.10k
    int tmp = INTEGER(VECTOR_ELT(x, 10))[i%nlen[10]];
1431
1.10k
    if (tmp == NA_INTEGER && strstr(buf2, "%z")) { // only need it for %z
1432
1
        tm.tm_gmtoff = 0;
1433
# ifdef USE_INTERNAL_MKTIME
1434
        R_mktime(&tm);
1435
# else
1436
        // At least on glibc this corrects it
1437
1
        mktime(&tm);
1438
1
# endif
1439
1.10k
    } else tm.tm_gmtoff = tmp;
1440
1.10k
      }
1441
1.10k
#endif
1442
      // The on-overflow behaviour is not determined by C99-C23.
1443
      // However, this should return 0 so we can throw an error.
1444
1.10k
      char buff[2049];
1445
1.10k
      size_t res;
1446
#ifdef USE_INTERNAL_MKTIME
1447
      res = R_strftime(buff, 2049, buf2, &tm);
1448
#else
1449
1.10k
      res = strftime(buff, 2049, buf2, &tm);
1450
1.10k
#endif
1451
1.10k
      if (res == 0 // overflow for at least internal and glibc
1452
          // if not from a format string that may give zero bytes
1453
0
          && strcmp(buf2, "%Z") && strcmp(buf2, "%z")
1454
0
          && strcmp(buf2, "%P") && strcmp(buf2, "%p")) {
1455
0
    Rf_error("output string exceeded 2048 bytes");
1456
0
      }
1457
1458
      /* Now assume tzone abbreviated name is < 40 bytes,
1459
         but they are currently 3 or 4 bytes.
1460
      */
1461
1.10k
      if(UseTZ) {
1462
646
    if(have_zone) {
1463
646
        const char *p = CHAR(STRING_ELT(VECTOR_ELT(x, 9), i%nlen[9]));
1464
646
        if(strlen(p)) {strcat(buff, " "); strcat(buff, p);}
1465
646
    } else if(!isNull(tz)) {
1466
0
        int ii = 0;
1467
0
        if(LENGTH(tz) == 3) {
1468
0
      if(tm.tm_isdst > 0) ii = 2;
1469
0
      else if(tm.tm_isdst == 0) ii = 1;
1470
0
      else ii = 0; /* Use base timezone name */
1471
0
        }
1472
0
        const char *p = CHAR(STRING_ELT(tz, ii));
1473
0
        if(strlen(p)) {strcat(buff, " "); strcat(buff, p);}
1474
0
    }
1475
646
      }
1476
1.10k
      SET_STRING_ELT(ans, i, mkChar(buff));
1477
1.10k
  }
1478
3.05k
    }
1479
1480
1481
3.05k
    SEXP nm = getAttrib(VECTOR_ELT(x, 5), R_NamesSymbol);
1482
3.05k
    PROTECT(nm);
1483
    // nm has length nlen[5] ; ans has length N
1484
    // so first pad nm to length n, then recycle to length N.
1485
3.05k
    if (nm != R_NilValue) {
1486
0
  SEXP nm2 = PROTECT(xlengthgets(nm, n));
1487
0
  SEXP nm3 = allocVector(STRSXP, N);
1488
0
  for (R_xlen_t j = 0; j < N; j++)
1489
0
      SET_STRING_ELT(nm3, j, STRING_ELT(nm2, j % n));
1490
0
  setAttrib(ans, R_NamesSymbol, nm3);
1491
0
  UNPROTECT(1);
1492
0
    }
1493
1494
3.05k
    reset_tz(&tzsi);
1495
3.05k
    UNPROTECT(3);
1496
3.05k
    return ans;
1497
3.05k
} // do_formatPOSIXlt
1498
1499
1500
// .Internal(strptime(as.character(x), format, tz))
1501
attribute_hidden SEXP do_strptime(SEXP call, SEXP op, SEXP args, SEXP env)
1502
7.77k
{
1503
7.77k
    checkArity(op, args);
1504
1505
7.77k
    SEXP x, sformat;
1506
7.77k
    if(!isString((x = CAR(args))))
1507
0
  error(_("invalid '%s' argument"), "x");
1508
7.77k
    if(!isString((sformat = CADR(args))) || XLENGTH(sformat) == 0)
1509
0
  error(_("invalid '%s' argument"), "format");
1510
7.77k
    SEXP stz = CADDR(args);
1511
7.77k
    GET_tz_CHECK_n_TZONE
1512
    /*------------------*/
1513
7.77k
    R_xlen_t
1514
7.77k
      n = XLENGTH(x),
1515
7.77k
      m = XLENGTH(sformat),
1516
7.77k
      N = (n > 0) ? ((m > n) ? m : n) : 0;
1517
1518
7.77k
    const int nans = 11;
1519
7.77k
    SEXP ans = PROTECT(allocVector(VECSXP, nans));
1520
77.7k
    for(int i = 0; i < 9; i++)
1521
69.9k
  SET_VECTOR_ELT(ans, i, allocVector(i > 0 ? INTSXP : REALSXP, N));
1522
7.77k
    SET_VECTOR_ELT(ans,  9, allocVector(STRSXP, N));
1523
7.77k
    SET_VECTOR_ELT(ans, 10, allocVector(INTSXP, N));
1524
1525
7.77k
    SEXP ansnames = PROTECT(allocVector(STRSXP, nans));
1526
93.3k
    for(int i = 0; i < nans; i++)
1527
85.5k
  SET_STRING_ELT(ansnames, i, mkChar(ltnames[i]));
1528
1529
1530
15.9k
    for(R_xlen_t i = 0; i < N; i++) {
1531
8.17k
  stm tm, tm2, *ptm = &tm;
1532
8.17k
  double psecs = 0.0;
1533
1534
  /* for glibc's sake. That only sets some unspecified fields,
1535
     sometimes. */
1536
8.17k
  memset(&tm, 0, sizeof(stm));
1537
8.17k
  tm.tm_sec = tm.tm_min = tm.tm_hour = 0;
1538
8.17k
  tm.tm_year = tm.tm_mon = tm.tm_mday = tm.tm_yday =
1539
8.17k
      tm.tm_wday = NA_INTEGER;
1540
8.17k
#ifdef HAVE_TM_GMTOFF
1541
8.17k
  tm.tm_gmtoff = (long) NA_INTEGER;
1542
8.17k
  tm.tm_isdst = -1;
1543
8.17k
#endif
1544
8.17k
  int offset = NA_INTEGER;
1545
8.17k
  bool invalid =
1546
8.17k
      STRING_ELT(x, i%n) == NA_STRING ||
1547
8.17k
      !R_strptime(translateChar(STRING_ELT(x, i%n)),
1548
8.17k
      translateChar(STRING_ELT(sformat, i%m)),
1549
8.17k
      &tm, &psecs, &offset);
1550
8.17k
  if(!invalid) {
1551
      /* Solaris sets missing fields to 0 */
1552
2.72k
      if(tm.tm_mday == 0) tm.tm_mday = NA_INTEGER;
1553
2.72k
      if(tm.tm_mon == NA_INTEGER || tm.tm_mday == NA_INTEGER || tm.tm_year == NA_INTEGER)
1554
1.13k
    glibc_fix(&tm, &invalid);
1555
2.72k
      tm.tm_isdst = -1;
1556
2.72k
      if (offset != NA_INTEGER) {
1557
25
#ifdef HAVE_TM_GMTOFF
1558
25
    tm.tm_gmtoff = offset; // not always correct; better than always NA
1559
25
#endif
1560
    /* we know the offset, but not the timezone
1561
       so all we can do is to convert to time_t,
1562
       adjust and convert back */
1563
25
    double t0;
1564
25
    memcpy(&tm2, &tm, sizeof(stm));
1565
    // Interface to mktime or timegm00, PATH-specific
1566
25
    t0 = mktime0(&tm2, 0);
1567
25
    if (t0 != -1) {
1568
21
        t0 -= offset; /* offset = -0800 is Seattle */
1569
21
        ptm = localtime0(&t0, !isUTC, &tm2);
1570
21
    } else invalid = true;
1571
2.69k
      } else {
1572
    /* we do want to set wday, yday, isdst, but not to
1573
       adjust structure at DST boundaries */
1574
2.69k
    memcpy(&tm2, &tm, sizeof(stm));
1575
2.69k
    if (isUTC) tm.tm_isdst = 0;
1576
    /* mktime _may_ result in error e.g. during the spring-forward gap */
1577
2.69k
    if (mktime0(&tm2, !isUTC) != -1) {
1578
        /* set wday, yday, isdst */
1579
2.65k
        tm.tm_wday = tm2.tm_wday;
1580
2.65k
        tm.tm_yday = tm2.tm_yday;
1581
2.65k
        if (!isUTC && tm.tm_hour == tm2.tm_hour
1582
264
                   && tm.tm_min == tm2.tm_min) {
1583
      /* do not adjust tm_isdst when the hours/minutes have
1584
         been adjusted; some mktime implementations adjust
1585
         the (non-existent) time in the spring-forward gap to
1586
         the time after the gap and they adjust the tm_isdst
1587
         value accordingly; taking just one of the two of
1588
         these adjustments would be incorrect (PR#18581). */
1589
263
      tm.tm_isdst = tm2.tm_isdst;
1590
263
        }
1591
2.65k
    }
1592
2.69k
      }
1593
2.72k
      invalid = validate_tm(&tm) != 0;
1594
2.72k
  }
1595
8.17k
  makelt(ptm, ans, i, !invalid, invalid ? NA_REAL : psecs - floor(psecs));
1596
8.17k
  if (isUTC) {
1597
7.09k
      SET_STRING_ELT(VECTOR_ELT(ans, 9), i, mkChar(tz));
1598
7.09k
      INTEGER(VECTOR_ELT(ans, 10))[i] = 0; // gmtoff
1599
7.09k
  } else {
1600
1.08k
      const char *p = "";
1601
1.08k
      if(!invalid && tm.tm_isdst >= 0) {
1602
259
#ifdef HAVE_TM_ZONE
1603
259
    p = tm.tm_zone;
1604
259
    if(!p)
1605
212
#endif
1606
212
        p = R_tzname[tm.tm_isdst];
1607
259
      }
1608
1.08k
      SET_STRING_ELT(VECTOR_ELT(ans, 9), i, mkChar(p));
1609
1.08k
#ifdef HAVE_TM_GMTOFF
1610
1.08k
      INTEGER(VECTOR_ELT(ans, 10))[i] =
1611
1.08k
    invalid ? NA_INTEGER : (int)tm.tm_gmtoff;
1612
#else
1613
      INTEGER(VECTOR_ELT(ans, 10))[i] = NA_INTEGER;
1614
#endif
1615
1.08k
  }
1616
8.17k
    } /* for(i ..) */
1617
1618
7.77k
    END_MAKElt;
1619
7.77k
    if(nm != R_NilValue) {
1620
0
  if (N > n) {// we need to recycle names
1621
0
      SEXP nm3 = allocVector(STRSXP, N);
1622
0
      for (R_xlen_t j = 0; j < N; j++)
1623
0
    SET_STRING_ELT(nm3, j, STRING_ELT(nm, j % n));
1624
0
      setAttrib(VECTOR_ELT(ans, 5), R_NamesSymbol, nm3);
1625
0
  }
1626
0
    }
1627
7.77k
    UNPROTECT(4 + (int)new_tz);
1628
7.77k
    return ans;
1629
7.77k
} // do_strptime
1630
1631
// .Internal(Date2POSIXlt(x)) called from as.POSIXlt.Date
1632
// currently always returns a date-time in UTC (even when tz (=stz) is not !)
1633
attribute_hidden SEXP do_D2POSIXlt(SEXP call, SEXP op, SEXP args, SEXP env)
1634
387
{
1635
387
    checkArity(op, args);
1636
387
    SEXP x = PROTECT(coerceVector(CAR(args), REALSXP));
1637
387
    SEXP stz = CADR(args);
1638
387
    if(!isString(stz) || LENGTH(stz) != 1)
1639
0
  error(_("invalid '%s' value"), "tz");
1640
387
    const char *tz = CHAR(STRING_ELT(stz, 0));
1641
387
    if (!tz[0]) tz = "UTC";
1642
387
    R_xlen_t n = XLENGTH(x);
1643
387
    SEXP ans = PROTECT(allocVector(VECSXP, 11));
1644
3.87k
    for(int i = 0; i < 9; i++)
1645
3.48k
  SET_VECTOR_ELT(ans, i, allocVector(i > 0 ? INTSXP : REALSXP, n));
1646
387
    SET_VECTOR_ELT(ans, 9, allocVector(STRSXP, n));
1647
387
    SET_VECTOR_ELT(ans, 10, allocVector(INTSXP, n));
1648
1649
387
    SEXP ansnames = PROTECT(allocVector(STRSXP, 11));
1650
4.64k
    for(int i = 0; i < 11; i++)
1651
4.25k
  SET_STRING_ELT(ansnames, i, mkChar(ltnames[i]));
1652
1653
774
    for(R_xlen_t i = 0; i < n; i++) {
1654
387
  stm tm;
1655
387
  double x_i = REAL(x)[i];
1656
387
  bool valid = R_FINITE(x_i) != 0;
1657
387
  if(valid) {
1658
      /* every 400 years is exactly 146097 days long and the
1659
         pattern is repeated */
1660
139
      double rounds = floor(floor(x_i) / 146097.0);
1661
139
      int day = (int) (floor(x_i) - 146097.0 * rounds);
1662
139
      tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
1663
      /* weekday: 1970-01-01 was a Thursday */
1664
139
      if ((tm.tm_wday = (((day % 7) + 4) % 7)) < 0) tm.tm_wday += 7;
1665
1666
      /* year & day within year */
1667
139
      int y = 1970, tmp, mon;
1668
139
      if (day >= 0)
1669
10.3k
    for ( ; day >= (tmp = days_in_year(y)); day -= tmp, y++);
1670
0
      else
1671
0
    for ( ; day < 0; --y, day += days_in_year(y) );
1672
      // Avoid overflows
1673
139
      double year0 =  y - 1900 + rounds * 400;
1674
139
      if (year0 > INT_MAX || year0 < INT_MIN)
1675
0
    valid = false;
1676
139
      else {
1677
139
    y = tm.tm_year = (int)year0;
1678
139
    tm.tm_yday = day;
1679
    /* month within year */
1680
139
    for (mon = 0;
1681
1.12k
         day >= (tmp = days_in_month(mon, y));
1682
988
         day -= tmp, mon++);
1683
139
    tm.tm_mon = mon;
1684
139
    tm.tm_mday = day + 1;
1685
139
    tm.tm_isdst = 0; /* no dst in GMT */
1686
139
      }
1687
139
  }
1688
387
  makelt(&tm, ans, i, valid, valid ? 0.0 : x_i);
1689
387
  SET_STRING_ELT(VECTOR_ELT(ans, 9), i, mkChar(tz));
1690
387
  INTEGER(VECTOR_ELT(ans, 10))[i] = 0;
1691
387
    }
1692
387
    SEXP tzone = mkString(tz);
1693
387
    PROTECT(tzone);
1694
1695
387
    tzset_info tzsi; /* tz reset not used */
1696
387
    prepare_dummy_reset_tz(&tzsi);
1697
387
    END_MAKElt;
1698
1699
387
    UNPROTECT(5);
1700
387
    return ans;
1701
387
}
1702
1703
// .Internal(POSIXlt2Date(x)), called from as.Date.POSIXlt(x)
1704
attribute_hidden SEXP do_POSIXlt2D(SEXP call, SEXP op, SEXP args, SEXP env)
1705
1.28k
{
1706
1.28k
    checkArity(op, args);
1707
1.28k
    SEXP x = PROTECT(duplicate(CAR(args)));
1708
1.28k
    valid_POSIXlt(x, 6);
1709
1710
1.28k
    R_xlen_t n = 0, nlen[9];
1711
9.01k
    for(int i = 0; i < 6; i++)
1712
7.72k
  if((nlen[i] = XLENGTH(VECTOR_ELT(x, i))) > n) n = nlen[i];// incl {sec,min,hour}
1713
1.28k
    if((nlen[8] = XLENGTH(VECTOR_ELT(x, 8))) > n) n = nlen[8]; // isdst
1714
1.28k
    if(n > 0) {
1715
9.01k
  for(int i = 0; i < 6; i++)
1716
7.72k
      check_nlen(i);
1717
1.28k
  check_nlen(8);
1718
1.28k
    }
1719
1720
1.28k
    SEXP ans = PROTECT(allocVector(REALSXP, n));
1721
2.57k
    for(R_xlen_t i = 0; i < n; i++) {
1722
  /* need to treat {sec, min, hour} in out-of-range case
1723
     {where fixup *may* change day,month... */
1724
1.28k
  double secs = REAL(VECTOR_ELT(x, 0))[i%nlen[0]], fsecs = floor(secs);
1725
1.28k
  stm tm;
1726
  // avoid (int) NAN
1727
  //  this assumes a fixed order of components.
1728
1.28k
  tm.tm_sec   = R_FINITE(secs) ? (int) fsecs: NA_INTEGER;
1729
1.28k
  tm.tm_min   = INTEGER(VECTOR_ELT(x, 1))[i%nlen[1]];
1730
1.28k
  tm.tm_hour  = INTEGER(VECTOR_ELT(x, 2))[i%nlen[2]];
1731
1.28k
  tm.tm_mday  = INTEGER(VECTOR_ELT(x, 3))[i%nlen[3]];
1732
1.28k
  tm.tm_mon   = INTEGER(VECTOR_ELT(x, 4))[i%nlen[4]];
1733
1.28k
  tm.tm_year  = INTEGER(VECTOR_ELT(x, 5))[i%nlen[5]];
1734
  /* mktime ignores tm.tm_wday and tm.tm_yday */
1735
1.28k
  tm.tm_isdst = 0;
1736
1.28k
  if(!R_FINITE(secs)) // +/-Inf, NA, NaN
1737
727
      REAL(ans)[i] = secs;
1738
561
  else if(tm.tm_min  == NA_INTEGER || tm.tm_hour == NA_INTEGER ||
1739
561
    tm.tm_mday == NA_INTEGER ||
1740
561
    tm.tm_mon  == NA_INTEGER || tm.tm_year == NA_INTEGER)
1741
0
      REAL(ans)[i] = NA_REAL;
1742
561
  else if(validate_tm(&tm) < 0) // validate_tm() fixes up out-of-range {sec,min,...}
1743
0
      REAL(ans)[i] = NA_REAL;
1744
561
  else { // normal case:
1745
561
      REAL(ans)[i] = mkdate00(&tm);
1746
561
  }
1747
1.28k
    }
1748
1749
1.28k
    SEXP nm = getAttrib(VECTOR_ELT(x, 5), R_NamesSymbol);
1750
1.28k
    if (nm != R_NilValue) setAttrib(ans, R_NamesSymbol, nm);
1751
1.28k
    SEXP klass = PROTECT(mkString("Date"));
1752
1.28k
    classgets(ans, klass);
1753
1.28k
    UNPROTECT(3);
1754
1.28k
    return ans;
1755
1.28k
}
1756
1757
static SEXP balancePOSIXlt(SEXP x, bool fill_only, bool do_class)
1758
925
{
1759
925
    MAYBE_INIT_balanced
1760
925
    const SEXP _filled_ = ScalarLogical(NA_LOGICAL);
1761
925
    SEXP bal = getAttrib(x, lt_balancedSymbol);
1762
    /*  bal in  (TRUE, NA, NULL) <==> ("balanced", "filled", <unset>) */
1763
925
    if(bal == _balanced_ || (fill_only && bal == _filled_)) {
1764
38
  if(!do_class) {
1765
38
      x = duplicate(x);
1766
38
      setAttrib(x, R_ClassSymbol, R_NilValue);
1767
38
  }
1768
38
  return(x);
1769
38
    }
1770
1771
887
    valid_POSIXlt(x, 11);
1772
887
    int n_comp = LENGTH(x);
1773
1774
887
    bool need_fill = false;
1775
887
    R_xlen_t n = 0, nlen[n_comp];
1776
10.6k
    for(int i = 0; i < n_comp; i++) {
1777
9.75k
  if((nlen[i] = XLENGTH(VECTOR_ELT(x, i))) > n)
1778
887
      n = nlen[i];
1779
8.87k
  else if(!need_fill && nlen[i] < n)
1780
0
      need_fill = true;
1781
9.75k
    }
1782
887
    if(fill_only && !need_fill) { // already filled; be fast
1783
0
  x = PROTECT(duplicate(x)); // (could mutate in the do_class case)
1784
0
  setAttrib(x, lt_balancedSymbol, _filled_); /* not there; checked above*/
1785
0
  if(!do_class)
1786
0
      setAttrib(x, R_ClassSymbol, R_NilValue);
1787
0
  UNPROTECT(1);
1788
0
  return(x);
1789
0
    }
1790
1791
    // check only now as we cannot return quickly :
1792
887
    if(!inherits(x, "POSIXlt"))
1793
0
  error(_("'%s' is not a \"%s\""), "x", "POSIXlt");
1794
887
    x = PROTECT(duplicate(x));
1795
887
    if(n > 0) {
1796
10.6k
  for(int i = 0; i < n_comp; i++)
1797
9.75k
    check_nlen(i);
1798
  // ==>  n := max(nlen[i]) and all  nlen[i] > 0
1799
887
    }
1800
1801
    // get names(.) [possibly empty]
1802
887
    SEXP nm = getAttrib(VECTOR_ELT(x, 5), R_NamesSymbol);
1803
887
    bool set_nm = (nlen[5] < n || !fill_only) && nm != R_NilValue;
1804
887
    if(set_nm && !fill_only)
1805
0
  PROTECT(nm);
1806
1807
887
    if(fill_only) { // & need_fill
1808
0
  R_xlen_t ni;
1809
  // x[0] : sec (double)
1810
0
  if((ni = nlen[0]) != n) { // recycle sec = x[[0]] to length n
1811
0
      SET_VECTOR_ELT(x, 0, xlengthgets(VECTOR_ELT(x, 0), n));
1812
0
      double *xi = REAL(VECTOR_ELT(x, 0));
1813
0
      for(R_xlen_t ii=ni; ii < n; ii++)
1814
0
    xi[ii] = xi[ii % ni];
1815
0
  }
1816
  // x[1:8] = {min, hour, mday, mon, year, wday, yday, isdst} :
1817
0
  for(int i = 1; i < 9; i++) {
1818
0
      ni = nlen[i];
1819
0
      if(ni != n) { // recycle x[[i]] to length n
1820
    // 1. extend to length (filling with NA; names with ""):
1821
0
    SET_VECTOR_ELT(x, i, xlengthgets(VECTOR_ELT(x, i), n));
1822
    // 2. fill by recycling:
1823
0
    int *xi = INTEGER(VECTOR_ELT(x, i));
1824
0
    for(R_xlen_t ii=ni; ii < n; ii++)
1825
0
        xi[ii] = xi[ii % ni];
1826
0
      }
1827
0
      if(i == 5 && set_nm) { /* set names(.) = names(x[[5]]) = names(x$year) : */
1828
0
    nm = PROTECT(getAttrib(VECTOR_ELT(x, 5), R_NamesSymbol)); // of full length n
1829
    // fill names, recycling:
1830
0
    for(R_xlen_t ii=ni; ii < n; ii++)
1831
0
        SET_STRING_ELT(nm, ii, STRING_ELT(nm, ii % ni));
1832
0
    setAttrib(VECTOR_ELT(x, 5), R_NamesSymbol, nm);
1833
0
    UNPROTECT(1);
1834
0
      }
1835
0
  }
1836
1837
0
  if(n_comp >= 10 && (ni = nlen[9]) != n) { // x[9] : zone (character)
1838
0
      SET_VECTOR_ELT(x, 9, xlengthgets(VECTOR_ELT(x, 9), n));
1839
0
      SEXP xi = VECTOR_ELT(x, 9);
1840
0
      for(R_xlen_t ii = ni; ii < n; ii++)
1841
0
    SET_STRING_ELT(xi, ii, STRING_ELT(xi, ii % ni));
1842
0
  }
1843
1844
0
  if(n_comp >= 11 && (ni = nlen[10]) != n) { // x[10] : gmtoff
1845
0
      SET_VECTOR_ELT(x, 10, xlengthgets(VECTOR_ELT(x, 10), n));
1846
0
      int *xi = INTEGER(VECTOR_ELT(x, 10));
1847
0
      for(R_xlen_t ii = ni; ii < n; ii++)
1848
0
    xi[ii] = xi[ii % ni];
1849
0
  }
1850
0
  if(!do_class) setAttrib(x, R_ClassSymbol, R_NilValue);
1851
0
  setAttrib(x, lt_balancedSymbol, _filled_);
1852
0
  UNPROTECT(1);
1853
0
  return(x);
1854
0
    }
1855
1856
    // fill *and* validate from now on:
1857
1858
887
    bool have_10 = n_comp >= 10, have_11 = n_comp >= 11;
1859
887
    SEXP ans = PROTECT(allocVector(VECSXP, n_comp));
1860
8.87k
    for(int i = 0; i < 9; i++)
1861
7.98k
  SET_VECTOR_ELT(ans, i, allocVector(i > 0 ? INTSXP : REALSXP, n));
1862
887
    if(have_10) SET_VECTOR_ELT(ans, 9, allocVector(STRSXP, n));
1863
887
    if(have_11) SET_VECTOR_ELT(ans, 10, allocVector(INTSXP, n));
1864
1865
887
    SEXP ansnames = PROTECT(allocVector(STRSXP, n_comp));
1866
10.6k
    for(int i = 0; i < n_comp; i++)
1867
9.75k
  SET_STRING_ELT(ansnames, i, mkChar(ltnames[i]));
1868
1869
1.77k
    for(R_xlen_t i = 0; i < n; i++) {
1870
      // 1. fill 'tm'
1871
887
  double secs = REAL(VECTOR_ELT(x, 0))[i%nlen[0]], fsecs = floor(secs);
1872
887
  stm tm;
1873
  // this assumes a fixed order of components.
1874
  // avoid (int) NAN
1875
887
  tm.tm_sec  = R_FINITE(secs) ? (int) fsecs: NA_INTEGER;
1876
887
  tm.tm_min  = INTEGER(VECTOR_ELT(x, 1))[i%nlen[1]];
1877
887
  tm.tm_hour = INTEGER(VECTOR_ELT(x, 2))[i%nlen[2]];
1878
887
  tm.tm_mday = INTEGER(VECTOR_ELT(x, 3))[i%nlen[3]];
1879
887
  tm.tm_mon  = INTEGER(VECTOR_ELT(x, 4))[i%nlen[4]];
1880
887
  tm.tm_year = INTEGER(VECTOR_ELT(x, 5))[i%nlen[5]];
1881
887
  tm.tm_wday = INTEGER(VECTOR_ELT(x, 6))[i%nlen[6]];
1882
887
  tm.tm_yday = INTEGER(VECTOR_ELT(x, 7))[i%nlen[7]];
1883
887
  tm.tm_isdst = INTEGER(VECTOR_ELT(x, 8))[i%nlen[8]];
1884
887
  char tm_zone[20];
1885
887
  if(have_10) {
1886
887
      strncpy(tm_zone, CHAR(STRING_ELT(VECTOR_ELT(x, 9), i%nlen[9])), 20 - 1);
1887
887
      tm_zone[20 - 1] = '\0';
1888
887
#ifdef HAVE_TM_ZONE
1889
887
      tm.tm_zone = tm_zone;
1890
#else
1891
      /* Modifying tzname causes memory corruption on Solaris. It
1892
         is not specified to have any effect and strftime is documented
1893
         to call settz().*/
1894
//      if(tm.tm_isdst >= 0 && strcmp(tzname[tm.tm_isdst], tm_zone))
1895
//    warning(_("Timezone specified in the object's 'zone' component cannot be used on this system."));
1896
#endif
1897
887
  }
1898
887
#ifdef HAVE_TM_GMTOFF
1899
887
  if (have_11)
1900
887
      tm.tm_gmtoff = INTEGER(VECTOR_ELT(x, 10))[i%nlen[10]];
1901
0
  else
1902
0
      tm.tm_gmtoff = NA_INTEGER; // or -1 or 0 ??
1903
887
#endif
1904
1905
  /* 2. checking for NA/non-finite --------------
1906
   * ----------- careful:
1907
   * validate_tm() must *not* be called if any other components are NA.
1908
   */
1909
887
  bool valid =
1910
887
      (R_FINITE(secs) &&
1911
417
       tm.tm_min  != NA_INTEGER &&
1912
417
       tm.tm_hour != NA_INTEGER &&
1913
417
       tm.tm_mday != NA_INTEGER &&
1914
417
       tm.tm_mon  != NA_INTEGER &&
1915
417
       tm.tm_year != NA_INTEGER);
1916
1917
887
  if(valid) {
1918
417
      validate_tm(&tm);
1919
      // Set correct {yday, wday}:
1920
      // The standards-conformant way to get these set
1921
      // is to call mktime (or timegm where supported).
1922
417
      mkdate00(&tm);
1923
417
  }
1924
1925
887
  makelt(&tm, ans, i, valid,
1926
887
         valid ? secs - fsecs : (R_FINITE(secs) ? NA_REAL : secs)); // fills ans[0..8]
1927
1928
887
  if (have_10) {
1929
887
      const char *p = "";
1930
887
      if(valid && tm.tm_isdst >= 0) {
1931
416
#ifdef HAVE_TM_ZONE
1932
416
    p = tm.tm_zone;
1933
416
    if(!p)
1934
0
#endif
1935
0
        p = R_tzname[tm.tm_isdst];
1936
416
      }
1937
887
      SET_STRING_ELT(VECTOR_ELT(ans, 9), i, mkChar(p));
1938
887
  }
1939
887
  if(have_11) {
1940
887
#ifdef HAVE_TM_GMTOFF
1941
887
    INTEGER(VECTOR_ELT(ans, 10))[i] =
1942
887
        valid ? (int)tm.tm_gmtoff : NA_INTEGER;
1943
#else
1944
    INTEGER(VECTOR_ELT(ans, 10))[i] = NA_INTEGER;
1945
#endif
1946
887
  }
1947
887
    } // end for(i ..)
1948
1949
887
    setAttrib(ans, R_NamesSymbol, ansnames); // sec, min, ...
1950
887
    if(do_class) {
1951
887
  SEXP klass = PROTECT(allocVector(STRSXP, 2));
1952
887
  SET_STRING_ELT(klass, 0, mkChar("POSIXlt"));
1953
887
  SET_STRING_ELT(klass, 1, mkChar("POSIXt"));
1954
887
  classgets(ans, klass);
1955
887
  UNPROTECT(1);
1956
887
    }
1957
1958
887
    SEXP tz = getAttrib(x, install("tzone"));
1959
887
    if(!isNull(tz)) {
1960
887
  if(!isString(tz)) error(_("invalid '%s'"), "attr(x, \"tzone\")");
1961
887
  setAttrib(ans, install("tzone"), tz);
1962
887
    }
1963
887
    if(set_nm) { // names(.), attached to x[[5]] = x$year:
1964
0
  SEXP nmN = PROTECT(allocVector(STRSXP, n));
1965
0
  R_xlen_t ni = XLENGTH(nm); // typically = nlen[5]
1966
  // names(.) will have to become length n;  $year is already
1967
  // fill names, recycling:
1968
0
  for(R_xlen_t i = 0; i < n; i++)
1969
0
      SET_STRING_ELT(nmN, i, STRING_ELT(nm, i % ni));
1970
0
  setAttrib(VECTOR_ELT(ans, 5), R_NamesSymbol, nmN);
1971
0
  UNPROTECT(2); // nm, nmN
1972
0
    }
1973
887
    setAttrib(ans, lt_balancedSymbol, _balanced_);
1974
887
    UNPROTECT(3);
1975
887
    return ans;
1976
887
}
1977
1978
// .Internal(balancePOSIXlt(x, fill.only, classed)) called from R's balancePOSIXlt()
1979
// or primitive  unCfillPOSIXlt(x)
1980
attribute_hidden SEXP do_balancePOSIXlt(SEXP call, SEXP op, SEXP args, SEXP env)
1981
925
{
1982
    /*
1983
       This may be called on objects generated on other versions of R
1984
       with/without tm_zone/rm_offset, or even different versions of
1985
       R.  Let alone hand-edited objects, as in datetime3.R, or those
1986
       created in packages.
1987
    */
1988
925
    checkArity(op, args);
1989
925
    SEXP x = CAR(args);
1990
1991
925
    int fill_only, do_class;
1992
925
    if(PRIMVAL(op) == 1) { // unCfillPOSIXlt(x)
1993
38
  fill_only = true;
1994
38
  do_class = false;
1995
887
    } else { // op == 0 :  .Internal(balancePOSIXlt(x, fill.only, classed))
1996
887
  fill_only = asLogical(CADR(args));
1997
887
  if(fill_only == NA_LOGICAL)
1998
0
      error(_("invalid '%s' argument"), "fill.only");
1999
887
  do_class = asLogical(CADDR(args));
2000
887
  if(do_class == NA_LOGICAL)
2001
0
      error(_("invalid '%s' argument"), "classed");
2002
887
    }
2003
925
    return balancePOSIXlt(x, (bool) fill_only, (bool) do_class);
2004
925
}
2005