script to calculate next date

Hello all,

I need a help in script to calculate next date like below:

current or today’s date: Oct/25/2017
grace days: 28
Next date:Nov/22/2017 (which i need to calculate programmatically)

Thanks

ROS scripting has ridiculous support for dates.
You can’t add/substract them.
You need split you date on days, months, years and then do date arithmetic taking into consideration days in the months and leap years.
From one hand it is no difficult task for programmers, but admins usually not programmers %)

In few days I will make and upload needed for you script. Be patient %)

################################################################### func_shiftDate - add days to date
#  Input: date, days
#    date - "jan/1/2017"
#    days - number
# correct only for years >1918
################################################################### uncomment for testing
#:local date "jan/01/2100"
#:local days 2560
########################################
#:put "$date + $days"
:local mdays  {31;28;31;30;31;30;31;31;30;31;30;31}
:local months {"jan"=1;"feb"=2;"mar"=3;"apr"=4;"may"=5;"jun"=6;"jul"=7;"aug"=8;"sep"=9;"oct"=10;"nov"=11;"dec"=12}
:local monthr  {"jan";"feb";"mar";"apr";"may";"jun";"jul";"aug";"sep";"oct";"nov";"dec"}

:local dd  [:tonum [:pick $date 4 6]]
:local yy [:tonum [:pick $date 7 11]]
:local month [:pick $date 0 3]

:local mm (:$months->$month)
:set dd ($dd+$days)

:local dm [:pick $mdays ($mm-1)]
:if ($mm=2 && (($yy&3=0 && ($yy/100*100 != $yy)) || $yy/400*400=$yy) ) do={ :set dm 29 }

:while ($dd>$dm) do={
  :set dd ($dd-$dm)
  :set mm ($mm+1)
  :if ($mm>12) do={
    :set mm 1
    :set yy ($yy+1)
  }
 :set dm [:pick $mdays ($mm-1)]
 :if ($mm=2 &&  (($yy&3=0 && ($yy/100*100 != $yy)) || $yy/400*400=$yy) ) do={ :set dm 29 }
};
:local res "$[:pick $monthr ($mm-1)]/"
:if ($dd<10) do={ :set res ($res."0") }
:set $res "$res$dd/$yy"
:return $res

Not fully tested yet.
To use script import it to your script and use as usual function:

:local shiftDate [:parse [/system script get func_shiftDate source]]
:put [$shiftDate date="jan/01/2017" days=256]

You should get “sep/14/2017”
To test any online calculator can be used, for example https://www.timeanddate.com/date/dateadd.html
PS: days in months calculated correct only for >1918. But we may simplify little bit calculations if restrict years to 2001..2399 or even to 2001…2099.

Thank you so much, i will give it a try today.

Excellent scrip!!! working as expected!!! Many Many Thanks!!!

Hello!
How i can get this result(sep/14/2017)? I tried all variables, and nothing - empty, for example :log info message=“What output variable must be here?”

Hm…
Just create new script named func_shiftDate , insert into it code from above and call this script from you script as function.
https://wiki.mikrotik.com/wiki/Manual:Scripting#Functions

Yes, I create two scripts:

  1. First script - I copy func_shiftDate code and named script func_shiftDate.
  2. Second script - named script4 :slight_smile:
    :local shiftDate [:parse [/system script get func_shiftDate source]]
    :put [$shiftDate date=“jan/01/2017” days=256]
    :log info message=“$shiftDate”;

output:
(code)

Do you have some idea what i do wrong?
Thanks for your time.

:put - outputs result to terminal, not to log
:log - outputs to log, not to terminal.
In your example you outputs new date to terminal and try output function’s parsed code to log. That you see (code) in log.
If you wish insert any calculations\functions call into the string you must use “$[calculation]” notation.
I.e. if you wish output shifted date to log run this:

:local newDate [$shiftDate date="jan/01/2017" days=256]
:log info message="$newDate"

or just

:log info message="$[$shiftDate date="jan/01/2017" days=256]"

PS: due to shiftDate returns already string you may do even simpler:

:local newDate [$shiftDate date="jan/01/2017" days=256]
:log info message=$newDate

or

:log info message=[$shiftDate date="jan/01/2017" days=256]

I.e. it is no need to insert string into the string

Yes, it.s works! My fault, thank you!

Do you planning add: subtraction?
Example: [$shiftDate date=“jan/01/2017” days=-60]

You read my mind %)
Yes, sure.
Also I’m planning to add dates comparison (<,>,==) and subtraction of dates (i.e. how much days between dates) to have full date arithmetic.

Done.
http://forum.mikrotik.com/t/dates-manipulation-module/113640/1

