Различие в дате в JavaScript (игнорирующий время суток)

Старый вопрос 2009 года. Теперь в 2015 году возможно новое решение с генераторами, определенными в ECMAscript 2015 aka ES6. Он был одобрен в июне, но ранее он был реализован в Firefox и Chrome. Теперь функция сна может быть сделана не занятой, неблокируемой и вложенной в циклы и подфункции без зависания браузера. Нужен только чистый JavaScript, никаких библиотек или фреймворков.

В программе ниже показано, как можно создать sleep() и runSleepyTask(). Функция sleep() является только оператором yield. Это так просто, что на самом деле проще написать оператор yield вместо вызова sleep(), но тогда не было бы спящего слова :-) yield возвращает значение времени методу next() внутри wakeup() и ждет. Фактический «сон» сделан в wakeup() с использованием старого доброго setTimeout(). При обратном вызове метод next() запускает оператор yield для продолжения, и «волшебство» yield заключается в том, что все локальные переменные и весь стек вызовов вокруг него все еще не повреждены.

Функции, которые используют sleep () или yield, должны быть определены как генераторы. Это легко сделать, добавив звездочку к ключевому слову function*. Выполнить генератор немного сложнее. При вызове с ключевым словом new генератор возвращает объект с методом next(), но тело генератора не выполняется (ключевое слово new является необязательным и не имеет значения). Метод next() запускает выполнение тела генератора, пока не встретится yield. Функция обертки runSleepyTask() запускает пинг-понг: next() ожидает yield, а yield ждет next().

Еще один способ вызвать генератор - использовать ключевое слово yield*, здесь он работает как простой вызов функции, но также включает возможность возврата к next().

Это все продемонстрировано на примере drawTree(). Он рисует дерево с листьями на вращающейся трехмерной сцене. Дерево нарисовано в виде ствола с 3-мя частями вверху в разных направлениях. Затем каждая часть рисуется как другое, но меньшее дерево, вызывая drawTree() рекурсивно после короткого сна. Очень маленькое дерево нарисовано только как лист.

Каждый лист имеет свою собственную жизнь в отдельной задаче, начатой ​​с runSleepyTask(). Он рождается, растет, сидит, увядает, падает и умирает в growLeaf(). Скорость регулируется с помощью sleep(). Это демонстрирует, как легко можно сделать многозадачность.

function* sleep(milliseconds) {yield milliseconds};

function runSleepyTask(task) {
    (function wakeup() {
        var result = task.next();
        if (!result.done) setTimeout(wakeup, result.value);
    })()
}
//////////////// written by Ole Middelboe  /////////////////////////////

pen3D =setup3D();
var taskObject = new drawTree(pen3D.center, 5);
runSleepyTask(taskObject);

function* drawTree(root3D, size) {
    if (size < 2) runSleepyTask(new growLeaf(root3D))
    else {
        pen3D.drawTrunk(root3D, size);
        for (var p of [1, 3, 5]) {
            var part3D = new pen3D.Thing;
            root3D.add(part3D);
            part3D.move(size).turn(p).tilt(1-p/20);
            yield* sleep(50);
            yield* drawTree(part3D, (0.7+p/40)*size);
        }
    }
}

function* growLeaf(stem3D) {
    var leaf3D = pen3D.drawLeaf(stem3D);
    for (var s=0;s++<15;) {yield* sleep(100); leaf3D.scale.multiplyScalar(1.1)}
    yield* sleep( 1000 + 9000*Math.random() );
    for (var c=0;c++<30;) {yield* sleep(200); leaf3D.skin.color.setRGB(c/30, 1-c/40, 0)}
    for (var m=0;m++<90;) {yield* sleep( 50); leaf3D.turn(0.4).tilt(0.3).move(2)}
    leaf3D.visible = false;
}
///////////////////////////////////////////////////////////////////////

