JSDateMath.cpp 9.26 KB
Newer Older
1 2
/*
 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3
 * Copyright (C) 2006, 2007, 2012 Apple Inc. All rights reserved.
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
 * Copyright (C) 2009 Google Inc. All rights reserved.
 * Copyright (C) 2007-2009 Torch Mobile, Inc.
 * Copyright (C) 2010 &yet, LLC. (nate@andyet.net)
 *
 * The Original Code is Mozilla Communicator client code, released
 * March 31, 1998.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1998
 * the Initial Developer. All Rights Reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 * Alternatively, the contents of this file may be used under the terms
 * of either the Mozilla Public License Version 1.1, found at
 * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public
 * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html
 * (the "GPL"), in which case the provisions of the MPL or the GPL are
 * applicable instead of those above.  If you wish to allow use of your
 * version of this file only under the terms of one of those two
 * licenses (the MPL or the GPL) and not to allow others to use your
 * version of this file under the LGPL, indicate your decision by
 * deletingthe provisions above and replace them with the notice and
 * other provisions required by the MPL or the GPL, as the case may be.
 * If you do not delete the provisions above, a recipient may use your
 * version of this file under any of the LGPL, the MPL or the GPL.

 * Copyright 2006-2008 the V8 project authors. All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above
 *       copyright notice, this list of conditions and the following
 *       disclaimer in the documentation and/or other materials provided
 *       with the distribution.
 *     * Neither the name of Google Inc. nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "JSDateMath.h"

#include "JSObject.h"
76
#include "JSScope.h"
77
#include "Operations.h"
78 79 80 81 82 83

#include <algorithm>
#include <limits.h>
#include <limits>
#include <stdint.h>
#include <time.h>
84 85
#include <wtf/ASCIICType.h>
#include <wtf/Assertions.h>
86 87 88 89
#include <wtf/CurrentTime.h>
#include <wtf/MathExtras.h>
#include <wtf/StdLibExtras.h>
#include <wtf/StringExtras.h>
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
#include <wtf/text/StringBuilder.h>

#if HAVE(ERRNO_H)
#include <errno.h>
#endif

#if HAVE(SYS_TIME_H)
#include <sys/time.h>
#endif

#if HAVE(SYS_TIMEB_H)
#include <sys/timeb.h>
#endif

using namespace WTF;

namespace JSC {

static inline double timeToMS(double hour, double min, double sec, double ms)
{
    return (((hour * minutesPerHour + min) * secondsPerMinute + sec) * msPerSecond + ms);
}

static inline int msToSeconds(double ms)
{
    double result = fmod(floor(ms / msPerSecond), secondsPerMinute);
    if (result < 0)
        result += secondsPerMinute;
    return static_cast<int>(result);
}

// 0: Sunday, 1: Monday, etc.
static inline int msToWeekDay(double ms)
{
    int wd = (static_cast<int>(msToDays(ms)) + 4) % 7;
    if (wd < 0)
        wd += 7;
    return wd;
}

130
// Get the combined UTC + DST offset for the time passed in.
131 132 133 134
//
// NOTE: The implementation relies on the fact that no time zones have
// more than one daylight savings offset change per month.
// If this function is called with NaN it returns NaN.
135
static LocalTimeOffset localTimeOffset(VM& vm, double ms)
136
{
137
    LocalTimeOffsetCache& cache = vm.localTimeOffsetCache;
138 139 140 141 142 143 144 145 146 147 148
    double start = cache.start;
    double end = cache.end;

    if (start <= ms) {
        // If the time fits in the cached interval, return the cached offset.
        if (ms <= end) return cache.offset;

        // Compute a possible new interval end.
        double newEnd = end + cache.increment;

        if (ms <= newEnd) {
149
            LocalTimeOffset endOffset = calculateLocalTimeOffset(newEnd);
150 151 152 153 154 155 156
            if (cache.offset == endOffset) {
                // If the offset at the end of the new interval still matches
                // the offset in the cache, we grow the cached time interval
                // and return the offset.
                cache.end = newEnd;
                cache.increment = msPerMonth;
                return endOffset;
157 158 159 160 161 162 163 164 165 166
            }
            LocalTimeOffset offset = calculateLocalTimeOffset(ms);
            if (offset == endOffset) {
                // The offset at the given time is equal to the offset at the
                // new end of the interval, so that means that we've just skipped
                // the point in time where the DST offset change occurred. Updated
                // the interval to reflect this and reset the increment.
                cache.start = ms;
                cache.end = newEnd;
                cache.increment = msPerMonth;
167
            } else {
168 169 170 171 172
                // The interval contains a DST offset change and the given time is
                // before it. Adjust the increment to avoid a linear search for
                // the offset change point and change the end of the interval.
                cache.increment /= 3;
                cache.end = ms;
173
            }
174 175 176
            // Update the offset in the cache and return it.
            cache.offset = offset;
            return offset;
177 178 179 180 181 182
        }
    }

    // Compute the DST offset for the time and shrink the cache interval
    // to only contain the time. This allows fast repeated DST offset
    // computations for the same time.
183
    LocalTimeOffset offset = calculateLocalTimeOffset(ms);
184 185 186 187 188 189 190
    cache.offset = offset;
    cache.start = ms;
    cache.end = ms;
    cache.increment = msPerMonth;
    return offset;
}

191
double gregorianDateTimeToMS(VM& vm, const GregorianDateTime& t, double milliSeconds, bool inputIsUTC)
192
{
193
    double day = dateToDaysFrom1970(t.year(), t.month(), t.monthDay());
194
    double ms = timeToMS(t.hour(), t.minute(), t.second(), milliSeconds);
195 196
    double result = (day * WTF::msPerDay) + ms;

197
    if (!inputIsUTC)
198
        result -= localTimeOffset(vm, result).offset;
199 200 201 202 203

    return result;
}

// input is UTC
204
void msToGregorianDateTime(VM& vm, double ms, bool outputIsUTC, GregorianDateTime& tm)
205
{
206
    LocalTimeOffset localTime;
207
    if (!outputIsUTC) {
208
        localTime = localTimeOffset(vm, ms);
209
        ms += localTime.offset;
210 211 212
    }

    const int year = msToYear(ms);
213 214 215 216 217 218 219
    tm.setSecond(msToSeconds(ms));
    tm.setMinute(msToMinutes(ms));
    tm.setHour(msToHours(ms));
    tm.setWeekDay(msToWeekDay(ms));
    tm.setYearDay(dayInYear(ms, year));
    tm.setMonthDay(dayInMonthFromDayInYear(tm.yearDay(), isLeapYear(year)));
    tm.setMonth(monthFromDayInYear(tm.yearDay(), isLeapYear(year)));
220
    tm.setYear(year);
221 222
    tm.setIsDST(localTime.isDST);
    tm.setUtcOffset(localTime.offset / WTF::msPerSecond);
223 224
}

225
double parseDateFromNullTerminatedCharacters(VM& vm, const char* dateString)
226 227 228 229
{
    bool haveTZ;
    int offset;
    double ms = WTF::parseDateFromNullTerminatedCharacters(dateString, haveTZ, offset);
230
    if (std::isnan(ms))
231
        return QNaN;
232 233

    // fall back to local timezone
234
    if (!haveTZ)
235
        offset = localTimeOffset(vm, ms).offset / WTF::msPerMinute;
236

237 238 239
    return ms - (offset * WTF::msPerMinute);
}

240
double parseDate(VM& vm, const String& date)
241
{
242 243
    if (date == vm.cachedDateString)
        return vm.cachedDateStringValue;
244
    double value = parseES5DateFromNullTerminatedCharacters(date.utf8().data());
245
    if (std::isnan(value))
246 247 248
        value = parseDateFromNullTerminatedCharacters(vm, date.utf8().data());
    vm.cachedDateString = date;
    vm.cachedDateStringValue = value;
249 250 251
    return value;
}

252
} // namespace JSC