All Downloads are FREE. Search and download functionalities are using the official Maven repository.

nextflow.util.Duration.groovy Maven / Gradle / Ivy

Go to download

A DSL modelled around the UNIX pipe concept, that simplifies writing parallel and scalable pipelines in a portable manner

There is a newer version: 18.12.0-edge
Show newest version
/*
 * Copyright (c) 2013-2016, Centre for Genomic Regulation (CRG).
 * Copyright (c) 2013-2016, Paolo Di Tommaso and the respective authors.
 *
 *   This file is part of 'Nextflow'.
 *
 *   Nextflow is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   Nextflow 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 General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with Nextflow.  If not, see .
 */

package nextflow.util
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.TimeUnit

import groovy.transform.CompileStatic
import groovy.transform.EqualsAndHashCode
import groovy.util.logging.Slf4j
import org.apache.commons.lang.time.DurationFormatUtils
/**
 * A simple time duration representation
 *
 * @author Paolo Di Tommaso 
 */
@Slf4j
@CompileStatic
@EqualsAndHashCode(includes = 'durationInMillis')
class Duration implements Comparable, Serializable {

    static private final FORMAT = ~/^(\d+)\s*([a-zA-Z]+)/

    static private final LEGACY = ~/(\d{1,2}):(\d{1,2}):(\d{1,2})/

    static private final List MILLIS = ['ms','milli','millis']

    static private final List SECONDS = ['s','sec','second','seconds']

    static private final List MINUTES = ['m','min','minute','minutes']

    static private final List HOURS = ['h','hour','hours']

    static private final List DAYS = ['d','day','days']

    static public final List UNITS

    static {
        UNITS = []
        UNITS.addAll(MILLIS)
        UNITS.addAll(SECONDS)
        UNITS.addAll(MINUTES)
        UNITS.addAll(HOURS)
        UNITS.addAll(DAYS)
    }

    /**
     * Duration in millis
     */
    final long durationInMillis

    /**
     * Create e a duration object having the specified number of millis
     *
     * @param duration The duration as milliseconds
     */
    Duration(long duration) {
        assert duration>=0, "Duration unit cannot be a negative number"
        this.durationInMillis = duration
    }


    /**
     * Default constructor is required by Kryo serializer
     * Do not removed or use it directly
     */
    private Duration() { durationInMillis=0 }

