📜 ⬆️ ⬇️

4-digit version numbering system with date and minors

In Chrome extensions, it is customary to specify the version of the script in the form of no more than 4 numbers separated by dots and not exceeding 32767 each and not starting with the number 0. This is more than enough if the usual version is included in the version number: version, sub-version, assembly. If we want to put the date in the version in the form of 3 numbers, then in the most readable record ( version.year.month.day ) of the year, month and day take 3 places out of 4. On the version there is the first number (as a higher priority than the date ), but there is nothing left for subversion and minor. Task: how to arrange the minor version to fit the format, so that the date is readable, and the version with a minor when comparing 2 lines takes the right place in a number of versions? In addition, we need a procedure for separating the date and version with a minor from a common string.

Obviously, the only place for the minor remains after the day of the month, with the only possible separator - numbers. And, in general, due to the limitation of the value of a number, more than 3 digits cannot be allotted to a minor: for example, on the 31st, adding 3 digits gives 31999, which is still less than 32767.

In general, this system "version + minor" is quite enough for numbering minor versions of the script, made in one day. In this case, the version is almost the assembly number supplied at the beginning, but with the possibility of not including minor assemblies in the numbering. This is also convenient: minor corrections can differ either by date, or, in extreme cases, by minor, mixed with the day of the month. Unfortunately, the natural version number with a minor will no longer be sorted correctly, but are we not going to add the mandatory minor “00000” at the end of each date for the sake of a hypothetical sorting? It is easier then, in the database or where it is necessary to sort, to consider versions without minors that have the minor “00000”. With the task figured out, let's go.

Example


Imagine that version 200 needed to be updated on April 1, without changing the number of the older version. We get 2 version numbers in one day:
')
200.2013.4.1
200.2013.4.101

Next update on the same day:

200.2013.4.102

And the next day, just change the date:

200.2013.4.2

To avoid unnecessary digits, and the number was not tied to a hard format, let's invent a system for recognizing any number of a minor that is different from writing the day of the month. So, in the example we need to understand whether we recorded the minor 01 on the 1st , or it is the minor 1, recorded on the 10th . We take into account the possibility of writing 0 before the date and month, so that the system works not only for Chrome, in which leading zeros are prohibited. So, let versions of the form be valid.

200.2013.04.01

Comparing the strings of versions does not suffer - the only thing that then cannot be mixed 2 spellings: versions 200.2013.04.01 and 200.2013.04.2 will be placed in the wrong order. But we consider the presence of zeros as a bonus for the recording format, because first of all the Khromovskaya system is interested in without leading zeros.

Recognition of minor version variants


Let's see how a developer can write a minor version after a date and how to better interpret it when dividing the date and version.

We have possible dates: 1,2, 3, ... 31. 01.02, ... 09 are added to them. Anything beyond the scope of this format will be considered the day of the month with the minor version number.

For example, 32 is day 3, minor version 2. (But as a result, for simplicity of parsing, we will NOT recognize numbers starting with 3, but we will define them as the 32nd number. Indeed, such a number will still not be in the data. But 42 and above will be recognized as the 4th number, minor 2).

And what will 156 mean? Is it the 15th and 6th version of the minor or the 1st number? Proceeding from the requirement of comparing versions as strings, we conclude that this record is the 15th number, because otherwise the order of versions is arranged incorrectly in this example:

1 15 156 //15- , 6- ,   1- , 56-  


It seems better to come up with another comparison rule ( Rule 1 ). Numbers starting with 1,2,3 - add zeros before comparing versions, if they are recognized as 1,2,3, not 1x, 2x, 3x. Then it will remain to come up with the rule of distinguishing dates. It can be accepted as a rule that the minor in ambiguous cases should be separated from the date by zero:

 101 // 1,  0,  1 1001 // 10,  0,  1 111 // 1,  11 


To distinguish 1 from 10, we add another rule ( Rule 2 ) that dates from 0 at the end should always have a separator in the form of 0, i.e. will always be written with 2 zeros in a row. This will preserve the consistency of the numbering and define a simple recognition rule - separation of the date from the version.

True, it turns out that on the 10th, the 20th and the 30th, the number of versions cannot be greater than 99, because 100100 is more than 32767, and 10100 is the 1st number, the 100th version, when the remaining dates allow 999 versions and more. However, this limitation for the version numbering system is not very critical.