please update the script suitable to 7.10 OS update. Please

Convert date/time to epoch:
http://forum.mikrotik.com/t/i-did-it-script-to-compute-unix-time/68576/24

add the required numer of seconds,

Convert back epoch to datetime:
http://forum.mikrotik.com/t/convert-uptime-to-date-and-time/157724/36

On those days I have just the time to reply on this way.
When I have time (and I remember this) can I check, but also other forum members can check and fix…

:global datetime2epoch do={
    :local dtime [:tostr $1]
    /system clock
    :local cyear [get date] ; :if ($cyear ~ "....-..-..") do={:set cyear [:pick $cyear 0 4]} else={:set cyear [:pick $cyear 7 11]}
    :if (([:len $dtime] = 10) or ([:len $dtime] = 11)) do={:set dtime "$dtime 00:00:00"}
    :if ([:len $dtime] = 15) do={:set dtime "$[:pick $dtime 0 6]/$cyear $[:pick $dtime 7 15]"}
    :if ([:len $dtime] = 14) do={:set dtime "$cyear-$[:pick $dtime 0 5] $[:pick $dtime 6 14]"}
    :if ([:len $dtime] =  8) do={:set dtime "$[get date] $dtime"}
    :if ([:tostr $1] = "") do={:set dtime ("$[get date] $[get time]")}
    :local vdate [:pick $dtime 0 [:find $dtime " " -1]]
    :local vtime [:pick $dtime ([:find $dtime " " -1] + 1) [:len $dtime]]
    :local vgmt  [get gmt-offset]; :if ($vgmt > 0x7FFFFFFF) do={:set vgmt ($vgmt - 0x100000000)}
    :if ($vgmt < 0) do={:set vgmt ($vgmt * -1)}
    :local arrm  [:toarray "0,0,31,59,90,120,151,181,212,243,273,304,334"]
    :local vdoff [:toarray "0,4,5,7,8,10"]
    :local MM    [:pick $vdate ($vdoff->2) ($vdoff->3)]
    :local M     [:tonum $MM]
    :if ($vdate ~ ".../../....") do={
        :set vdoff [:toarray "7,11,1,3,4,6"]
        :set M     ([:find "xxanebarprayunulugepctovecANEBARPRAYUNULUGEPCTOVEC" [:pick $vdate ($vdoff->2) ($vdoff->3)] -1] / 2)
        :if ($M>12) do={:set M ($M - 12)}
    }
    :local yyyy  [:pick $vdate ($vdoff->0) ($vdoff->1)] ; :if ((($yyyy - 1968) % 4) = 0) do={:set ($arrm->1) -1; :set ($arrm->2) 30}
    :local totd  ((($yyyy - 1970) * 365) + (($yyyy - 1968) / 4) + ($arrm->$M) + ([:pick $vdate ($vdoff->4) ($vdoff->5)] - 1))
    :return      (((((($totd * 24) + [:pick $vtime 0 2]) * 60) + [:pick $vtime 3 5]) * 60) + [:pick $vtime 6 8] - $vgmt)
}



      :local currDate [/system clock get date]
      :local currDateSeconds [$datetime2epoch $currDate]
      :local aDayinSeconds "86400"      
      :local DSR
      :set DSR ([:tonum $currDateSeconds]-[:tonum $aDayinSeconds]) #Deducted 86400 to get Previous Date. 



#########################################################################xxxxxxxxxxxxxxxxxxxxxxxxxxx




:global unixtodatetime do={
    :local ux [:tonum $1]
    :local Fzerofill do={:return [:pick (100 + $1) 1 3]}
    :local prMntDays [:toarray "0,0,31,59,90,120,151,181,212,243,273,304,334"]
    :local vgmt      [:tonum [/system clock get gmt-offset]]; :if ($vgmt > 0x7FFFFFFF) do={:set vgmt ($vgmt - 0x100000000)}
    :if ($vgmt < 0) do={:set vgmt ($vgmt * -1)}
    :local tzepoch   ($ux + $vgmt)
    :if ($tzepoch < 0) do={:set tzepoch 0} ; # unsupported negative unix epoch
    :local yearStart (1970 + ($tzepoch / 31536000))
    :local tmpbissex (($yearStart - 1968) / 4) ; :if ((($yearStart - 1968) % 4) = 0) do={:set ($prMntDays->1) -1 ; :set ($prMntDays->2) 30}
    :local tmpsec    ($tzepoch % 31536000)
    :local tmpdays   (($tmpsec / 86400) - $tmpbissex)
    :if (($tmpsec < (86400 * $tmpbissex)) and ((($yearStart - 1968) % 4) = 0)) do={
        :set tmpbissex ($tmpbissex - 1) ; :set ($prMntDays->1) 0 ; :set ($prMntDays->2) 31 ; :set tmpdays  ($tmpdays + 1)
    }
    :if ($tmpsec < (86400 * $tmpbissex)) do={:set yearStart ($yearStart - 1) ; :set tmpdays   ($tmpdays + 365)}
    :local mnthStart 12 ; :while (($prMntDays->$mnthStart) > $tmpdays) do={:set mnthStart ($mnthStart - 1)}
    :local dayStart  [$Fzerofill (($tmpdays + 1) - ($prMntDays->$mnthStart))]
    :local timeStart (00:00:00 + [:totime ($tmpsec % 86400)])
    :return "$yearStart/$[$Fzerofill $mnthStart]/$[$Fzerofill $dayStart] $timeStart"
}

:local YESTERDAY [$unixtodatetime $DSR]

This is for previous date. Adding 86400 to current date to get next date

Thanks you @rextended

Another way to do it (based on @rextended functions)

:global unixtodatetime do={
:local ux [:tonum $1]
:local Fzerofill do={:return [:pick (100 + $1) 1 3]}
:local prMntDays [:toarray “0,0,31,59,90,120,151,181,212,243,273,304,334”]
:local vgmt [:tonum [/system clock get gmt-offset]]; :if ($vgmt > 0x7FFFFFFF) do={:set vgmt ($vgmt - 0x100000000)}
:if ($vgmt < 0) do={:set vgmt ($vgmt * -1)}
:local tzepoch ($ux + $vgmt)
:if ($tzepoch < 0) do={:set tzepoch 0} ; # unsupported negative unix epoch
:local yearStart (1970 + ($tzepoch / 31536000))
:local tmpbissex (($yearStart - 1968) / 4) ; :if ((($yearStart - 1968) % 4) = 0) do={:set ($prMntDays->1) -1 ; :set ($prMntDays->2) 30}
:local tmpsec ($tzepoch % 31536000)
:local tmpdays (($tmpsec / 86400) - $tmpbissex)
:if (($tmpsec < (86400 * $tmpbissex)) and ((($yearStart - 1968) % 4) = 0)) do={
:set tmpbissex ($tmpbissex - 1) ; :set ($prMntDays->1) 0 ; :set ($prMntDays->2) 31 ; :set tmpdays ($tmpdays + 1)
}
:if ($tmpsec < (86400 * $tmpbissex)) do={:set yearStart ($yearStart - 1) ; :set tmpdays ($tmpdays + 365)}
:local mnthStart 12 ; :while (($prMntDays->$mnthStart) > $tmpdays) do={:set mnthStart ($mnthStart - 1)}
:local dayStart [$Fzerofill (($tmpdays + 1) - ($prMntDays->$mnthStart))]
:local timeStart (00:00:00 + [:totime ($tmpsec % 86400)])
:return “$yearStart/$[$Fzerofill $mnthStart]/$[$Fzerofill $dayStart] $timeStart”
}

:global timetoseconds do={
:local inTime $1
:local wPos [:find $inTime “w” -1]
:local dPos [:find $inTime “d” -1]
:local itLen [:find $inTime “.” -1] ; :if ([:typeof $itLen] = “nil”) do={:set itLen [:len $inTime]}
:local itSec [:pick $inTime ($itLen - 2) $itLen]
:local itMin [:pick $inTime ($itLen - 5) ($itLen - 3)]
:local itHou [:pick $inTime ($itLen - :sunglasses: ($itLen - 6)]
:local itDay 0
:local itWee 0
:if (([:typeof $wPos] = “nil”) and ([:typeof $dPos] = “num”)) do={:set itDay [:pick $inTime 0 $dPos] }
:if (([:typeof $wPos] = “num”) and ([:typeof $dPos] = “num”)) do={:set itDay [:pick $inTime ($wPos + 1) $dPos] }
:if ([:typeof $wPos] = “num”) do={:set itWee [:pick $inTime 0 $wPos] }
:local totitSec ($itSec + (60 * $itMin) + (3600 * $itHou) + (86400 * $itDay) + (604800 * $itWee))
:return $totitSec
}

=======================

{
:global unixtodatetime
:global timetoseconds
:local currDateSeconds [$timetoseconds [:timestamp]]
:local aDayinSeconds “86400”
:local DSR ($currDateSeconds - $aDayinSeconds) ; #Deducted 86400 to get Previous Date.
:local YESTERDAY [$unixtodatetime $DSR]
:put $YESTERDAY
}
Result: 2023/10/28 16:30:04