    /**
     * Create the object using a string 'duration' format.
     * Accepted prefix are:
     * 
  • {@code ms}, {@code milli}, {@code millis}: for milliseconds *
  • {@code s}, {@code second}, {@code seconds}: for seconds *
  • {@code m}, {@code minute}, {@code minutes}: for minutes *
  • {@code h}, {@code hour}, {@code hours}: for hours *
  • {@code d}, {@code day}, {@code days}: for days * * * @param str */ Duration(String str) { try { try { durationInMillis = parseSimple(str) } catch( IllegalArgumentException e ) { durationInMillis = parseLegacy(str) } } catch( IllegalArgumentException e ) { throw e } catch( Exception e ) { throw new IllegalArgumentException("Not a valid duration value: ${str}", e) } } /** * Parse a duration string in legacy format i.e. hh:mm:ss * * @param str The string to be parsed e.g. {@code 05:10:30} (5 hours, 10 mins, 30 seconds) * @return The duration in millisecond */ private long parseLegacy( String str ) { def matcher = (str =~ LEGACY) if( !matcher.matches() ) new IllegalArgumentException("Not a valid duration value: ${str}") def groups = (List)matcher[0] def hh = groups[1].toInteger() def mm = groups[2].toInteger() def ss = groups[3].toInteger() return TimeUnit.HOURS.toMillis(hh) + TimeUnit.MINUTES.toMillis(mm) + TimeUnit.SECONDS.toMillis(ss) } /** * Parse a duration string * * @param str A duration string containing one or more component e.g. {@code 1d 3h 10mins} * @return The duration in millisecond */ private long parseSimple( String str ) { long result=0 for( int i=0; true; i++ ) { def matcher = (str =~ FORMAT) if( matcher.find() ) { def groups = (List)matcher[0] def all = groups[0] def digit = groups[1] def unit = groups[2] result += convert( digit.toInteger(), unit ) str = str.substring(all.length()).trim() continue } if( i == 0 ) throw new IllegalArgumentException("Not a valid duration value: ${str}") break } return result } /** * Parse a single duration component * * @param digit * @param unit A valid duration unit e.g. {@code d}, {@code d}, {@code h}, {@code hour}, etc * @return The duration in millisecond */ private long convert( int digit, String unit ) { if( unit in MILLIS ) { return digit } if ( unit in SECONDS ) { return TimeUnit.SECONDS.toMillis(digit) } if ( unit in MINUTES ) { return TimeUnit.MINUTES.toMillis(digit) } if ( unit in HOURS ) { return TimeUnit.HOURS.toMillis(digit) } if ( unit in DAYS ) { return TimeUnit.DAYS.toMillis(digit) } throw new IllegalStateException() } Duration(long value, TimeUnit unit) { assert value>=0, "Duration unit cannot be a negative number" assert unit, "Time unit cannot be null" this.durationInMillis = unit.toMillis(value) } static Duration of( long value ) { new Duration(value) } static Duration of( String str ) { new Duration(str) } static Duration of( String str, Duration fallback ) { try { return new Duration(str) } catch( IllegalArgumentException e ) { log.debug "Not a valid duration value: $str -- Fallback on default value: $fallback" return fallback } } long toMillis() { durationInMillis } long toSeconds() { TimeUnit.MILLISECONDS.toSeconds(durationInMillis) } long toMinutes() { TimeUnit.MILLISECONDS.toMinutes(durationInMillis) } long toHours() { TimeUnit.MILLISECONDS.toHours(durationInMillis) } long toDays() { TimeUnit.MILLISECONDS.toDays(durationInMillis) } /** * Duration formatting utilities and constants. The following table describes the tokens used in the pattern language for formatting. *

    *

         *   character	duration element
         *   y	        years
         *   d	        days
         *   H	        hours
         *   m	        minutes
         *   s	        seconds
         * 
    * * @param fmt * @return */ String format( String fmt ) { DurationFormatUtils.formatDuration(durationInMillis, fmt) } String toString() { // just prints the milliseconds if( durationInMillis < 1_000 ) { return durationInMillis + 'ms' } // when less than 60 seconds round up to 100th of millis if( durationInMillis < 60_000 ) { return String.valueOf( Math.round(durationInMillis / 1_000 * 10 as float) / 10 ) + 's' } def secs def mins def hours def days def result = [] // round up to seconds secs = Math.round( (double)(durationInMillis / 1_000) ) mins = secs.intdiv(60) secs = secs % 60 if( secs ) result.add( secs+'s' ) hours = mins.intdiv(60) mins = mins % 60 if( mins ) result.add(0, mins+'m' ) days = hours.intdiv(24) hours = hours % 24 if( hours ) result.add(0, hours+'h' ) if( days ) result.add(0, days+'d') return result.join(' ') } def plus( Duration value ) { return new Duration( durationInMillis + value.durationInMillis ) } def minus( Duration value ) { return new Duration( durationInMillis - value.durationInMillis ) } def multiply( Number value ) { return new Duration( durationInMillis * value ) } def div( Number value ) { return new Duration( Math.round((double)(durationInMillis / value)) ) } @Override int compareTo(Duration that) { return this.durationInMillis <=> that.durationInMillis } @EqualsAndHashCode static class ThrottleObj { Object result long timestamp ThrottleObj() {} ThrottleObj( value, long timestamp ) { this.result = value this.timestamp = timestamp } } def throttle( Closure closure ) { throttle0( durationInMillis, null, closure) } def throttle( seed, Closure closure ) { def initialValue = new ThrottleObj( seed, System.currentTimeMillis() ) throttle0( durationInMillis, initialValue, closure) } static final Map throttleMap = new ConcurrentHashMap<>() private static throttle0( long delayMillis, ThrottleObj initialValue, Closure closure ) { assert closure != null def key = 17 key = 31 * key + closure.class.hashCode() key = 31 * key + closure.owner.hashCode() key = 31 * key + closure.delegate?.hashCode() ?: 0 ThrottleObj obj = throttleMap.get(key) if( obj == null ) { obj = initialValue ?: new ThrottleObj() throttleMap.put(key,obj) } if( System.currentTimeMillis() - obj.timestamp > delayMillis ) { obj.timestamp = System.currentTimeMillis() obj.result = closure.call() } obj.result } }




  • © 2015 - 2025 Weber Informatics LLC | Privacy Policy