# Category:Jq/Date.jq

module { "name": "Date.jq", "description": "Dates and times since the beginning of Year 1 in the Gregorian Calendar", "version": "0.0.1", "homepage": "https://rosettacode.org/w/index.php?title=Category:Jq/Date.jq", "license": "MIT", "author": "pkoppstein at gmail dot com", }; # This module provides a Date constructor and some associated date and time functions # that assume a Gregorian calendar with dates on or after January 1 in the year 1. # # The calendrical computations are based on isLeapYear/0; no corrections for leap seconds are made. # Examples: # To compute the number of days between two Dates, use daysBeforeDate/0. # To compute the Date corresponding to a given number of days before or after another Date, use addDays/1. ## Motivation: # Currently jq's support for some functions related to dates is limited, e.g. # jq -n '[1792,8,22,0,0,0,6,265] | mktime' # results in an error message complaining about "invalid gmtime representation". # Also, jaq does not currently provide any specific time or date built-ins. ## Date and the Epoch # The constructor Date($year; $month; $day) simply yields the JSON # object {"year": $year, "month": $month, "day": $day}, but # the date functions defined here assume the Gregorian # calendar and that $year, $month, and $day are all positive integers. # For present purposes the epoch begins at the beginning of New Year's # Day of the year 1, that is, on the date represented by Date(1;1;1). def Date($year; $month; $day): {$year, $month, $day}; def monthNames: ["", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; # Input: an integer year def isLeapYear: .%4 == 0 and (.%100 != 0 or .%400 == 0); def daysInYear: if isLeapYear then 366 else 365 end; def daysBeforeMonthArray: if isLeapYear then [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366] else [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365] end; # Input: a non-negative integer representing a year # Output: the number of days from the eve of Date(1;1;1) to the start of the given year, # with the understanding that: # 1 | daysBeforeYear #=> 0 # 2 | daysBeforeYear #=> 365 def daysBeforeYear: if . <= 0 or . != trunc then "daysBeforeYear expects the specified year to be non-negative (vs \(.))" | error else (.-1) as $y | $y*365 + (($y/4)|floor) - (($y/100)|floor) + (($y/400)|floor) end; # Input: a possibly augmented Date def daysBeforeDate: (.year|daysBeforeYear) + (.year|daysBeforeMonthArray)[.month-1] + .day - 1; # Input: a Date possibly augmented with any combination of {hour, minute, second} # Output: The number of seconds from the beginning of the Epoch (Date(1;1;1)) to # the beginning of the given date or date and time, assuming the latter is later than the former. # Note that .year, .month, and .day must be non-negative, but # .hour, .minute, and .second need only be numeric. def seconds: if .year <= 0 or .month <= 0 or .day <= 0 then "improper Date (\(.))"|error end | (.year | daysBeforeYear) as $d1 # days before start of year | (.year|daysBeforeMonthArray[.month-1]) as $d2 | (($d1 + $d2 + .day - 1) * 86400) + (if .hour then (.hour-1) * 3600 else 0 end) + (if .minute then (.minute-1) * 60 else 0 end) + (if .second then (.second-1) else 0 end) ; # Input: a positive integer (a number of days) # Output: Date corresponding to the given number of days after the day before Day(1;1;1) # Example: 1 | today #=> Day(1;1;1) def toDate: if . < 0 or . != trunc then "toDate requires that . be a non-negative integer"|error end | . as $in | { year: (1 + ((./366) | floor)) } | .days = (.year | daysBeforeYear) | .excess = ($in - .days) | until(.excess <= (.year|daysInYear); .year += 1 | .days = (.year | daysBeforeYear) | .excess = ($in - .days) ) | if .excess <= 31 then Date(.y; 1; .excess) else (.year|daysBeforeMonthArray) as $calendar | .month = 0 | until( .excess <= $calendar[.month]; .month += 1 ) | .day = .excess - $calendar[.month - 1] | {year, month, day} end; # Input: Date # Output: the Date that is $days later, ignoring leap seconds. # $days should be an integer, but if negative, it must satisfy the requirement: # daysBeforeDate + $days >= 0 def addDays($days): daysBeforeDate + 1 + $days | toDate;

*This category currently contains no pages or media.*