Remember also that there is a precedence conflict for the simple string comparison procedure. If we begin to write minors of the first number, then a string like '101', '1001', '10234' will always be greater than the string '10', which means the 10th number.

For dates starting with numbers 4–9 we don’t have conflicts at all and we can even not use zero separators (but no one forbids using): 62, 405, 435 can always be recognized as the 6th number, 2nd version, 4- e number, 05th version, etc. It is possible for the numbers 32 ...- 39 ... to come up with the same, but it will be a complication of the idea and scripts.

The whole difficulty remains in ambiguities: what are the numbers 301, 3002, 2145? We have already agreed that the presence of 1 zero at the end of the date means a one-digit number for the date, and two zeros means a two-digit one. For numbers without zeros, we agree, for string comparison reasons, that ( Rule 3 ) the maximum possible date will be recognized, i.e. two-digit, if the first digit is less than 4. We will consider a valid 2-digit date any, starting with 1, 2 or 3 (otherwise, it is necessary to analyze months and leap years, and this is difficult for templates, although it is doable).

Rules for people


So, when writing a minor, you need to remember 2 non-obvious rules - for dates 1,2,3 - to write only 1 null separator. For dates 10,20,30 - write after zero one more zero (separator).

There are no difficulties with other numbers: 2134 is 21.34, and 20034 is 20.34. If desired, you can also write 21034 as 21.34, but you cannot omit the 0-separator for 1,2,3,10,20,30.

Now it remains to write regular expressions for the rules obtained with such difficulty.

Regular Expression Version Recognition, Date and Minor


With the beginning of the line - everything is simple.

\ d + - older version
\ d {4} - year
\ d {1,2} - month

How to divide the day and version?

([4-9] | [1-3] [1-9]) - all simple cases of days. After them - empty or minor with leading zeros. There are 6 difficult cases.

[4-9] | [1-3] [1-9] | [1-3] 0? $ - everything without a version

([1-3]) 0 ([1-9] \ d * | $) - the first 3 days
(10 | 20 | 30) 0 ([1-9] \ d * | $) - 10th, 20th, and 30th

Single expression (separator points are added in the form of "\.", And "\" are doubled according to the rules for writing lines):
 rVerMinor = RegExp('(\\d+)\\.' +'(\\d{4})\\.' // JS "\\" -  "\"   +'(\\d{1,2})\\.' +'(' +'([4-9]|[1-3][1-9]|[1-3]0?$)(\\d*|$)' +'|([1-3])0([1-9]\\d*|$)' +'|(10|20|30)0([1-9]\\d*|$)' +')'); a = s.match(rVerMinor); 

Let's write it executable and with a number of tests: jsfiddle.net/spmbt/dk346

There are nested brackets. In accordance with the rules for finding values ​​in brackets, after the month value, as a result, as many values ​​appear as there are brackets in the expression. And after parsing, you need to additionally try to find non-empty values ​​among them. It is more convenient to combine array positions that have the same meaning by rewriting an expression with a smaller number of brackets.
 rVerMinor = RegExp('(\\d+)\\.' +'(\\d{4})\\.' +'(\\d{1,2})\\.' +'(' +'([4-9]|[1-3][1-9]|[1-3]0?$)(\\d*|$)' +'|([1-3]|10|20|30)0([1-9]\\d*|$)' // +')'); 

Finally, we eliminate the case of leading zeros: 200.2013.4.10001
 rVerMinor = RegExp('(\\d+)\\.' +'(\\d{4})\\.' +'(\\d{1,2})\\.' +'(' +'([4-9]|[1-3][1-9]|[1-3]0?$)(\\d*|$)' +'|(10|20|30|[1-3])0+([1-9]\\d*|$)' //  +')'); 

There are 2 options left for the results that will have to be parsed by scripts In places, a [5], a [6] are cases without zero separators, on a [7], a [8] - the rest. It would be nice to combine them. But while there are uncovered cases of leading zeros in the dates. This is not necessary for Google, but not by Google as one ... We put everywhere, where necessary, “0?”, Plus there was a case when it was necessary to write | 0 [1-3] and further - without a separator.
 rVerMinor = RegExp('(\\d+)\\.' +'(\\d{4})\\.' +'(\\d{1,2})\\.' +'(' +'(0?[4-9]|[1-3][1-9]|0?[1-3]0?$|0[1-3])(\\d*|$)' // 0?  |0[1-3] +'|(10|20|30|0?[1-3])0+([1-9]\\d*|$)' //"0?" +')'); 

The fact that leading zeros fall into the found variants is not terrible, but it is important to remember that a numeric entry with a leading zero is an octal number in order to parse it correctly later.

Tests passed, jsfiddle.net/spmbt/dk346/1 . Now everything is ready for the Great Unification. It can be seen that both expressions are similar, but to merge into one, you need to rethink a few rules. The previous construction works, but we will assume that we are too lazy to write JS and would like to twist the retexp a little more.

We write the following rules:
 '('+ //  [1-3]0(?=0)'+ //10|20|30,    0 ' |0?[1-3]0$'+ //10|20|30   ' |[1-3][1-9](?!0)'+ //11-31  20,30,  . 0 ' |0?[1-9]'+ // 1  9 ')(\\d*|$)' //      

It covers all the required rules and has only 1 range of alternatives. Date and version always fall into a [4] and a [5]. JS will become simpler and the expression itself will contain fewer sample lines. It is essentially used that the separator is 0, and it was assigned to the 2nd number in all cases. Tests: jsfiddle.net/spmbt/dk346/2 .

Result


It remains to add JS.
 var s =['200.2013.04.123','200.2013.04.1002'] ,rVerMinor = RegExp('(\\d+)\\.' +'(\\d{4})\\.' +'(\\d{1,2})\\.' +'([1-3]0(?=0)|0?[1-3]0$|[1-3][1-9](?!0)|0?[1-9])(\\d*|$)'); for(var i =0, iL = s.length; i < iL; i++){ a = s[i].match(rVerMinor); console.log(s[i], a && a.slice(1)) document.body.innerHTML += '<i style=color:#999>'+ s[i] +'</i>' +' =>' +(a &&'<br>    v.' + a[1] + (a[5] ?'.'+ a[5].replace(/^0+/,''):'') +',  '+ a[2] +'-' +(a[3].length==1?'0':'')+ a[3] +'-'+(a[4].length==1?'0':'') + a[4])+'<br>'; } 

Final tests in 20 examples with the design of the version and date: jsfiddle.net/spmbt/dk346/3
Results :
Listing test
200.2013.4.1 =>
v.200, 2013-04-01
200.2013.04.12 =>
v.200, 2013-04-12
200.2013.04.123 =>
v.200.3, 2013-04-12
200.2013.4.103 =>
v.200.3, 2013-04-01
200.2013.4.20 =>
v.200, 2013-04-20
200.2013.4.1053 =>
v.200.53, 2013-04-01
200.2013.4.10253 =>
v.200.253, 2013-04-01
200.2013.4.10001 =>
v.200.1, 2013-04-10
200.2013.4.2003 =>
v.200.3, 2013-04-20
200.2013.4.10015 =>
v.200.15, 2013-04-10
200.2013.4.300199 =>
v.200.199, 2013-04-30
200.2013.4.30199 =>
v.200.199, 2013-04-03
200.2013.4.31199 =>
v.200.199, 2013-04-31
200.2013.4.3199 =>
v.200.99, 2013-04-31
200.2013.4.03 =>
v.200, 2013-04-03
200.2013.4.031 =>
v.200.1, 2013-04-03
200.2013.4.0301 =>
v.200.1, 2013-04-03
200.2013.4.00 =>null
200.2013.12.56 =>
v.200.6, 2013-12-05
200.2013.4.501 =>
v.200.1, 2013-04-05

Regexp in 1 line:
 /(\d+)\.(\d{4})\.(\d{1,2})\.([1-3]0(?=0)|0?[1-3]0$|[1-3][1-9](?!0)|0?[1-9])(\d*|$)/ 


UPD : for testing a regular expression, the version of the same examples is laid out on the service from a neighboring article: regexponline.com/s/we#sthash.GpUQK42k.92zmz1RF.dpuf

What is left to readers


You can practice cutting off unacceptable dates, months, days. Make a check of 2 forms of the version: with date and without (type HHCH.CHCH). Check expression on other test string examples, make sure it works as intended, or find an error. Use the recording version of the program in the format with the date in their projects.
* Paginator (page navigation) on XSLT

Source: https://habr.com/ru/post/175187/


All Articles