function setup3D() {
    var scene, camera, renderer, diretionalLight, pen3D;

    scene = new THREE.Scene();
    camera = new THREE.PerspectiveCamera(75, 
        window.innerWidth / window.innerHeight, 0.1, 1000);
    camera.position.set(0, 15, 20);
    renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
    renderer.setSize(window.innerWidth, window.innerHeight);
    document.body.appendChild(renderer.domElement);
    
    directionalLight = new THREE.DirectionalLight(0xffffaa, 0.7);
    directionalLight.position.set(-1, 2, 1);
    scene.add(directionalLight);
    scene.add(new THREE.AmbientLight(0x9999ff));
      
    (function render() {
        requestAnimationFrame(render);
        // renderer.setSize( window.innerWidth, window.innerHeight );
        scene.rotateY(10/60/60);
        renderer.render(scene, camera);
    })();
    
    window.addEventListener(
        'resize',
        function(){
            renderer.setSize( window.innerWidth, window.innerHeight );
            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();
       }, 
       false
    );
    
    pen3D = {
        drawTrunk: function(root, size) {
            // root.skin = skin(0.5, 0.3, 0.2);
            root.add(new THREE.Mesh(new THREE.CylinderGeometry(size/12, size/10, size, 16), 
                root.skin).translateY(size/2));
            root.add(new THREE.Mesh(new THREE.SphereGeometry(size/12, 16), 
                root.skin).translateY(size));
            return root;
        },
        
        drawLeaf: function(stem) {
            stem.skin.color.setRGB(0, 1, 0);
            stem.add(new THREE.Mesh(new THREE.CylinderGeometry(0, 0.02, 0.6), 
                stem.skin) .rotateX(0.3).translateY(0.3));
            stem.add(new THREE.Mesh(new THREE.CircleGeometry(0.2), 
                stem.skin) .rotateX(0.3).translateY(0.4));
            return stem;
        },
        
        Thing: function() {
            THREE.Object3D.call(this);
            this.skin = new THREE.MeshLambertMaterial({
                color: new THREE.Color(0.5, 0.3, 0.2),
                vertexColors: THREE.FaceColors,
                side: THREE.DoubleSide
            })
        }
    };

    pen3D.Thing.prototype = Object.create(THREE.Object3D.prototype);
    pen3D.Thing.prototype.tilt = pen3D.Thing.prototype.rotateX;
    pen3D.Thing.prototype.turn = pen3D.Thing.prototype.rotateY;
    pen3D.Thing.prototype.move = pen3D.Thing.prototype.translateY;
    
    pen3D.center = new pen3D.Thing;
    scene.add(pen3D.center);
    
    return pen3D;
}

3D-содержимое скрыто внутри setup3D () и включено только для того, чтобы сделать его менее скучным, чем console.log ( ). Кстати, ангелы измеряются в радианах.

Проверено на работу в Firefox и Chrome. Не реализовано в Internet Explore и iOS (iPad). Попробуйте запустить его самостоятельно.

После очередного прохода ответов я обнаружил, что Габриэль Ратенер сделал аналогичный ответ год назад: https://stackoverflow.com/a/24401317/5032384

54
задан Alan 23 June 2009 в 19:44
поделиться

5 ответов

Если вам нужны целые дни для примера аренды студенческой камеры ...

function daysBetween(first, second) {

    // Copy date parts of the timestamps, discarding the time parts.
    var one = new Date(first.getFullYear(), first.getMonth(), first.getDate());
    var two = new Date(second.getFullYear(), second.getMonth(), second.getDate());

    // Do the math.
    var millisecondsPerDay = 1000 * 60 * 60 * 24;
    var millisBetween = two.getTime() - one.getTime();
    var days = millisBetween / millisecondsPerDay;

    // Round down.
    return Math.floor(days);
}
47
ответ дан 7 November 2019 в 07:56
поделиться

используйте метод setHours (), предполагая, что количество дней никогда не может быть равно нулю :)

function dateDiff1(startDate, endDate) {
    endDate.setHours(0);
    startDate.setHours(0);
    //assuming days cannt be 0.

    var x = ((endDate.getTime() - startDate.getTime()) / 1000*60*60*24);
    if (x <1 && x>=0) {
        return 1;
    }
    return x;
}
1
ответ дан 7 November 2019 в 07:56
поделиться

Поскольку юлианский день фактически представляет собой количество дней (а в некоторых случаях также часть количества дней) с определенной даты, он практически совпадает с меткой времени UNIX , представлены иначе. Вы можете получить количество полных дней с 1970 года следующим образом:

Date.prototype.getWholeDays = function () {
    return Math.floor(new Date() / 1000*60*60*24);
};

function dateDiff(startDate, endDate) {
    return endDate.getWholeDays() - startDate.getWholeDays();
}
6
ответ дан 7 November 2019 в 07:56
поделиться

Я бы установил одно и то же время для двух дат. Например, установите время endDate на 12:00 и время startDate на 12:00. Тогда обратите внимание на разницу между ними.

Кстати, поскольку я тоже работаю в индустрии программного обеспечения для аренды оборудования, похоже, что вы теряете доход от аренды, не считая часы. В соответствии с вашим примером, если кто-то забрал оборудование 6 июля в 6 утра и вернул его 7 июля в 22:00. У них было два полных дня, чтобы использовать оборудование и, возможно, тоже понести дополнительную плату за счетчик ...

10
ответ дан 7 November 2019 в 07:56
поделиться

Вы можете настроить дату начала на полночь того же дня, а затем вычислить количество 24-часовых периодов между датами. Затем вы возьмете верхний или нижний предел этого числа в зависимости от того, хотите ли вы считать какую-либо часть дня целым днем ​​или нет.

function dateDiff(startDate, endDate) {
    // set startDate to the beginning of the day
    startDate = new Date(
        startDate.getFullYear(),
        startDate.getMonth(),
        startDate.getDate());

    return Math.floor((endDate - startDate) / 1000*60*60*24);
}
1
ответ дан 7 November 2019 в 07:56
поделиться
Другие вопросы по тегам:

Похожие вопросы: